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, nrow=0): # 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) self.tree.grid(row=nrow, column=0, sticky=tk.NSEW) # add a scrollbar scrollbar = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=self.tree.yview) self.tree.configure(yscroll=scrollbar.set) scrollbar.grid(row=nrow, column=1, sticky='ns') #self.tree.bind('<>', self.item_selected) # Einfachklick self.tree.bind('', self.item_selected) # Doppelklick: 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): parent_children_mapping = self.controller.get_parent_children() red_ids = self.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() class AllFrames(): """ enthält alle gleichen Methoden für die Widgets in der rechte Spalte """ def __init__(self, nrow=0): self.set_start_rwo(nrow) self._controller = None 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 set_controller(self, controller): self._controller = controller def get_controller(self): return self._controller def callback(self, key): controller = self.get_controller() if controller: controller.callback(key) 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 LeftFrameAsm(AllFrames): def __init__(self, frame, nrow=0): AllFrames.__init__(self, nrow) self.frame = frame self.build_widgets() def build_widgets(self): frame = self.frame self.rownum = AllFrames.get_rows(self) col_left = 0 # Baugruppennummer asm_label = ttk.Label(frame, text="Baugruppennummer:" ) asm_label.grid(column=col_left, row=self.rownum, sticky=tk.W, padx=5, pady=5) self.rownum += 1 self.asm_number = tk.StringVar() asm_entry = ttk.Entry(frame, textvariable=self.asm_number, width=9) asm_entry.grid(column=col_left, row=self.rownum, sticky=tk.W, padx=5, pady=5) self.rownum += 1 def get_data(self): return { "asm_id": self.asm_number.get(), } def data_from_model(self): data = self.get_controller().get_all_gui_data() if 'asm_id' in data: self.asm_number.set(data["asm_id"]) class RightFrameLabels(AllFrames): def __init__(self, frame, nrow=0): AllFrames.__init__(self, nrow) self.frame = frame self.build_widgets() def build_widgets(self): frame = self.frame self.rownum = AllFrames.get_rows(self) 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) teilenummer_entry.bind('<>', lambda event:self.callback("teilenummer")) 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): data = self.get_controller().get_all_gui_data() if 'teilenummer' in data: self.teilenummer_text.set(data["teilenummer"]) class RightFramePDM(AllFrames): def __init__(self, frame, nrow=0): AllFrames.__init__(self, nrow) self.frame = frame self.build_widgets() def build_widgets(self): frame = self.frame self.rownum = AllFrames.get_rows(self) 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('<>', lambda event:self.callback('pdm_kategorie_neu')) 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, command=lambda: self.callback("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('<>', lambda event:self.callback('lebenszyklus_status_neu')) 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, command=lambda: self.callback("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): data = self.get_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"]) 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.lf1 = LeftFrameAsm(left_frame, 0) nrows = self.lf1.get_rows() self.lf2 = TreeView(left_frame, nrows) 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.lf1.data_from_model() self.lf2.data_from_model() self.rf0.data_from_model() self.rf1.data_from_model() def set_controller(self, controller): # merken des Handels auf den Controller in jedem widget self._controller = controller self.lf1.set_controller(controller) self.lf2.set_controller(controller) self.rf0.set_controller(controller) self.rf1.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. <--fills gui -- --manipulates--> --user input-> <--give model data-- """