diff --git a/src/lib/db/__init__.py b/src/lib/db/__init__.py index 9bf0cd9..d8f0a4f 100644 --- a/src/lib/db/__init__.py +++ b/src/lib/db/__init__.py @@ -59,66 +59,66 @@ if admin is None or not admin.is_admin: Users.register(username="ADMIN", password="123123", roles=["admin"]) # register or reset user Users.register(username="USER", password="user") -# register test steps -Steps.replace( - id=0, - name="TEST_LEAK_TEST", - type="leak", - spec={ - "pre_filling_time": 5, - "pre_filling_pressure": 1000, - "filling_time": 5, - "settling_time": 10, - "settling_pressure_min_percent": 50, - "settling_pressure_max_percent": 50, - "test_time": 10, - "test_pressure_min_delta": 500, - "test_pressure": 1000, - "test_pressure_max_delta": 500, - "flush_time": 2, - "flush_pressure": 5, - }, - description="TEST_DESCRIPTION", - archived=False, -).execute() -Steps.replace( - id=1, - name="TEST_LEAK_TEST", - type="leak", - spec={ - "pre_filling_time": 5, - "pre_filling_pressure": 1000, - "filling_time": 5, - "settling_time": 10, - "settling_pressure_min_percent": 50, - "settling_pressure_max_percent": 50, - "test_time": 10, - "test_pressure_min_delta": 500, - "test_pressure": 1000, - "test_pressure_max_delta": 500, - "flush_time": 2, - "flush_pressure": 5, - }, - description="TEST_DESCRIPTION", - archived=False, -).execute() -Steps.replace( - id=2, - name="TEST_VISION_TEST", - type="vision", - spec={ - "recipe": "1.ini", - }, - description="TEST_DESCRIPTION VISION_RECIPE: 1", - archived=False, -).execute() +# # register test steps +# Steps.replace( +# id=0, +# name="TEST_LEAK_TEST", +# type="leak", +# spec={ +# "pre_filling_time": 5, +# "pre_filling_pressure": 1000, +# "filling_time": 5, +# "settling_time": 10, +# "settling_pressure_min_percent": 50, +# "settling_pressure_max_percent": 50, +# "test_time": 10, +# "test_pressure_min_delta": 500, +# "test_pressure": 1000, +# "test_pressure_max_delta": 500, +# "flush_time": 2, +# "flush_pressure": 5, +# }, +# description="TEST_DESCRIPTION", +# archived=False, +# ).execute() +# Steps.replace( +# id=1, +# name="TEST_LEAK_TEST", +# type="leak", +# spec={ +# "pre_filling_time": 5, +# "pre_filling_pressure": 1000, +# "filling_time": 5, +# "settling_time": 10, +# "settling_pressure_min_percent": 50, +# "settling_pressure_max_percent": 50, +# "test_time": 10, +# "test_pressure_min_delta": 500, +# "test_pressure": 1000, +# "test_pressure_max_delta": 500, +# "flush_time": 2, +# "flush_pressure": 5, +# }, +# description="TEST_DESCRIPTION", +# archived=False, +# ).execute() +# Steps.replace( +# id=2, +# name="TEST_VISION_TEST", +# type="vision", +# spec={ +# "recipe": "1.ini", +# }, +# description="TEST_DESCRIPTION VISION_RECIPE: 1", +# archived=False, +# ).execute() # register test recipe Recipes.replace( id=0, name="TEST", client="TEST_CLIENT", part_number="TEST_PART_NUMBER", - spec={"steps": [0, 1, 2, ]}, + spec={"steps": []}, description="TEST_DESCRIPTION", archived=False, ).execute() diff --git a/src/lib/db/models/steps.py b/src/lib/db/models/steps.py index 7c73d87..3417dc6 100644 --- a/src/lib/db/models/steps.py +++ b/src/lib/db/models/steps.py @@ -6,10 +6,10 @@ from .base_model import BaseModel class Steps(BaseModel): id = AutoField(primary_key=True, unique=True, null=False) - name = TextField(null=False) + name = TextField(null=True) type = TextField(null=False) 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) @classmethod diff --git a/src/ui/crud/crud.py b/src/ui/crud/crud.py index 78a2db4..c831ad7 100755 --- a/src/ui/crud/crud.py +++ b/src/ui/crud/crud.py @@ -6,7 +6,7 @@ from datetime import datetime from lib.db import Crud_DB from peewee import TextField -from PyQt5.QtCore import Qt, pyqtSignal +from PyQt5.QtCore import QObject, Qt, pyqtSignal from PyQt5.QtWidgets import (QAbstractItemView, QComboBox, QDialog, QGridLayout, QHeaderView, QLineEdit, QMessageBox, QPlainTextEdit, QPushButton) @@ -53,7 +53,7 @@ class Cell: self.set_readonly(self.readonly) self.do_autocomplete(self.autocomplete) - def set_readonly(self, readonly): + def set_readonly(self, readonly=True): raise NotImplementedError() def do_autocomplete(self, autocomplete): @@ -104,7 +104,7 @@ class Line_Edit_Cell_Widget(QLineEdit, Cell): 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) - def set_readonly(self, readonly): + def set_readonly(self, readonly=True): self.setReadOnly(readonly) def connect_modified(self): @@ -123,7 +123,7 @@ class Combo_Box_Cell_Widget(QComboBox, Cell): 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) - def set_readonly(self, readonly): + def set_readonly(self, readonly=True): self.setEditable(not readonly) def do_autocomplete(self, autocomplete): @@ -151,11 +151,14 @@ class External_Dialog_Cell_Widget(QPushButton, Cell): 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.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) + def show_dialog(self): + self.dialog.show() + def connect_modified(self): 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): - def __init__(self, editor_widget_instance, readonly=True, autocomplete=None, field_name=None, field_alias=None, field=None, row_number=None, crud=None): - if not isinstance(editor_widget_instance, Editor): - raise AssertionError(f"editor_widget_instance {editor_widget_instance!r} must be a subclass of {Editor!r}") - self.editor = editor_widget_instance + 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 issubclass(editor_widget_class, Editor): + raise AssertionError(f"editor_widget_class {editor_widget_class!r} must be a subclass of {Editor!r}") + 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.setAttribute(Qt.WA_DeleteOnClose, on=False) 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.clicked.connect(self.dialog.show) + self.clicked.connect(self.show_dialog) + if hasattr(self.editor, "init"): + self.editor.init() - def set_readonly(self, readonly): - self.editor.set_readonly(readonly) + def set_readonly(self, readonly=True): + self.editor.set_readonly(readonly=readonly) def do_autocomplete(self, 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): self.editor.connect_modified(self.check_modified) @@ -412,6 +422,8 @@ class Crud(Widget): 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) 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) if fn in r: 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(): 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.set_readonly(readonly=w.readonly) + w.do_autocomplete(w.autocomplete) w.modified.connect(self.set_modified) self.db_tw.setCellWidget(rn, cn, w) self.db_tw.scrollToBottom() diff --git a/src/ui/editor/editor.py b/src/ui/editor/editor.py index 2cf9d30..38973f8 100644 --- a/src/ui/editor/editor.py +++ b/src/ui/editor/editor.py @@ -6,16 +6,19 @@ from ui.widget import Widget class Editor(Widget): - def __init__(self): + def __init__(self, cell_widget=None): super().__init__() + self.cell_widget = cell_widget self.spec = {} - def set_readonly(self, readonly): + def set_readonly(self, readonly=True): for w in self.spec.values(): if isinstance(w, QComboBox): w.setDisabled(readonly) elif isinstance(w, QListWidget): w.setDisabled(readonly) + elif isinstance(w, QCheckBox): + w.setDisabled(readonly) elif isinstance(w, QObject): w.setReadOnly(readonly) diff --git a/src/ui/imgs/print.png b/src/ui/imgs/print.png new file mode 100644 index 0000000..52b2a72 Binary files /dev/null and b/src/ui/imgs/print.png differ diff --git a/src/ui/leak_step_editor/leak_step_editor.py b/src/ui/leak_step_editor/leak_step_editor.py index a88a14c..0ae64d5 100644 --- a/src/ui/leak_step_editor/leak_step_editor.py +++ b/src/ui/leak_step_editor/leak_step_editor.py @@ -2,8 +2,8 @@ from ui.editor import Editor class Leak_Step_Editor(Editor): - def __init__(self): - super().__init__() + def __init__(self, cell_widget=None): + super().__init__(cell_widget=cell_widget) self.spec.update({ # pre-filling "pre_filling_time": self.pre_filling_time_sb, diff --git a/src/ui/recipe_selection/recipe_selection.py b/src/ui/recipe_selection/recipe_selection.py index 266f9af..1226ae8 100755 --- a/src/ui/recipe_selection/recipe_selection.py +++ b/src/ui/recipe_selection/recipe_selection.py @@ -49,7 +49,7 @@ class Recipe_Selection(Widget): autocomplete={ "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) self.crud_modified = None diff --git a/src/ui/recipe_spec_and_step_editor/recipe_spec_and_step_editor.py b/src/ui/recipe_spec_and_step_editor/recipe_spec_and_step_editor.py index 5c9f9a8..6610b64 100644 --- a/src/ui/recipe_spec_and_step_editor/recipe_spec_and_step_editor.py +++ b/src/ui/recipe_spec_and_step_editor/recipe_spec_and_step_editor.py @@ -1,136 +1,139 @@ +from lib.db import Steps +from playhouse.shortcuts import model_to_dict from ui.editor import Editor 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): - def __init__(self): - super().__init__() - # self.spec.update({ - # "barcode_editor": self.barcode_editor_w, - # "leak_editor_1": self.leak_editor_1_w, - # "leak_editor_2": self.leak_editor_2_w, - # "vision_editor": self.vision_editor_w, - # "print_editor": self.print_editor_w, - # "barcode_enabled": self.barcode_enabled_cb, - # "leak_enabled_1": self.leak_enabled_1_cb, - # "leak_eabled_2": self.leak_eabled_2_cb, - # "vision_enabled": self.vision_enabled_cb, - # "vision_enabled": self.vision_enabled_cb, - # "print_enabled_cb": self.print_enabled_cb, - # }) - # replace_widget(self, "barcode_editor_w", _Editor()) - # replace_widget(self, "leak_editor_1_w", _Editor()) - # replace_widget(self, "leak_editor_2_w", _Editor()) - # replace_widget(self, "vision_editor_w", _Editor()) - # replace_widget(self, "print_editor_w", _Editor()) - # - # def do_autocomplete(self, autocomplete): - # if autocomplete is None: - # return - # for k, v in autocomplete.items(): - # w = self.spec[k] - # if isinstance(w, QCheckBox): - # w.setChecked(bool(v)) - # elif isinstance(w, QComboBox): - # # while w.count() > 0: - # # w.removeItem(0) - # # w.clear() - # w.addItems(list(map(str, v))) - # elif isinstance(w, QDoubleSpinBox): - # w.setValue(float(v)) - # elif isinstance(w, QLineEdit): - # w.setText(str(v)) - # elif isinstance(w, QListWidget): - # # while w.count() > 0: - # # w.takeItem(0) - # # w.clear() - # w.addItems(list(map(str, v))) - # elif isinstance(w, QRadioButton): - # w.setChecked(bool(v)) - # elif isinstance(w, QSpinBox): - # w.setValue(int(v)) - # elif isinstance(w, QPlainTextEdit): - # w.setPlainText(str(v)) - # elif isinstance(w, QObject): - # raise NotImplementedError(f"widget of type {type(w)!r} not implemented") - # else: - # # ignore default values - # pass - # + def __init__(self, cell_widget=None): + super().__init__(cell_widget=cell_widget) + self.map = { + "barcodes": { + "type": "barcodes", + "enable": self.barcode_enabled_cb, + "widget": "barcode_editor_w", + # "editor": _Editor, + }, + "leak_1": { + "type": "leak", + "enable": self.leak_enabled_1_cb, + "widget": "leak_editor_1_w", + "editor": Leak_Step_Editor(), + }, + "leak_2": { + "type": "leak", + "enable": self.leak_eabled_2_cb, + "widget": "leak_editor_2_w", + "editor": Leak_Step_Editor(), + }, + "vision": { + "type": "vision", + "enable": self.vision_enabled_cb, + "widget": "vision_editor_w", + "editor": Vision_Step_Editor(), + }, + "print": { + "type": "print", + "enable": self.print_enabled_cb, + "widget": "print_editor_w", + # "editor": _Editor, + }, + } + if len(self.map) > 1024: + raise NotImplementedError("self.map must contain at most 1024 steps") + self.spec.update({editor_name: map["enable"] for editor_name, map in self.map.items()}) + for editor_name, map in self.map.items(): + editor = map.get("editor", None) + if editor is not None: + replace_widget(self, map["widget"], editor) + self.save_steps_b.clicked.connect(self.save_steps) + self.reset_steps_b.clicked.connect(self.reset_steps) + self.recipe_id = None + self.step_editors_autocomplete = {} + + def set_readonly(self, readonly=True): + super().set_readonly(readonly=readonly) + for editor_name, map in self.map.items(): + editor = map.get("editor", None) + if editor is not None: + editor.set_readonly(readonly=readonly) + 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): - # for k, w in self.spec.items(): - # if isinstance(w, QCheckBox): - # w.toggled.connect(check_modified) - # elif isinstance(w, QComboBox): - # w.currentTextChanged.connect(check_modified) - # elif isinstance(w, QDoubleSpinBox): - # w.valueChanged.connect(check_modified) - # elif isinstance(w, QLineEdit): - # w.textChanged.connect(check_modified) - # elif isinstance(w, QListWidget): - # w.currentItemChanged.connect(check_modified) - # elif isinstance(w, QRadioButton): - # w.toggled.connect(check_modified) - # elif isinstance(w, QSpinBox): - # w.valueChanged.connect(check_modified) - # elif isinstance(w, QPlainTextEdit): - # w.textChanged.connect(check_modified) - # elif isinstance(w, QObject): - # raise NotImplementedError(f"widget of type {type(w)!r} not implemented") - # else: - # # ignore default values - # pass - # - # def render(self, data, field_name=None, row_number=None, crud=None): - # for k, v in data.items(): - # if k not in self.spec: - # continue - # w = self.spec[k] - # if isinstance(w, QCheckBox): - # w.setChecked(bool(v)) - # elif isinstance(w, QComboBox): - # w.setCurrentText(str(v)) - # elif isinstance(w, QDoubleSpinBox): - # w.setValue(float(v)) - # elif isinstance(w, QLineEdit): - # w.setText(str(v)) - # elif isinstance(w, QListWidget): - # w.clear() - # w.addItems(list(map(str, v))) - # elif isinstance(w, QRadioButton): - # w.setChecked(bool(v)) - # elif isinstance(w, QSpinBox): - # w.setValue(int(v)) - # elif isinstance(w, QPlainTextEdit): - # w.setPlainText(str(v)) - # elif isinstance(w, QObject): - # raise NotImplementedError(f"widget of type {type(w)!r} not implemented") - # else: - # # ignore default values - # pass - # - # def parse(self, row_number=None, crud=None): - # ret = {} - # for k, w in self.spec.items(): - # if isinstance(w, QCheckBox): - # ret[k] = w.isChecked() - # elif isinstance(w, QComboBox): - # 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 + # super().connect_modified(check_modified) + # # for map in self.map.values(): + # # editor = map.get("editor", None) + # # if editor is not None: + # # editor.connect_modified(self.check_modified) + + def render(self, data, field_name=None, row_number=None, crud=None): + super().render(data, field_name=field_name, row_number=row_number, crud=crud) + steps = set(data.get("steps", set())) + for map in self.map.values(): + map["enable"].setChecked(map["step"]["id"] in steps) + + def parse(self, row_number=None, crud=None): + ret = super().parse(row_number=row_number, crud=crud) + ret["steps"] = [] + for map in self.map.values(): + if map["enable"].isChecked(): + ret["steps"].append(map["step"]["id"]) + return ret + + @staticmethod + def get_step_id(recipe_id, step_n): + return recipe_id * 1024 + step_n + + def get_steps(self): + for n, [editor_name, map] in enumerate(self.map.items()): + id = self.get_step_id(self.recipe_id, n) + try: + step = model_to_dict(Steps.get_by_id(id)) + except Steps.DoesNotExist: + step = { + "id": id, + "type": map["type"], + "spec": {}, + } + map["step"] = step + + def render_steps(self): + for n, [editor_name, map] in enumerate(self.map.items()): + editor = map.get("editor", None) + step = map["step"] + if editor is not None: + editor.render(step["spec"]) + + def reset_steps(self): + self.get_steps() + self.render_steps() + + def save_steps(self): + steps = [] + for map in self.map.values(): + editor = map.get("editor", None) + if editor is not None: + map["step"]["spec"] = editor.parse() + steps.append(map["step"]) + Steps.replace_many(steps).execute() + self.reset_steps() diff --git a/src/ui/recipe_spec_and_step_editor/recipe_spec_and_step_editor.ui b/src/ui/recipe_spec_and_step_editor/recipe_spec_and_step_editor.ui index 2710a3f..30efe43 100644 --- a/src/ui/recipe_spec_and_step_editor/recipe_spec_and_step_editor.ui +++ b/src/ui/recipe_spec_and_step_editor/recipe_spec_and_step_editor.ui @@ -6,15 +6,31 @@ 0 0 - 247 - 145 + 330 + 163 Recipe Spec And Step Editor - + + + + Salva configurasione +Fasi di Test + + + + + + + Resetta configurazione +Fasi di Test + + + + 0 @@ -236,13 +252,6 @@ - - - - Salva - - - diff --git a/src/ui/recipe_spec_editor/recipe_spec_editor.py b/src/ui/recipe_spec_editor/recipe_spec_editor.py index 3f6291d..6a14dad 100644 --- a/src/ui/recipe_spec_editor/recipe_spec_editor.py +++ b/src/ui/recipe_spec_editor/recipe_spec_editor.py @@ -3,15 +3,15 @@ from ui.editor import Editor class Recipe_Spec_Editor(Editor): - def __init__(self): - super().__init__() + def __init__(self, cell_widget=None): + super().__init__(cell_widget=cell_widget) self.spec.update({ "steps": self.steps_lw, }) self.steps_lw.setDragDropMode(QAbstractItemView.InternalMove) - def set_readonly(self, readonly): - super().set_readonly(readonly) + def set_readonly(self, readonly=True): + super().set_readonly(readonly=readonly) self.steps_lw.setDisabled(readonly) self.remove_step_b.setDisabled(readonly) self.steps_available_lw.setDisabled(readonly) diff --git a/src/ui/recipes_management/recipes_management.py b/src/ui/recipes_management/recipes_management.py index 1452cd8..c0b1b3f 100644 --- a/src/ui/recipes_management/recipes_management.py +++ b/src/ui/recipes_management/recipes_management.py @@ -24,6 +24,6 @@ class Recipes_Management(Widget): autocomplete={ "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) diff --git a/src/ui/steps_management/steps_management.py b/src/ui/steps_management/steps_management.py index 486b251..4a5735a 100644 --- a/src/ui/steps_management/steps_management.py +++ b/src/ui/steps_management/steps_management.py @@ -1,11 +1,8 @@ from glob import iglob from PyQt5.QtCore import Qt -from PyQt5.QtWidgets import (QCheckBox, QComboBox, QDoubleSpinBox, QLineEdit, - QPlainTextEdit, QPushButton, QRadioButton, - QSpinBox) -from ui.crud import (Cell, Combo_Box_Cell_Widget, Crud, - Json_External_Dialog_Editor_Cell_Widget) +from PyQt5.QtWidgets import QPushButton +from ui.crud import Cell, Combo_Box_Cell_Widget, Crud from ui.dialog import Dialog from ui.leak_step_editor import Leak_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): def __init__(self, readonly=True, autocomplete=None, field_name=None, field_alias=None, field=None, row_number=None, crud=None): self.editors = { - "vision": Vision_Step_Editor(), - "leak": Leak_Step_Editor(), + "vision": Vision_Step_Editor(cell_widget=self), + "leak": Leak_Step_Editor(cell_widget=self), } self.editor = None self.editor_type = None @@ -36,9 +33,9 @@ class Step_Spec_JEDECW(QPushButton, Cell): self.dialog.setCentralWidget(self.editor) self.dialog.adjustSize() - def set_readonly(self, readonly): + def set_readonly(self, readonly=True): for editor in self.editors.values(): - editor.set_readonly(readonly) + editor.set_readonly(readonly=readonly) def do_autocomplete(self, autocomplete): for editor_type, editor in self.editors.items(): diff --git a/src/ui/test/test.py b/src/ui/test/test.py index 4d23191..297d699 100755 --- a/src/ui/test/test.py +++ b/src/ui/test/test.py @@ -38,11 +38,12 @@ class Test(Widget): self.cycle_available_steps = { # "assembly_1": Test_Assembly(self.select_step_img("assembly_1"), u"INSERIRE SENSORE"), "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"), "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"), "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()), "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"), @@ -58,7 +59,8 @@ class Test(Widget): # else: self.autotest_period = None # INIT TEST DATA - self.data = {} + self.data = {"ok": True, "overridden": False} + self.archived = None # INIT PIECES COUNTER ([pieces_ok, pieces_failed]) self.pieces = [0, 0] # CONNECT CYCLE CONTROLS @@ -149,7 +151,8 @@ class Test(Widget): self.step = Steps(type="select_recipe") self.cycle_index = -1 # RESET TEST DATA - self.data = {} + self.data = {"ok": True, "overridden": False} + self.archived = None elif action == "fail": self.log.info(f"cycle next: action: {action!r}") # COUNT FAIL @@ -158,7 +161,8 @@ class Test(Widget): self.step = Steps(type="fail") self.cycle_index = -1 # RESET TEST DATA - self.data = {} + self.data = {"ok": True, "overridden": False} + self.archived = None elif action is not None: raise NotImplementedError(f"cycle next: action {action!r} is not a valid action") # if action did not set the next cycle step @@ -180,6 +184,7 @@ class Test(Widget): self.step = self.cycle_steps[self.cycle_index] else: self.cycle_index = -1 + self.archived = None self.step = Steps(type=None) # enable/disable cycle controls 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}") # INIT TEST DATA IF STARTING CYCLE LOOP if self.cycle_index == 0: - self.data = {} + self.data = {"ok": True, "overridden": False} + self.archived = None w = self.cycle_available_steps[self.step.type] if hasattr(w, "start"): w.start(recipe=self.recipe, step=self.cycle_steps[self.cycle_index]) if w is not current_w: self.setCentralWidget(w) 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) elif self.step.type == "fail": self.next_timer.start(2000) @@ -213,7 +222,17 @@ class Test(Widget): if self.recipe is None: self.cycle_steps = None 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 if self.recipe is not None: self.recipe_l.setText(self.recipe.name) @@ -233,24 +252,30 @@ class Test(Widget): if "leak" not in self.data: self.data["leak"] = [] self.data["leak"].append(leak) - self.data["overridden"] = self.data.get("overridden", False) or leak.get("overridden", False) - self.data["ok"] = self.data.get("ok", True) and leak.get("ok", False) + self.data["overridden"] = self.data["overridden"] or leak.get("overridden", False) + self.data["ok"] = self.data["ok"] and leak.get("ok", False) self.next() def set_vision(self, vision=None): if "vision" not in self.data: self.data["vision"] = [] self.data["vision"].append(vision) - self.data["overridden"] = self.data.get("overridden", False) or vision.get("overridden", False) - self.data["ok"] = self.data.get("ok", True) and vision.get("ok", False) + self.data["overridden"] = self.data["overridden"] or vision.get("overridden", False) + self.data["ok"] = self.data["ok"] and vision.get("ok", False) self.next() def done(self, ok=True): self.log.info("cycle done") 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}") + # COUNT OK + self.pieces[0] += 1 + return archived + + def print(self, archived=None): + self.log.info("cycle print") # LABEL PRINT - self.components["label_printer"].print_label("EtichettaR5", context={ + context = { "DATAMATRIX": 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", "-")), @@ -267,9 +292,8 @@ class Test(Widget): "TTEST": "-", "MAXLEAK": "-", "PTESTMIN": "-", - "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")), - }) - self.log.info(f"cycle printed: {archived!r}") - # COUNT OK - self.pieces[0] += 1 + "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")), + } + self.components["label_printer"].print_label("EtichettaR5", context=context) + self.log.info(f"cycle printed: {context!r}") diff --git a/src/ui/vision_step_editor/vision_step_editor.py b/src/ui/vision_step_editor/vision_step_editor.py index 20c2c9e..5dbaf32 100644 --- a/src/ui/vision_step_editor/vision_step_editor.py +++ b/src/ui/vision_step_editor/vision_step_editor.py @@ -2,8 +2,8 @@ from ui.editor import Editor class Vision_Step_Editor(Editor): - def __init__(self): - super().__init__() + def __init__(self, cell_widget=None): + super().__init__(cell_widget=cell_widget) self.spec.update({ "recipe": self.vision_recipe_cb, })