diff --git a/bin/gui.bat b/bin/gui.bat new file mode 100644 index 0000000..3d01e5f --- /dev/null +++ b/bin/gui.bat @@ -0,0 +1,6 @@ +@echo off +CALL cremig_setenv.bat +IF NOT EXIST %CREMIG%\.venv CALL %CREMIG_BIN%\install_py.bat +CALL %CREMIG%\.venv\Scripts\activate.bat +python %CREMIG_LIB%\CreoMigrateGui.py %* +deactivate diff --git a/lib/CreoMigrateGui.py b/lib/CreoMigrateGui.py new file mode 100644 index 0000000..274dcd7 --- /dev/null +++ b/lib/CreoMigrateGui.py @@ -0,0 +1,309 @@ +import tkinter as tk +from tkinter import ttk +from controller import Controller +from caditem import CadItem +#from tkinter import messagebox +#import logging + + +#logger = logging.getLogger(__name__) +class TreeView(): + def __init__(self, frame): + # a treeview + # hier Anzahl der sichtbaren Zeilen des Baums angeben: + self.tree = ttk.Treeview(frame, height=40) + # TODO hier anstatt root die Statistik rein schreiben. + self.tree.heading('#0', text='root', anchor=tk.W) + + # add a scrollbar + scrollbar = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=self.tree.yview) + self.tree.configure(yscroll=scrollbar.set) + scrollbar.grid(row=0, column=1, sticky='ns') + + self.tree.bind('<>', self.item_selected) + + def set_controller(self, controller): + self.controller = controller + + def refresh(self, parent_children_mapping, red_ids): + self.clear() + self.create(parent_children_mapping, red_ids) + + def clear(self): + for c in self.tree.get_children(): + self.tree.delete(c) + + def create(self, parent_children_mapping, red_ids): + # adding data of first column after root + pool = set() + for parent,children in parent_children_mapping.items(): + if parent not in pool: + self.tree.insert('', tk.END, text=parent, iid=parent, open=True) + pool.add(parent) + cn = children + # sortiere die Zahlen im Baum immer aufsteigend + cn.sort(reverse=True) + for child_id in cn: + if child_id not in pool: + pool.add(child_id) + if child_id not in red_ids: rowtags = ('good') + else: rowtags = ('missing') + self.tree.insert('', tk.END, text=child_id, iid=child_id, tags=rowtags, open=True) + #self.tree.move(child_id, parent, 0) + + # einmal über alles gehen und alle keys an die richtige Stelle schieben + for parent,children in parent_children_mapping.items(): + for child_id in children: + self.tree.move(child_id, parent, 0) + + # https://www.askpython.com/python-modules/tkinter/tkinter-colors + #self.tree.tag_configure('good', background='LawnGreen') + self.tree.tag_configure('good', background='white') + self.tree.tag_configure('missing', background='red') + + def data_from_model(self, controller): + parent_children_mapping = controller.get_parent_children() + red_ids = controller.get_red_ids() + self.refresh(parent_children_mapping, red_ids) + + def item_selected(self, event): + """callback of TK if an item in the tree has been selected + """ + cid = self.tree.selection()[0] + # trage die neue ID ins Modell ein + self.controller.set_gui_data('teilenummer', cid) + self.controller.refresh_gui() + + def get_handle(self): + return self.tree + +class AllRightFrames(): + """ enthält alle gleichen Methoden für die Widgets in der rechte Spalte + """ + def __init__(self, nrow=0): + self.set_start_rwo(nrow) + + def get_rows(self): + return self.rownum + + def set_start_rwo(self, nrow): + if nrow < 0: + raise Exception("this row can not be set") + self.rownum = nrow + + def data_to_model(self, controller): + data = self.get_data() + controller.set_gui_dict(data) + + def data_to_model(self, controller, key, value): + controller.set_gui_data(key, value) + +class RightFrameLabels(AllRightFrames): + def __init__(self, frame, nrow=0): + AllRightFrames.__init__(self, nrow) + self.frame = frame + self.build_widgets() + + def build_widgets(self): + frame = self.frame + self.rownum = 0 + col_left = 0 + # Feld, SIVAS-Werte, Überschriebene Werte + feld_label = ttk.Label(frame, text="Feld" ) + feld_label.grid(column=col_left, row=self.rownum, sticky=tk.W, padx=5, pady=5) + Sivas_label = ttk.Label(frame, text="SIVAS-Werte" ) + Sivas_label.grid(column=col_left+1, row=self.rownum, sticky=tk.W, padx=5, pady=5) + transwritten_label = ttk.Label(frame, text="Überschriebene Werte" ) + transwritten_label.grid(column=col_left+2, row=self.rownum, sticky=tk.W, padx=5, pady=5) + + feld_label.configure(font=("Helvetica", 16)) + Sivas_label.configure(font=("Helvetica",16)) + transwritten_label.configure(font=("Helvetica",16)) + self.rownum += 1 + + separator = ttk.Separator(frame, orient='horizontal') + separator.grid(column=col_left, columnspan=3, row=self.rownum, sticky=tk.EW, padx=5, pady=5) + self.rownum += 1 + + # Teilenummer + teilenummer_label = ttk.Label(frame, text="Teilenummer:") + teilenummer_label.grid(column=col_left, row=self.rownum, sticky=tk.W, padx=5, pady=5) + self.teilenummer_text = tk.StringVar() + teilenummer_entry = ttk.Entry(frame, textvariable=self.teilenummer_text) + teilenummer_entry.grid(column=col_left+1, row=self.rownum, sticky=tk.W, padx=5, pady=5) + self.rownum += 1 + separator2 = ttk.Separator(frame, orient='horizontal') + separator2.grid(column=col_left, columnspan=3, row=self.rownum, sticky=tk.EW, padx=5, pady=5) + self.rownum += 1 + + + def get_data(self): + return { + "teilennummer": self.teilenummer_text.get(), + } + + def data_from_model(self, controller): + data = controller.get_all_gui_data() + if 'teilenummer' in data: + self.teilenummer_text.set(data["teilenummer"]) + +class RightFramePDM(AllRightFrames): + def __init__(self, frame, nrow=0): + AllRightFrames.__init__(self, nrow) + self.frame = frame + self.build_widgets() + + def build_widgets(self): + frame = self.frame + self.rownum = 4 + col_left = 0 + # pdmkategorie + pdmkategorie_label = ttk.Label(frame, text="PDM-Kategorie:" ) + pdmkategorie_label.grid(column=col_left, row=self.rownum, sticky=tk.W, padx=5, pady=5) + self.pdm_category = tk.StringVar() + pdmkategorie_combo = ttk.Combobox(frame, textvariable=self.pdm_category) + pdmkategorie_combo['values'] = ["Baugruppe", "Einzelteil", "Kaufteil", "NichtSivas", "Normteil", "Schweissbaugruppe"] + pdmkategorie_combo['state'] = 'readonly' + pdmkategorie_combo.bind('<>', self.combo_callbacks) # falls nötig + pdmkategorie_combo.grid(column=col_left+2, row=self.rownum, sticky=tk.W, padx=5, pady=5) + self.rownum += 1 + + # Neuaufbau + neuaufbau_label = ttk.Label(frame, text="Neuaufbau:") + neuaufbau_label.grid(column=col_left, row=self.rownum, sticky=tk.W, padx=5, pady=5) + + self.neuaufbau = None + neuaufbau_checkbutton = ttk.Checkbutton(frame, + text='ja', + variable=self.neuaufbau, + onvalue="True", + offvalue="False") + neuaufbau_checkbutton.grid(column=col_left+2, row=self.rownum, sticky=tk.W, padx=5, pady=5) + self.rownum += 1 + + # Lebenszyklusstatus + lebenszyklusstatus_label = ttk.Label(frame, text="Lebenszyklusstatus:") + lebenszyklusstatus_label.grid(column=col_left, row=self.rownum, sticky=tk.W, padx=5, pady=5) + self.lebenszyklus_status_neu = tk.StringVar() + lebenszyklusstatus_combo = ttk.Combobox(frame, textvariable=self.lebenszyklus_status_neu) + lebenszyklusstatus_combo['values'] = ["Serie", "Prototyp"] + lebenszyklusstatus_combo['state'] = 'readonly' + lebenszyklusstatus_combo.bind('<>', self.combo_callbacks) + lebenszyklusstatus_combo.grid(column=col_left+2, row=self.rownum, sticky=tk.W, padx=5, pady=5) + self.rownum += 1 + + # aus Halbzeug + halbzeug_label = ttk.Label(frame, text="aus Halbzeug:") + halbzeug_label.grid(column=col_left, row=self.rownum, sticky=tk.W, padx=5, pady=5) + self.aus_halbzeug_neu = None + aushalbzeug_checkbuttonr = ttk.Checkbutton(frame, + text='neu', + variable=self.aus_halbzeug_neu, + onvalue="True", + offvalue="False") + aushalbzeug_checkbuttonr.grid(column=col_left+2, row=self.rownum, sticky=tk.W, padx=5, pady=5) + self.rownum += 1 + + # Zusatzinformationen + zusatzinfos_label = ttk.Label(frame, text="Zusatzinformationen:") + zusatzinfos_label.grid(column=col_left, row=self.rownum, sticky=tk.W, padx=5, pady=5) + self.zusatzinfos_text = tk.StringVar() + zusatzinfos_entry = ttk.Entry(frame, textvariable=self.zusatzinfos_text) + zusatzinfos_entry.grid(column=col_left+2, row=self.rownum, sticky=tk.W, padx=5, pady=5) + self.rownum += 1 + + def get_data(self): + return { + "pdm_kategorie_neu": self.pdm_category.get(), + "neuaufbau": self.neuaufbau, + "lebenszyklus_status_neu": self.lebenszyklus_status_neu.get(), + "aus_halbzeug_neu": self.aus_halbzeug_neu, + "zusatzinfos_txt_neu": self.zusatzinfos_text.get(), + } + + def data_from_model(self, controller): + data = controller.get_all_gui_data() + if 'pdm_kategorie_neu' in data: + self.pdm_category.set(data["pdm_kategorie_neu"]) + if 'neuaufbau' in data: + self.neuaufbau = data["neuaufbau"] + if 'lebenszyklus_status_neu' in data: + self.lebenszyklus_status_neu.set(data["lebenszyklus_status_neu"]) + if 'aus_halbzeug_neu' in data: + self.aus_halbzeug_neu= data["aus_halbzeug_neu"] + if 'zusatzinfos_text_neu' in data: + self.zusatzinfos_text.set(data["zusatzinfos_text_neu"]) + + def combo_callbacks(self, event): + """ handle the changed event of TK""" + # TODO falls nötig + pass + + + +class App(): + def __init__(self, root): + """Standardgui mit Baum zur Anwahl der Teile des Assemblies""" + + self.root = root + + # configure the grid + # die erste Spalte mit Baum sollte unbeweglich bleiben bei Vergräßerung des Fensters. + # self.root.columnconfigure(0, weight=2) + self.root.columnconfigure(1, weight=1) + #self.root.geometry('800x800') + + # Create left and right frames + left_frame = ttk.Frame(self.root, width=200, height=800) + left_frame.grid(row=0, column=0, padx=10, pady=5) + + right_frame = ttk.Frame(self.root, width=650, height=800) + right_frame.grid(row=0, column=1, padx=10, pady=5) + + style = ttk.Style(self.root) + style.theme_use('classic') + + self.tv = TreeView(left_frame) + gridhdl = self.tv.get_handle() + gridhdl.grid(row=0, column=0, sticky=tk.NSEW) + + self.rf0 = RightFrameLabels(right_frame, 0) + nrows = self.rf0.get_rows() + self.rf1 = RightFramePDM(right_frame, nrows) + nrows = self.rf1.get_rows() + #self.rf2 = RightFramePDM(right_frame, nrows) + # TODO usw. + + def data_from_model(self): + """ methode zum auslesen aller Daten der Oberfläche aus dem Modell + """ + self.tv.data_from_model(self.controller) + self.rf0.data_from_model(self.controller) + self.rf1.data_from_model(self.controller) + + def set_controller(self, controller): + # merken des Handels auf den Controller + self.controller = controller + # der Tree braucht aufgrund des Callbacks der Selektion auch einen Verweis + self.tv.set_controller(controller) + + def mainloop(self): + self.root.mainloop() + + +if __name__ == "__main__": + + """ using the Model-View-Controller (MVC) Pattern + - The MODEL is responsible for managing the data of the application. It + receives user input from the CONTROLLER + - The VIEW renders presentation of the model in a particular format. + - The CONTROLLER responds to the user input and performs interactions on the + data model objects. The CONTROLLER receives the input, optionally validates + it and then passes the input to the model. + + MODEL + <-updates- ^--manipulates-- + VIEW CONTROLLER + --sees --> -- uses --> + USER + """ diff --git a/lib/caditem.py b/lib/caditem.py new file mode 100644 index 0000000..3e517e2 --- /dev/null +++ b/lib/caditem.py @@ -0,0 +1,169 @@ +import json +from controller import Controller, Event + + + + + +class CadItem(): + """ + This item contains all data for writing all necessary data to the solid edg cad file + + """ + # diese Daten bilden dann die Impfdaten + data_keys = [ "Author", "Teilenummer", "StartRevision", + "Bezeichnung1", "Bezeichnung2", "Bezeichnung4", "KategorieNE", + "BeschaffungsartNE", "TOS-Gruppe_ERP", "Dispogruppe", + "Dispolaufgruppe", "WerkstofftypNE", "WerkstoffNE", + "Oberflaechenbehandlung", "Farbe", "KalkulationsartNE", + "RuestcodeAusw", "Fertigungsart", "Allgemeintoleranz", + "HalbzeugInternAngabe", "Halbzeug", "HalbzeugOhneSaegeliste", + "EinheitNE", "FOS-GruppeNE", "Hersteller", "HerstellerTeilenummer", + "Serien-Status", "Kantennorm", "Oberflaechennorm", "Massnorm", + "Tolerierungsnorm", "TOS-Gruppe", "TOS_Schoenenberger_Produkt", + "TOS_Automation", "TOS_Maschinenelement", + "TOS_Schoenenberger_Komponenten", "TOS_Pneumatik", "TOS_Konfig", + "TOS_ILS", "TOS_MAS", "TOS_Spinnereikomponente", + "TOS_Betriebsmittel", "TOS_Geruest", "TOS_FremdTransport", + "TOS_MAS_Konfig", "TOS_Schaltschrank", "TOS_Schalt_Einbau", + "TOS_Dezentral", "TOS_Automation_Sensorik", "TOS_Kabel", + "TOS_Installation", "TOS_Automation_Sonstiges", + "TOS_Verbindungselement", "TOS_Lager", "TOS_Kraft_Drehmoment", + "TOS_Sicherungselement", "TOS_Bedienungselement", "TOS_ILS_2", + "TOS_ILS_Foerderer", "TOS_MAS_Fahrstecke", "TOS_Trolley", + "TOS_Trolley_Projekte", "TOS_Trolley_Omniflo", + "TOS_Trolley_Spinnerei", "TOS_Trolley_ILS", "Änderungsbeschreibung", + "Freigabevermerk", "ZusaetzlicheInformationen"] + gui_data_keys = [ "teilenummer", + "pdm_kategorie_neu", + "neuaufbau", + "lebenszyklus_status_neu", + "aus_halbzeug_neu", + "zusatzinfos_text_neu", + "bezeichnung_sivas", "bezeichnung_neu", + "beschaffungsart_sivas","beschaffungsart_neu" + "mengeneinheit_sivas","mengeneinheit_neu" + "halbzeug_sivas","halbzeug_neu", + "tos_gruppe_sivas","tos_gruppe_neu" + # TODO siehe Layout Entwurf svg + ] + + def __init__(self, asm_id): + """ is initalized with its unique id""" + self.set_asm_id(asm_id) + self.red_ids = {} + self.parent_children_mapping = {} + self.data = {} + self.gui_data = {} + self.controller = None + + def set_controller(self, controller): + self.controller = controller + + def set_asm_id(self, asm_id): + self.asm_id = asm_id + def get_current_asm_id(self): + return self.asm_id + + def get_current_part_id(self): + return self.get_gui_data("teilenummer") + + + def set_parent_children(self, parent_children_mapping): + """ contains all id relations below, data of subassemblies etc. in + a dict of the kind parent-children + """ + self.parent_children_mapping = parent_children_mapping + def get_parent_children(self): + return self.parent_children_mapping + + def set_red_ids(self, red_ids): + """ these ids are not yet known by the PDM""" + self.red_ids = red_ids + + def get_red_ids(self): + return self.red_ids + + # Daten zum Impfen merken + def set_data_dict(self, data:dict): + for k,v in data: + self.set_data(k,v) + def set_data(self, key, val): + if key not in self.data_keys: + raise Exception("this data can not be set") + else: + self.data[key] = val + + def get_data(self, key): + """ gibt ein Datenstück zurück """ + return self.data[key] + def get_all_data(self): + """ gibt alle enthaltenen Daten zurück """ + return self.data + + # Daten in der GUI + def set_gui_dict(self, data:dict): + """ setzt die Daten der GUI """ + for k,v in data.items(): + self.set_gui_data(k,v) + def set_gui_data(self, key, val): + """ merkt sich ein Datenatom für die GUI """ + if key not in self.gui_data_keys: + raise Exception("this data can not be set"+ key) + else: + self.gui_data[key] = val + + def get_gui_data(self, key): + return self.gui_data[key] + def get_all_gui_data(self): + return self.gui_data + + def load(): + #try: + # with open('data.json', 'r') as f: + # self.model.todos = json.load(f) + #except Exception: + # pass + pass + def save(): + #with open('data.json', 'w') as f: + # data = json.dump(self.model.data, f) + pass + +class CadItemRepository: + """ + container class for all CadItems in the local folder + also just managed by the controller + """ + def __init__(self): + self.caditems = [] + + def add(self, caditem): + self.caditems.append(caditem) + + def get(self, id): + for caditem in self.caditems: + if caditem.id == id: + return caditem + return None + + def get_all(self): + return self.caditems + + def update(self, old_item, new_item): + self.delete(old_item.id) + self.add(new_item) + raise Exception("CadItem not found") + + def delete(self, id): + for p in self.caditems: + if p.id == id: + self.caditems.remove(p) + return + raise Exception("CadItem not found") + +if __name__ == "__main__": + pass + + + diff --git a/lib/controller.py b/lib/controller.py new file mode 100644 index 0000000..82d61fb --- /dev/null +++ b/lib/controller.py @@ -0,0 +1,91 @@ +""" +Contains the logic for the MVC of CreoMigrate Gui +""" + +class Observer(): + _observers = [] + def __init__(self): + self._observers.append(self) + self._observables = {} + + def observe(self, event_name, callback): + """If the observer is not in the list, + append it into the list""" + if event_name not in self._observables: + self._observables[event_name] = callback + +class Event(): + def __init__(self, name, data, autofire = True): + self.name = name + self.data = data + if autofire: + self.fire() + def fire(self): + for observer in Observer._observers: + if self.name in observer._observables: + observer._observables[self.name](self.data) + +class Controller(Observer): + """ + Class for manipulating the model and returning data for the view + """ + def __init__(self, model, view): + self.set_model(model) + self.set_view(view) + #Observer.__init__(self) + + def set_model(self, model): + self.model = model + def set_view(self, view): + self.view = view + + def refresh_gui(self): + self.view.data_from_model() + def data_from_model(self): + self.view.data_from_model() + + def get_current_asm_id(self): + self.model.get_current_asm_id() + def set_current_asm_id(self, asm_id): + self.model.set_current_asm_id(asm_id) + + def set_red_ids(self, red_ids): + self.model.set_red_ids(red_ids) + def get_red_ids(self): + return self.model.get_red_ids() + + def set_parent_children(self, parent_children_mapping): + self.model.set_parent_children(parent_children_mapping) + def get_parent_children(self): + return self.model.get_parent_children() + + # merkt sich die reinen Daten zum Impfen + def set_data_dict(self, data:dict): + """merken der Daten zum Impfen""" + for k,v in data: + self.set_data(k,v) + def set_data(self, key, value): + """merken der Daten zum Impfen""" + self.model.set_data(key, value) + + def get_data(self, key): + return self.model.get_data(key) + def get_all_data(self): + return self.model.get_all_data() + + # merkt sich die Nutzereingaben, bzw. Daten für die GUI + def set_gui_dict(self, data:dict): + """merken der Eingaben des Nutzers der GUI """ + for k,v in data: + self.set_gui_data(k,v) + def set_gui_data(self, key, value): + """merken der Eingaben des Nutzers der GUI """ + self.model.set_gui_data(key, value) + + def get_gui_data(self, key): + return self.model.get_gui_data(key) + def get_all_gui_data(self): + return self.model.get_all_gui_data() + +if __name__ == "__main__": + pass diff --git a/lib/test.py b/lib/test_configread.py similarity index 100% rename from lib/test.py rename to lib/test_configread.py diff --git a/lib/test_gui.py b/lib/test_gui.py new file mode 100644 index 0000000..77df455 --- /dev/null +++ b/lib/test_gui.py @@ -0,0 +1,94 @@ +import tkinter as tk +from caditem import CadItem +from CreoMigrateGui import App +from controller import Controller + + +red_ids = ( '400102274', '400102275', '400102190', '400102198', + '400102161', '400102277', '400102163', '400102278', + '400102282', '400102284', '400102195', 'MA0000008', + 'MA0000004', '929055170', '400102267', '400102235', + '400102236', 'MA0000006', '400102237', '400102238', + '400102239', '400102268', '829416159', '924031058', + '924031081', '822064091', '822066199', '822066200') + +parent_children_mapping = {'400102196': ['400102125', '400102126', '400102130', + '400102161', '400102163', '400102165', + '400102166', '400102167', '400102168', + '400102190', '400102195', '400102198', + '400102246', '400102247', '400102267', + '400102272', '400102274', '400102275', + '400102277', '400102278', '400102282', + '400102284', '400102417', '720002003', + '721001034', '821114025', '822064091', + '822066085', '822996075', '829416061', + '829516004', '911021113', '911021181', + '911021259', '911091054', '911091096', + '911091097', '911251013', '912011009', + '919050006', '924031058', + '924031081'], + '400102282': ['822066026', '822066089', '919010052'], + '400102195': ['822066075', '822066216', + '822066154', '822066101', '822064090', + '822066196', '400102129', '822066217', + '822066160', '822066215', '913041045', + '913011009', '911011057', '822064035', + '925011006', '911091041', '913051040', + '911021112', '911021166', '825006003', + '912041005', '911011014', '911011009', + '911021144', '911021001', '913011016', + '929055170', '913011010', '911021035', + '829416065', '712000151', + '712000100'], + '822066075': ['822066157', '822066162', '911261023'], + '822066162': ['822064013'], + '822066196': ['MA0000008'], + '822066160': ['MA0000004'], + '822064035': ['822066065'], + '822064093': ['822066204', '822066205', '822066206', '822066207', '822066208'], + '822066220': ['924031033'], + '822064091': ['822066212', '822064093', '822066220', + '822066200', '912011010', '911021156', + '822066201', '911071027', '913011109', + '911011070', '913011001', '710005021', + '919010055', '720000008', '720000007'], + '400102267': ['400102235', '829416159', '829416077', + '829416079', '911021260', '911021147', + '911251014'], + '400102235': ['400102236', '400102237', '400102238', + '400102239', '400102268', '911021143', + '911021054', '911021055'], + '400102236': ['MA0000006'], + '829416159': ['829416089'], + '400102130': ['400102142'], + '821116004': ['822026004'], + '400102168': ['821116001'], + '821114025': ['911081007', '821116053'], + '400102167': ['821116004']} + + + +model = CadItem("400102196") +gui_data = { + "teilenummer":"400102142", + "pdm_kategorie_neu":"Einzelteil", + "neuaufbau" :"False", + "lebenszyklus_status_neu" :"Serie", + "aus_halbzeug_neu" :"True", + "zusatzinfos_text_neu": 'Dies sind neue Zusatzinfos', + } +model.set_gui_dict(gui_data) +model.set_parent_children(parent_children_mapping) +model.set_red_ids(red_ids) + + +root = tk.Tk() +app = App(root) +controller = Controller(model, app) +# um ohne Observer pattern auch Rückmeldungen geben zu können +app.set_controller(controller) +model.set_controller(controller) + +# Daten des Modells in der Oberfläche darstellen +controller.refresh_gui() +app.mainloop()