This commit is contained in:
matteo porta 2022-07-26 12:24:53 +02:00
parent af61cbb6f9
commit 408a1e466f
14 changed files with 300 additions and 250 deletions

View File

@ -59,66 +59,66 @@ if admin is None or not admin.is_admin:
Users.register(username="ADMIN", password="123123", roles=["admin"]) Users.register(username="ADMIN", password="123123", roles=["admin"])
# register or reset user # register or reset user
Users.register(username="USER", password="user") Users.register(username="USER", password="user")
# register test steps # # register test steps
Steps.replace( # Steps.replace(
id=0, # id=0,
name="TEST_LEAK_TEST", # name="TEST_LEAK_TEST",
type="leak", # type="leak",
spec={ # spec={
"pre_filling_time": 5, # "pre_filling_time": 5,
"pre_filling_pressure": 1000, # "pre_filling_pressure": 1000,
"filling_time": 5, # "filling_time": 5,
"settling_time": 10, # "settling_time": 10,
"settling_pressure_min_percent": 50, # "settling_pressure_min_percent": 50,
"settling_pressure_max_percent": 50, # "settling_pressure_max_percent": 50,
"test_time": 10, # "test_time": 10,
"test_pressure_min_delta": 500, # "test_pressure_min_delta": 500,
"test_pressure": 1000, # "test_pressure": 1000,
"test_pressure_max_delta": 500, # "test_pressure_max_delta": 500,
"flush_time": 2, # "flush_time": 2,
"flush_pressure": 5, # "flush_pressure": 5,
}, # },
description="TEST_DESCRIPTION", # description="TEST_DESCRIPTION",
archived=False, # archived=False,
).execute() # ).execute()
Steps.replace( # Steps.replace(
id=1, # id=1,
name="TEST_LEAK_TEST", # name="TEST_LEAK_TEST",
type="leak", # type="leak",
spec={ # spec={
"pre_filling_time": 5, # "pre_filling_time": 5,
"pre_filling_pressure": 1000, # "pre_filling_pressure": 1000,
"filling_time": 5, # "filling_time": 5,
"settling_time": 10, # "settling_time": 10,
"settling_pressure_min_percent": 50, # "settling_pressure_min_percent": 50,
"settling_pressure_max_percent": 50, # "settling_pressure_max_percent": 50,
"test_time": 10, # "test_time": 10,
"test_pressure_min_delta": 500, # "test_pressure_min_delta": 500,
"test_pressure": 1000, # "test_pressure": 1000,
"test_pressure_max_delta": 500, # "test_pressure_max_delta": 500,
"flush_time": 2, # "flush_time": 2,
"flush_pressure": 5, # "flush_pressure": 5,
}, # },
description="TEST_DESCRIPTION", # description="TEST_DESCRIPTION",
archived=False, # archived=False,
).execute() # ).execute()
Steps.replace( # Steps.replace(
id=2, # id=2,
name="TEST_VISION_TEST", # name="TEST_VISION_TEST",
type="vision", # type="vision",
spec={ # spec={
"recipe": "1.ini", # "recipe": "1.ini",
}, # },
description="TEST_DESCRIPTION VISION_RECIPE: 1", # description="TEST_DESCRIPTION VISION_RECIPE: 1",
archived=False, # archived=False,
).execute() # ).execute()
# register test recipe # register test recipe
Recipes.replace( Recipes.replace(
id=0, id=0,
name="TEST", name="TEST",
client="TEST_CLIENT", client="TEST_CLIENT",
part_number="TEST_PART_NUMBER", part_number="TEST_PART_NUMBER",
spec={"steps": [0, 1, 2, ]}, spec={"steps": []},
description="TEST_DESCRIPTION", description="TEST_DESCRIPTION",
archived=False, archived=False,
).execute() ).execute()

View File

@ -6,10 +6,10 @@ from .base_model import BaseModel
class Steps(BaseModel): class Steps(BaseModel):
id = AutoField(primary_key=True, unique=True, null=False) id = AutoField(primary_key=True, unique=True, null=False)
name = TextField(null=False) name = TextField(null=True)
type = TextField(null=False) type = TextField(null=False)
spec = JSONField(null=False) # keys inside spec must not overlap withthe model spec = JSONField(null=False) # keys inside spec must not overlap withthe model
description = TextField(null=False) description = TextField(null=True)
archived = BooleanField(null=False, default=False) archived = BooleanField(null=False, default=False)
@classmethod @classmethod

View File

@ -6,7 +6,7 @@ from datetime import datetime
from lib.db import Crud_DB from lib.db import Crud_DB
from peewee import TextField from peewee import TextField
from PyQt5.QtCore import Qt, pyqtSignal from PyQt5.QtCore import QObject, Qt, pyqtSignal
from PyQt5.QtWidgets import (QAbstractItemView, QComboBox, QDialog, from PyQt5.QtWidgets import (QAbstractItemView, QComboBox, QDialog,
QGridLayout, QHeaderView, QLineEdit, QMessageBox, QGridLayout, QHeaderView, QLineEdit, QMessageBox,
QPlainTextEdit, QPushButton) QPlainTextEdit, QPushButton)
@ -53,7 +53,7 @@ class Cell:
self.set_readonly(self.readonly) self.set_readonly(self.readonly)
self.do_autocomplete(self.autocomplete) self.do_autocomplete(self.autocomplete)
def set_readonly(self, readonly): def set_readonly(self, readonly=True):
raise NotImplementedError() raise NotImplementedError()
def do_autocomplete(self, autocomplete): def do_autocomplete(self, autocomplete):
@ -104,7 +104,7 @@ class Line_Edit_Cell_Widget(QLineEdit, Cell):
super().__init__() super().__init__()
Cell.__init__(self, readonly=readonly, autocomplete=autocomplete, field_name=field_name, field_alias=field_alias, field=field, row_number=row_number, crud=crud) Cell.__init__(self, readonly=readonly, autocomplete=autocomplete, field_name=field_name, field_alias=field_alias, field=field, row_number=row_number, crud=crud)
def set_readonly(self, readonly): def set_readonly(self, readonly=True):
self.setReadOnly(readonly) self.setReadOnly(readonly)
def connect_modified(self): def connect_modified(self):
@ -123,7 +123,7 @@ class Combo_Box_Cell_Widget(QComboBox, Cell):
super().__init__() super().__init__()
Cell.__init__(self, readonly=readonly, autocomplete=autocomplete, field_name=field_name, field_alias=field_alias, field=field, row_number=row_number, crud=crud) Cell.__init__(self, readonly=readonly, autocomplete=autocomplete, field_name=field_name, field_alias=field_alias, field=field, row_number=row_number, crud=crud)
def set_readonly(self, readonly): def set_readonly(self, readonly=True):
self.setEditable(not readonly) self.setEditable(not readonly)
def do_autocomplete(self, autocomplete): def do_autocomplete(self, autocomplete):
@ -151,11 +151,14 @@ class External_Dialog_Cell_Widget(QPushButton, Cell):
super().__init__(u"\u238B modifica") super().__init__(u"\u238B modifica")
Cell.__init__(self, readonly=readonly, autocomplete=autocomplete, field_name=field_name, field_alias=field_alias, field=field, row_number=row_number, crud=crud) Cell.__init__(self, readonly=readonly, autocomplete=autocomplete, field_name=field_name, field_alias=field_alias, field=field, row_number=row_number, crud=crud)
self.dialog.setWindowTitle(self.field_alias) self.dialog.setWindowTitle(self.field_alias)
self.clicked.connect(self.dialog.show) self.clicked.connect(self.show_dialog)
def set_readonly(self, readonly): def set_readonly(self, readonly=True):
self.editor.setReadOnly(readonly) self.editor.setReadOnly(readonly)
def show_dialog(self):
self.dialog.show()
def connect_modified(self): def connect_modified(self):
self.editor.textChanged.connect(self.check_modified) self.editor.textChanged.connect(self.check_modified)
@ -175,24 +178,31 @@ class Json_External_Dialog_Cell_Widget(External_Dialog_Cell_Widget):
class Json_External_Dialog_Editor_Cell_Widget(QPushButton, Cell): class Json_External_Dialog_Editor_Cell_Widget(QPushButton, Cell):
def __init__(self, editor_widget_instance, readonly=True, autocomplete=None, field_name=None, field_alias=None, field=None, row_number=None, crud=None): def __init__(self, editor_widget_class, *args, readonly=True, autocomplete=None, field_name=None, field_alias=None, field=None, row_number=None, crud=None, **kwargs):
if not isinstance(editor_widget_instance, Editor): if not issubclass(editor_widget_class, Editor):
raise AssertionError(f"editor_widget_instance {editor_widget_instance!r} must be a subclass of {Editor!r}") raise AssertionError(f"editor_widget_class {editor_widget_class!r} must be a subclass of {Editor!r}")
self.editor = editor_widget_instance self.editor = editor_widget_class(*args, cell_widget=self, **kwargs)
super().__init__(u"\u238B modifica")
Cell.__init__(self, readonly=readonly, autocomplete=autocomplete, field_name=field_name, field_alias=field_alias, field=field, row_number=row_number, crud=crud)
self.dialog = Dialog() self.dialog = Dialog()
self.dialog.setAttribute(Qt.WA_DeleteOnClose, on=False) self.dialog.setAttribute(Qt.WA_DeleteOnClose, on=False)
self.dialog.setCentralWidget(self.editor) self.dialog.setCentralWidget(self.editor)
super().__init__(u"\u238B modifica")
Cell.__init__(self, readonly=readonly, autocomplete=autocomplete, field_name=field_name, field_alias=field_alias, field=field, row_number=row_number, crud=crud)
self.dialog.setWindowTitle(self.field_alias) self.dialog.setWindowTitle(self.field_alias)
self.clicked.connect(self.dialog.show) self.clicked.connect(self.show_dialog)
if hasattr(self.editor, "init"):
self.editor.init()
def set_readonly(self, readonly): def set_readonly(self, readonly=True):
self.editor.set_readonly(readonly) self.editor.set_readonly(readonly=readonly)
def do_autocomplete(self, autocomplete): def do_autocomplete(self, autocomplete):
self.editor.do_autocomplete(autocomplete) self.editor.do_autocomplete(autocomplete)
def show_dialog(self):
if hasattr(self.editor, "showing_dialog"):
self.editor.showing_dialog()
self.dialog.show()
def connect_modified(self): def connect_modified(self):
self.editor.connect_modified(self.check_modified) self.editor.connect_modified(self.check_modified)
@ -412,6 +422,8 @@ class Crud(Widget):
for fn, cn in self.select_index.items(): for fn, cn in self.select_index.items():
readonly = self.readonly is None or self.readonly is True or (self.readonly is not False and fn in self.readonly) readonly = self.readonly is None or self.readonly is True or (self.readonly is not False and fn in self.readonly)
w = self.widget_classes.get(fn, self.default_widget_class)(readonly=readonly, autocomplete=self.autocomplete.get(fn, None), field_name=fn, field_alias=self.fields_aliases[fn], field=self.db.table_model._meta.fields.get(fn, None), row_number=rn, crud=self) w = self.widget_classes.get(fn, self.default_widget_class)(readonly=readonly, autocomplete=self.autocomplete.get(fn, None), field_name=fn, field_alias=self.fields_aliases[fn], field=self.db.table_model._meta.fields.get(fn, None), row_number=rn, crud=self)
w.set_readonly(readonly=w.readonly)
w.do_autocomplete(w.autocomplete)
w.modified.connect(self.set_modified) w.modified.connect(self.set_modified)
if fn in r: if fn in r:
w._render(data=r[fn], row_number=rn, crud=self) w._render(data=r[fn], row_number=rn, crud=self)
@ -434,6 +446,8 @@ class Crud(Widget):
for fn, cn in self.select_index.items(): for fn, cn in self.select_index.items():
readonly = self.readonly is None or self.readonly is True or (self.readonly is not False and fn in self.readonly) readonly = self.readonly is None or self.readonly is True or (self.readonly is not False and fn in self.readonly)
w = self.widget_classes.get(fn, self.default_widget_class)(readonly=readonly, autocomplete=self.autocomplete.get(fn, None), field_name=fn, field_alias=self.fields_aliases[fn], field=self.db.table_model._meta.fields.get(fn, None), row_number=rn, crud=self) w = self.widget_classes.get(fn, self.default_widget_class)(readonly=readonly, autocomplete=self.autocomplete.get(fn, None), field_name=fn, field_alias=self.fields_aliases[fn], field=self.db.table_model._meta.fields.get(fn, None), row_number=rn, crud=self)
w.set_readonly(readonly=w.readonly)
w.do_autocomplete(w.autocomplete)
w.modified.connect(self.set_modified) w.modified.connect(self.set_modified)
self.db_tw.setCellWidget(rn, cn, w) self.db_tw.setCellWidget(rn, cn, w)
self.db_tw.scrollToBottom() self.db_tw.scrollToBottom()

View File

@ -6,16 +6,19 @@ from ui.widget import Widget
class Editor(Widget): class Editor(Widget):
def __init__(self): def __init__(self, cell_widget=None):
super().__init__() super().__init__()
self.cell_widget = cell_widget
self.spec = {} self.spec = {}
def set_readonly(self, readonly): def set_readonly(self, readonly=True):
for w in self.spec.values(): for w in self.spec.values():
if isinstance(w, QComboBox): if isinstance(w, QComboBox):
w.setDisabled(readonly) w.setDisabled(readonly)
elif isinstance(w, QListWidget): elif isinstance(w, QListWidget):
w.setDisabled(readonly) w.setDisabled(readonly)
elif isinstance(w, QCheckBox):
w.setDisabled(readonly)
elif isinstance(w, QObject): elif isinstance(w, QObject):
w.setReadOnly(readonly) w.setReadOnly(readonly)

BIN
src/ui/imgs/print.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -2,8 +2,8 @@ from ui.editor import Editor
class Leak_Step_Editor(Editor): class Leak_Step_Editor(Editor):
def __init__(self): def __init__(self, cell_widget=None):
super().__init__() super().__init__(cell_widget=cell_widget)
self.spec.update({ self.spec.update({
# pre-filling # pre-filling
"pre_filling_time": self.pre_filling_time_sb, "pre_filling_time": self.pre_filling_time_sb,

View File

@ -49,7 +49,7 @@ class Recipe_Selection(Widget):
autocomplete={ autocomplete={
"archived": False, "archived": False,
}, },
widget_classes={"spec": lambda *args, **kwargs: Json_External_Dialog_Editor_Cell_Widget(Recipe_Spec_And_Step_Editor(), *args, **kwargs), }, widget_classes={"spec": lambda *args, **kwargs: Json_External_Dialog_Editor_Cell_Widget(Recipe_Spec_And_Step_Editor, *args, **kwargs), },
) )
replace_widget(self, "crud_w", self.crud) replace_widget(self, "crud_w", self.crud)
self.crud_modified = None self.crud_modified = None

View File

@ -1,136 +1,139 @@
from lib.db import Steps
from playhouse.shortcuts import model_to_dict
from ui.editor import Editor from ui.editor import Editor
from ui.helpers import replace_widget from ui.helpers import replace_widget
from ui.leak_step_editor import Leak_Step_Editor
from ui.vision_step_editor import Vision_Step_Editor
class Recipe_Spec_And_Step_Editor(Editor): class Recipe_Spec_And_Step_Editor(Editor):
def __init__(self): def __init__(self, cell_widget=None):
super().__init__() super().__init__(cell_widget=cell_widget)
# self.spec.update({ self.map = {
# "barcode_editor": self.barcode_editor_w, "barcodes": {
# "leak_editor_1": self.leak_editor_1_w, "type": "barcodes",
# "leak_editor_2": self.leak_editor_2_w, "enable": self.barcode_enabled_cb,
# "vision_editor": self.vision_editor_w, "widget": "barcode_editor_w",
# "print_editor": self.print_editor_w, # "editor": _Editor,
# "barcode_enabled": self.barcode_enabled_cb, },
# "leak_enabled_1": self.leak_enabled_1_cb, "leak_1": {
# "leak_eabled_2": self.leak_eabled_2_cb, "type": "leak",
# "vision_enabled": self.vision_enabled_cb, "enable": self.leak_enabled_1_cb,
# "vision_enabled": self.vision_enabled_cb, "widget": "leak_editor_1_w",
# "print_enabled_cb": self.print_enabled_cb, "editor": Leak_Step_Editor(),
# }) },
# replace_widget(self, "barcode_editor_w", _Editor()) "leak_2": {
# replace_widget(self, "leak_editor_1_w", _Editor()) "type": "leak",
# replace_widget(self, "leak_editor_2_w", _Editor()) "enable": self.leak_eabled_2_cb,
# replace_widget(self, "vision_editor_w", _Editor()) "widget": "leak_editor_2_w",
# replace_widget(self, "print_editor_w", _Editor()) "editor": Leak_Step_Editor(),
# },
# def do_autocomplete(self, autocomplete): "vision": {
# if autocomplete is None: "type": "vision",
# return "enable": self.vision_enabled_cb,
# for k, v in autocomplete.items(): "widget": "vision_editor_w",
# w = self.spec[k] "editor": Vision_Step_Editor(),
# if isinstance(w, QCheckBox): },
# w.setChecked(bool(v)) "print": {
# elif isinstance(w, QComboBox): "type": "print",
# # while w.count() > 0: "enable": self.print_enabled_cb,
# # w.removeItem(0) "widget": "print_editor_w",
# # w.clear() # "editor": _Editor,
# w.addItems(list(map(str, v))) },
# elif isinstance(w, QDoubleSpinBox): }
# w.setValue(float(v)) if len(self.map) > 1024:
# elif isinstance(w, QLineEdit): raise NotImplementedError("self.map must contain at most 1024 steps")
# w.setText(str(v)) self.spec.update({editor_name: map["enable"] for editor_name, map in self.map.items()})
# elif isinstance(w, QListWidget): for editor_name, map in self.map.items():
# # while w.count() > 0: editor = map.get("editor", None)
# # w.takeItem(0) if editor is not None:
# # w.clear() replace_widget(self, map["widget"], editor)
# w.addItems(list(map(str, v))) self.save_steps_b.clicked.connect(self.save_steps)
# elif isinstance(w, QRadioButton): self.reset_steps_b.clicked.connect(self.reset_steps)
# w.setChecked(bool(v)) self.recipe_id = None
# elif isinstance(w, QSpinBox): self.step_editors_autocomplete = {}
# w.setValue(int(v))
# elif isinstance(w, QPlainTextEdit): def set_readonly(self, readonly=True):
# w.setPlainText(str(v)) super().set_readonly(readonly=readonly)
# elif isinstance(w, QObject): for editor_name, map in self.map.items():
# raise NotImplementedError(f"widget of type {type(w)!r} not implemented") editor = map.get("editor", None)
# else: if editor is not None:
# # ignore default values editor.set_readonly(readonly=readonly)
# pass self.save_steps_b.setDisabled(readonly)
#
def do_autocomplete(self, autocomplete):
if autocomplete is None:
return
self.step_editors_autocomplete = autocomplete.pop("step_editors", None)
super().do_autocomplete(autocomplete)
if self.step_editors_autocomplete is not None:
for editor_name, map in self.map.items():
editor = map.get("editor", None)
if editor is not None:
editor.do_autocomplete(self.step_editors_autocomplete.get(editor_name, None))
def init(self):
self.recipe_id = self.cell_widget.crud.data_index[self.cell_widget.row_number - 1] # - 1 because row_number starts from 1 (filters line)
self.get_steps()
def showing_dialog(self):
self.reset_steps()
# def connect_modified(self, check_modified): # def connect_modified(self, check_modified):
# for k, w in self.spec.items(): # super().connect_modified(check_modified)
# if isinstance(w, QCheckBox): # # for map in self.map.values():
# w.toggled.connect(check_modified) # # editor = map.get("editor", None)
# elif isinstance(w, QComboBox): # # if editor is not None:
# w.currentTextChanged.connect(check_modified) # # editor.connect_modified(self.check_modified)
# elif isinstance(w, QDoubleSpinBox):
# w.valueChanged.connect(check_modified) def render(self, data, field_name=None, row_number=None, crud=None):
# elif isinstance(w, QLineEdit): super().render(data, field_name=field_name, row_number=row_number, crud=crud)
# w.textChanged.connect(check_modified) steps = set(data.get("steps", set()))
# elif isinstance(w, QListWidget): for map in self.map.values():
# w.currentItemChanged.connect(check_modified) map["enable"].setChecked(map["step"]["id"] in steps)
# elif isinstance(w, QRadioButton):
# w.toggled.connect(check_modified) def parse(self, row_number=None, crud=None):
# elif isinstance(w, QSpinBox): ret = super().parse(row_number=row_number, crud=crud)
# w.valueChanged.connect(check_modified) ret["steps"] = []
# elif isinstance(w, QPlainTextEdit): for map in self.map.values():
# w.textChanged.connect(check_modified) if map["enable"].isChecked():
# elif isinstance(w, QObject): ret["steps"].append(map["step"]["id"])
# raise NotImplementedError(f"widget of type {type(w)!r} not implemented") return ret
# else:
# # ignore default values @staticmethod
# pass def get_step_id(recipe_id, step_n):
# return recipe_id * 1024 + step_n
# def render(self, data, field_name=None, row_number=None, crud=None):
# for k, v in data.items(): def get_steps(self):
# if k not in self.spec: for n, [editor_name, map] in enumerate(self.map.items()):
# continue id = self.get_step_id(self.recipe_id, n)
# w = self.spec[k] try:
# if isinstance(w, QCheckBox): step = model_to_dict(Steps.get_by_id(id))
# w.setChecked(bool(v)) except Steps.DoesNotExist:
# elif isinstance(w, QComboBox): step = {
# w.setCurrentText(str(v)) "id": id,
# elif isinstance(w, QDoubleSpinBox): "type": map["type"],
# w.setValue(float(v)) "spec": {},
# elif isinstance(w, QLineEdit): }
# w.setText(str(v)) map["step"] = step
# elif isinstance(w, QListWidget):
# w.clear() def render_steps(self):
# w.addItems(list(map(str, v))) for n, [editor_name, map] in enumerate(self.map.items()):
# elif isinstance(w, QRadioButton): editor = map.get("editor", None)
# w.setChecked(bool(v)) step = map["step"]
# elif isinstance(w, QSpinBox): if editor is not None:
# w.setValue(int(v)) editor.render(step["spec"])
# elif isinstance(w, QPlainTextEdit):
# w.setPlainText(str(v)) def reset_steps(self):
# elif isinstance(w, QObject): self.get_steps()
# raise NotImplementedError(f"widget of type {type(w)!r} not implemented") self.render_steps()
# else:
# # ignore default values def save_steps(self):
# pass steps = []
# for map in self.map.values():
# def parse(self, row_number=None, crud=None): editor = map.get("editor", None)
# ret = {} if editor is not None:
# for k, w in self.spec.items(): map["step"]["spec"] = editor.parse()
# if isinstance(w, QCheckBox): steps.append(map["step"])
# ret[k] = w.isChecked() Steps.replace_many(steps).execute()
# elif isinstance(w, QComboBox): self.reset_steps()
# ret[k] = w.currentText()
# elif isinstance(w, QDoubleSpinBox):
# ret[k] = w.value()
# elif isinstance(w, QLineEdit):
# ret[k] = w.text()
# elif isinstance(w, QListWidget):
# ret[k] = [w.item(i).data(0) for i in range(w.count())]
# elif isinstance(w, QRadioButton):
# ret[k] = w.isChecked()
# elif isinstance(w, QSpinBox):
# ret[k] = w.value()
# elif isinstance(w, QPlainTextEdit):
# ret[k] = w.toPlainText()
# elif isinstance(w, QObject):
# raise NotImplementedError(f"widget of type {type(w)!r} not implemented")
# else:
# # pass through default values
# ret[k] = w
# return ret

View File

@ -6,15 +6,31 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>247</width> <width>330</width>
<height>145</height> <height>163</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Recipe Spec And Step Editor</string> <string>Recipe Spec And Step Editor</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0"> <item row="1" column="0">
<widget class="QPushButton" name="save_steps_b">
<property name="text">
<string>Salva configurasione
Fasi di Test</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="reset_steps_b">
<property name="text">
<string>Resetta configurazione
Fasi di Test</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>0</number>
@ -236,13 +252,6 @@
</widget> </widget>
</widget> </widget>
</item> </item>
<item row="1" column="0">
<widget class="QPushButton" name="save_b">
<property name="text">
<string>Salva</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

View File

@ -3,15 +3,15 @@ from ui.editor import Editor
class Recipe_Spec_Editor(Editor): class Recipe_Spec_Editor(Editor):
def __init__(self): def __init__(self, cell_widget=None):
super().__init__() super().__init__(cell_widget=cell_widget)
self.spec.update({ self.spec.update({
"steps": self.steps_lw, "steps": self.steps_lw,
}) })
self.steps_lw.setDragDropMode(QAbstractItemView.InternalMove) self.steps_lw.setDragDropMode(QAbstractItemView.InternalMove)
def set_readonly(self, readonly): def set_readonly(self, readonly=True):
super().set_readonly(readonly) super().set_readonly(readonly=readonly)
self.steps_lw.setDisabled(readonly) self.steps_lw.setDisabled(readonly)
self.remove_step_b.setDisabled(readonly) self.remove_step_b.setDisabled(readonly)
self.steps_available_lw.setDisabled(readonly) self.steps_available_lw.setDisabled(readonly)

View File

@ -24,6 +24,6 @@ class Recipes_Management(Widget):
autocomplete={ autocomplete={
"archived": False, "archived": False,
}, },
widget_classes={"spec": lambda *args, **kwargs: Json_External_Dialog_Editor_Cell_Widget(Recipe_Spec_Editor(), *args, **kwargs), }, widget_classes={"spec": lambda *args, **kwargs: Json_External_Dialog_Editor_Cell_Widget(Recipe_Spec_Editor, *args, **kwargs), },
) )
self.layout().addWidget(self.crud, 0, 0, -1, -1) self.layout().addWidget(self.crud, 0, 0, -1, -1)

View File

@ -1,11 +1,8 @@
from glob import iglob from glob import iglob
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QCheckBox, QComboBox, QDoubleSpinBox, QLineEdit, from PyQt5.QtWidgets import QPushButton
QPlainTextEdit, QPushButton, QRadioButton, from ui.crud import Cell, Combo_Box_Cell_Widget, Crud
QSpinBox)
from ui.crud import (Cell, Combo_Box_Cell_Widget, Crud,
Json_External_Dialog_Editor_Cell_Widget)
from ui.dialog import Dialog from ui.dialog import Dialog
from ui.leak_step_editor import Leak_Step_Editor from ui.leak_step_editor import Leak_Step_Editor
from ui.vision_step_editor import Vision_Step_Editor from ui.vision_step_editor import Vision_Step_Editor
@ -15,8 +12,8 @@ from ui.widget import Widget
class Step_Spec_JEDECW(QPushButton, Cell): class Step_Spec_JEDECW(QPushButton, Cell):
def __init__(self, readonly=True, autocomplete=None, field_name=None, field_alias=None, field=None, row_number=None, crud=None): def __init__(self, readonly=True, autocomplete=None, field_name=None, field_alias=None, field=None, row_number=None, crud=None):
self.editors = { self.editors = {
"vision": Vision_Step_Editor(), "vision": Vision_Step_Editor(cell_widget=self),
"leak": Leak_Step_Editor(), "leak": Leak_Step_Editor(cell_widget=self),
} }
self.editor = None self.editor = None
self.editor_type = None self.editor_type = None
@ -36,9 +33,9 @@ class Step_Spec_JEDECW(QPushButton, Cell):
self.dialog.setCentralWidget(self.editor) self.dialog.setCentralWidget(self.editor)
self.dialog.adjustSize() self.dialog.adjustSize()
def set_readonly(self, readonly): def set_readonly(self, readonly=True):
for editor in self.editors.values(): for editor in self.editors.values():
editor.set_readonly(readonly) editor.set_readonly(readonly=readonly)
def do_autocomplete(self, autocomplete): def do_autocomplete(self, autocomplete):
for editor_type, editor in self.editors.items(): for editor_type, editor in self.editors.items():

View File

@ -38,11 +38,12 @@ class Test(Widget):
self.cycle_available_steps = { self.cycle_available_steps = {
# "assembly_1": Test_Assembly(self.select_step_img("assembly_1"), u"INSERIRE SENSORE"), # "assembly_1": Test_Assembly(self.select_step_img("assembly_1"), u"INSERIRE SENSORE"),
"autotest": Test_Assembly(None, u"ESEGUIRE PROCEDURA DI AUTOTEST", Test_Autotest()), "autotest": Test_Assembly(None, u"ESEGUIRE PROCEDURA DI AUTOTEST", Test_Autotest()),
"barcodes": Test_Assembly(None, u"SCANSIONARE IL SERIALE DEL PEZZO DA COLLAUDARE", Test_Barcodes()),
"done": Test_Assembly(self.select_step_img("success"), u"COLLAUDO COMPLETATO - RIMUOVERE IL SENSORE"), "done": Test_Assembly(self.select_step_img("success"), u"COLLAUDO COMPLETATO - RIMUOVERE IL SENSORE"),
"emergency": Test_Assembly(self.select_step_img("reset_emergency"), u"EMERGENZA INTERVENUTA - RIPRISTINARE PULSANTE E SELEZIONARE \"RESET EMERGENZA\" DAL MEN\u00d9 \"STRUMENTI\""), "emergency": Test_Assembly(self.select_step_img("reset_emergency"), u"EMERGENZA INTERVENUTA - RIPRISTINARE PULSANTE E SELEZIONARE \"RESET EMERGENZA\" DAL MEN\u00d9 \"STRUMENTI\""),
"fail": Test_Assembly(self.select_step_img("fail"), u"CICLO INTERROTTO - RIMUOVERE IL SENSORE"), "fail": Test_Assembly(self.select_step_img("fail"), u"CICLO INTERROTTO - RIMUOVERE IL SENSORE"),
"leak": Test_Assembly(None, u"ESECUZIONE TEST IN CORSO - ATTENDERE", Test_Leak(components=self.components, recipe=self.recipe, step=self.step)), "leak": Test_Assembly(None, u"ESECUZIONE TEST IN CORSO - ATTENDERE", Test_Leak(components=self.components, recipe=self.recipe, step=self.step)),
"barcodes": Test_Assembly(None, u"SCANSIONARE IL SERIALE DEL PEZZO DA COLLAUDARE", Test_Barcodes()), "print": Test_Assembly(self.select_step_img("print"), u"STAMPA ETICHETTA IN CORSO"),
"select_recipe": Test_Assembly(None, u"SELEZIONARE IL CODICE DA COLLAUDARE", Recipe_Selection()), "select_recipe": Test_Assembly(None, u"SELEZIONARE IL CODICE DA COLLAUDARE", Recipe_Selection()),
"vision": Test_Assembly(None, u"ESEGUIRE PROCEDURA DI AUTOTEST", Test_Vision(components=self.components, recipe=self.recipe, step=self.step)), "vision": Test_Assembly(None, u"ESEGUIRE PROCEDURA DI AUTOTEST", Test_Vision(components=self.components, recipe=self.recipe, step=self.step)),
"wait": Test_Assembly(self.select_step_img("wait"), u"ATTENDERE - PAUSA INTER CICLO"), "wait": Test_Assembly(self.select_step_img("wait"), u"ATTENDERE - PAUSA INTER CICLO"),
@ -58,7 +59,8 @@ class Test(Widget):
# else: # else:
self.autotest_period = None self.autotest_period = None
# INIT TEST DATA # INIT TEST DATA
self.data = {} self.data = {"ok": True, "overridden": False}
self.archived = None
# INIT PIECES COUNTER ([pieces_ok, pieces_failed]) # INIT PIECES COUNTER ([pieces_ok, pieces_failed])
self.pieces = [0, 0] self.pieces = [0, 0]
# CONNECT CYCLE CONTROLS # CONNECT CYCLE CONTROLS
@ -149,7 +151,8 @@ class Test(Widget):
self.step = Steps(type="select_recipe") self.step = Steps(type="select_recipe")
self.cycle_index = -1 self.cycle_index = -1
# RESET TEST DATA # RESET TEST DATA
self.data = {} self.data = {"ok": True, "overridden": False}
self.archived = None
elif action == "fail": elif action == "fail":
self.log.info(f"cycle next: action: {action!r}") self.log.info(f"cycle next: action: {action!r}")
# COUNT FAIL # COUNT FAIL
@ -158,7 +161,8 @@ class Test(Widget):
self.step = Steps(type="fail") self.step = Steps(type="fail")
self.cycle_index = -1 self.cycle_index = -1
# RESET TEST DATA # RESET TEST DATA
self.data = {} self.data = {"ok": True, "overridden": False}
self.archived = None
elif action is not None: elif action is not None:
raise NotImplementedError(f"cycle next: action {action!r} is not a valid action") raise NotImplementedError(f"cycle next: action {action!r} is not a valid action")
# if action did not set the next cycle step # if action did not set the next cycle step
@ -180,6 +184,7 @@ class Test(Widget):
self.step = self.cycle_steps[self.cycle_index] self.step = self.cycle_steps[self.cycle_index]
else: else:
self.cycle_index = -1 self.cycle_index = -1
self.archived = None
self.step = Steps(type=None) self.step = Steps(type=None)
# enable/disable cycle controls # enable/disable cycle controls
self.change_recipe_b.setEnabled(self.recipe is not None) self.change_recipe_b.setEnabled(self.recipe is not None)
@ -192,14 +197,18 @@ class Test(Widget):
self.log.info(f"cycle next: next cycle step: {self.step!r}") self.log.info(f"cycle next: next cycle step: {self.step!r}")
# INIT TEST DATA IF STARTING CYCLE LOOP # INIT TEST DATA IF STARTING CYCLE LOOP
if self.cycle_index == 0: if self.cycle_index == 0:
self.data = {} self.data = {"ok": True, "overridden": False}
self.archived = None
w = self.cycle_available_steps[self.step.type] w = self.cycle_available_steps[self.step.type]
if hasattr(w, "start"): if hasattr(w, "start"):
w.start(recipe=self.recipe, step=self.cycle_steps[self.cycle_index]) w.start(recipe=self.recipe, step=self.cycle_steps[self.cycle_index])
if w is not current_w: if w is not current_w:
self.setCentralWidget(w) self.setCentralWidget(w)
if self.step.type == "done": if self.step.type == "done":
self.done() self.archived = self.done()
self.next_timer.start(2000)
elif self.step.type == "print":
self.print(self.archived)
self.next_timer.start(2000) self.next_timer.start(2000)
elif self.step.type == "fail": elif self.step.type == "fail":
self.next_timer.start(2000) self.next_timer.start(2000)
@ -213,7 +222,17 @@ class Test(Widget):
if self.recipe is None: if self.recipe is None:
self.cycle_steps = None self.cycle_steps = None
else: else:
self.cycle_steps = [Steps(type="barcodes")] + self.recipe.get_steps() + [Steps(type="done"), Steps(type="wait")] steps = self.recipe.get_steps()
print_found = False
for i, step in enumerate(steps):
if step.type == "print":
steps.insert(i, Steps(type="done"))
print_found = True
break
if not print_found:
steps.append(Steps(type="done"))
steps.append(Steps(type="wait"))
self.cycle_steps = steps
# UPDATE RECIPE DISPLAY # UPDATE RECIPE DISPLAY
if self.recipe is not None: if self.recipe is not None:
self.recipe_l.setText(self.recipe.name) self.recipe_l.setText(self.recipe.name)
@ -233,24 +252,30 @@ class Test(Widget):
if "leak" not in self.data: if "leak" not in self.data:
self.data["leak"] = [] self.data["leak"] = []
self.data["leak"].append(leak) self.data["leak"].append(leak)
self.data["overridden"] = self.data.get("overridden", False) or leak.get("overridden", False) self.data["overridden"] = self.data["overridden"] or leak.get("overridden", False)
self.data["ok"] = self.data.get("ok", True) and leak.get("ok", False) self.data["ok"] = self.data["ok"] and leak.get("ok", False)
self.next() self.next()
def set_vision(self, vision=None): def set_vision(self, vision=None):
if "vision" not in self.data: if "vision" not in self.data:
self.data["vision"] = [] self.data["vision"] = []
self.data["vision"].append(vision) self.data["vision"].append(vision)
self.data["overridden"] = self.data.get("overridden", False) or vision.get("overridden", False) self.data["overridden"] = self.data["overridden"] or vision.get("overridden", False)
self.data["ok"] = self.data.get("ok", True) and vision.get("ok", False) self.data["ok"] = self.data["ok"] and vision.get("ok", False)
self.next() self.next()
def done(self, ok=True): def done(self, ok=True):
self.log.info("cycle done") self.log.info("cycle done")
archived = Archive.archive(self.recipe, self.data, ok and self.data["ok"], overridden=self.data["overridden"]) archived = Archive.archive(self.recipe, self.data, ok and self.data["ok"], overridden=self.data["overridden"])
self.log.info(f"cycle archived locally: {archived!r}") self.log.info(f"cycle archived locally: {archived!r}")
# COUNT OK
self.pieces[0] += 1
return archived
def print(self, archived=None):
self.log.info("cycle print")
# LABEL PRINT # LABEL PRINT
self.components["label_printer"].print_label("EtichettaR5", context={ context = {
"DATAMATRIX": str(self.data.get("barcodes", {}).get("serial", "")), "DATAMATRIX": str(self.data.get("barcodes", {}).get("serial", "")),
"DATAMATRIX_TEXT": str(self.data.get("barcodes", {}).get("serial", "-")), "DATAMATRIX_TEXT": str(self.data.get("barcodes", {}).get("serial", "-")),
"PIECE_NUM": str(self.data.get("barcodes", {}).get("serial", "-")), "PIECE_NUM": str(self.data.get("barcodes", {}).get("serial", "-")),
@ -267,9 +292,8 @@ class Test(Widget):
"TTEST": "-", "TTEST": "-",
"MAXLEAK": "-", "MAXLEAK": "-",
"PTESTMIN": "-", "PTESTMIN": "-",
"RESULT_L1": str(self.data.get("leak", [{}])[0].get("results", {}).get("result", "FAIL")), "RESULT_L1": str(self.data.get("leak", [{}, {}])[0].get("results", {}).get("result", "FAIL")),
"RESULT_L2": str(self.data.get("leak", [{}])[1].get("results", {}).get("result", "FAIL")), "RESULT_L2": str(self.data.get("leak", [{}, {}])[1].get("results", {}).get("result", "FAIL")),
}) }
self.log.info(f"cycle printed: {archived!r}") self.components["label_printer"].print_label("EtichettaR5", context=context)
# COUNT OK self.log.info(f"cycle printed: {context!r}")
self.pieces[0] += 1

View File

@ -2,8 +2,8 @@ from ui.editor import Editor
class Vision_Step_Editor(Editor): class Vision_Step_Editor(Editor):
def __init__(self): def __init__(self, cell_widget=None):
super().__init__() super().__init__(cell_widget=cell_widget)
self.spec.update({ self.spec.update({
"recipe": self.vision_recipe_cb, "recipe": self.vision_recipe_cb,
}) })