class ExplorerProGui(BanyanBase): """ The Pimoroni Explorer-Pro For Raspberry Pi Demo Station GUI """ def __init__(self, **kwargs): """ Build the main screen as a notebook. """ # initialize the parent class super(ExplorerProGui, self).__init__( back_plane_ip_address=kwargs['back_plane_ip_address'], subscriber_port=kwargs['subscriber_port'], publisher_port=kwargs['publisher_port'], process_name=kwargs['process_name']) self.set_subscriber_topic('report_from_hardware') self.main = Tk() self.main.title('Demo Station For Explorer-Pro HAT') # gives weight to the cells in the grid rows = 0 while rows < 50: self.main.rowconfigure(rows, weight=1) self.main.columnconfigure(rows, weight=1) rows += 1 # Defines and places the notebook widget self.nb = Notebook(self.main, padding=20, height=300, width=800) self.nb.grid(row=1, column=0, columnspan=50, rowspan=49, sticky='NESW') # create the analog inputs tab self.analog_inputs_tab = Frame(self.nb, padding=[130, 20]) self.nb.add(self.analog_inputs_tab, text='Analog Inputs') # create an instance of the AnalogInputs class self.analog_inputs = AnalogInputs(self.analog_inputs_tab, self) # create the digital inputs tab self.digital_inputs_tab = Frame(self.nb, padding=[130, 20]) self.nb.add(self.digital_inputs_tab, text='Digital Inputs') # create an instance of the DigitalInputs class # to populate and control Signal inputs self.digital_inputs = DigitalInputs(self.digital_inputs_tab, self) # create the touch tab self.touch_tab = Frame(self.nb, padding=10) self.nb.add(self.touch_tab, text='Touch Inputs') self.touch_inputs = TouchInputs(self.touch_tab, self) # create the digital outputs tab self.digital_outputs_tab = Frame(self.nb, padding=10) self.nb.add(self.digital_outputs_tab, text='Digital Outputs') # create an instance of the DigitalOutputs class # to populate and control digital outputs self.digital_outputs = DigitalOutputs(self.digital_outputs_tab, self) # create the LED digital outputs tab self.led_digital_outputs_tab = Frame(self.nb, padding=10) self.nb.add(self.led_digital_outputs_tab, text='LED Digital Outputs') # create an instance of the DigitalOutputs class # to populate and control digital outputs self.led_digital_outputs = LedDigitalOutputs(self.led_digital_outputs_tab, self) # create the pwm output tab self.pwm_output_tab = Frame(self.nb, padding=10) self.nb.add(self.pwm_output_tab, text='PWM Outputs') self.pwm_output = PwmOutputs(self.pwm_output_tab, self) # create the LED PWM output tab self.led_pwm_output_tab = Frame(self.nb, padding=10) self.nb.add(self.led_pwm_output_tab, text='LED PWM Outputs') self.led_pwm_output = LedPwmOutputs(self.led_pwm_output_tab, self) # create the dc motors tab self.motors_tab = Frame(self.nb, padding=10) self.nb.add(self.motors_tab, text='DC Motors') self.dc_motors = DcMotors(self.motors_tab, self) l = Label(self.main, text='Copyright (c) 2019 Alan Yorinks All Rights Reserved.') l.config(font="Helvetica 8 ") l.grid(row=48, column=0, padx=[505, 0]) self.main.after(5, self.get_message) try: self.main.mainloop() except KeyboardInterrupt: self.on_closing() def notebook_tab_selection(self): print(self.nb.tab(self.nb.select(), "text")) print(self.nb.index(self.nb.select())) # noinspection DuplicatedCode def get_message(self): """ This method is called from the tkevent loop "after" method. It will poll for new zeromq messages within the tkinter event loop. """ try: data = self.subscriber.recv_multipart(zmq.NOBLOCK) self.incoming_message_processing(data[0].decode(), msgpack.unpackb(data[1], raw=False)) self.main.after(1, self.get_message) except zmq.error.Again: try: self.main.after(1, self.get_message) except KeyboardInterrupt: self.main.destroy() self.publisher.close() self.subscriber.close() self.context.term() sys.exit(0) except KeyboardInterrupt: self.main.destroy() self.publisher.close() self.subscriber.close() self.context.term() sys.exit(0) def incoming_message_processing(self, topic, payload): """ This method processes the incoming pin state change messages for GPIO pins set as inputs. :param topic: :param payload: Typical report: {'report': 'digital_input', 'pin': pin, 'value': level, 'timestamp': time.time()} """ # if the pin currently input active, process the state change pin = payload['pin'] - 1 value = payload['value'] timestamp = payload['timestamp'] report_type = payload['report'] if report_type == 'analog_input': if 0 <= pin <= 3: self.analog_inputs.set_input_value(pin, value) self.analog_inputs.set_time_stamp_value(pin, timestamp) else: raise RuntimeError('analog pin out of range: ', pin) elif report_type == 'digital_input': if 0 <= pin <= 3: self.digital_inputs.set_input_value(pin, value) self.digital_inputs.set_time_stamp_value(pin, timestamp) else: raise RuntimeError('digital pin out of range') elif report_type == 'touch': if 0 <= pin <= 8: self.touch_inputs.set_input_value(pin, value) self.touch_inputs.set_time_stamp_value(pin, timestamp) else: raise RuntimeError('touch pin out of range') else: raise RuntimeError('Unknown report type: ', payload['report']) def on_closing(self): """ Destroy the window """ self.clean_up() self.main.destroy()
class App: # need it for generate reports __ast = None __sym_table = None __sym_table_3d = None def __init__(self, ide): # setting title ide.title("TenorC @danii_mor") # setting window size width=700 height=400 screenwidth = ide.winfo_screenwidth() screenheight = ide.winfo_screenheight() alignstr = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2) ide.geometry(alignstr) ide.resizable(width=True, height=True) # create menubar menubar = Menu(ide) # file menu filemenu = Menu(menubar, tearoff=0) filemenu.add_command(label="New", command=self.newFile) filemenu.add_command(label="Open", command=self.file_open) filemenu.add_command(label="Save", command=self.file_save) filemenu.add_command(label="Save as...", command=self.file_save_as) filemenu.add_command(label="Close", command=self.exitTab) filemenu.add_separator() filemenu.add_command(label="Exit", command=ide.quit) # edit menu editmenu = Menu(menubar, tearoff=0) editmenu.add_command(label="Cut", command=self.donothing) editmenu.add_command(label="Copy", command=self.copy_to_clipboard) editmenu.add_command(label="Paste", command=self.donothing) editmenu.add_separator() editmenu.add_command(label="Find", command=self.donothing) editmenu.add_command(label="Replace", command=self.donothing) # run menu runmenu = Menu(menubar, tearoff=0) runmenu.add_command(label="Execute Analysis", command=self.execute_current_tab_lef) runmenu.add_command(label="Show Intermediate Code", command=self.show3D) runmenu.add_separator() runmenu.add_command(label="Symbol Table", command=self.show_sym_table) runmenu.add_command(label="Error Report", command=self.show_error) runmenu.add_command(label="Abstract Syntax Tree", command=self.show_ast) runmenu.add_command(label="Grammar", command=self.show_grammar) runmenu.add_separator() runmenu.add_command(label="Debugging", command=self.execute_debug) # option menu #optionmenu = Menu(menubar, tearoff=0) #optionmenu.add_command(label="Theme...", command=self.donothing) #optionmenu.add_command(label="Line Numbers...", command=self.donothing) # help menu helpmenu = Menu(menubar, tearoff=0) helpmenu.add_command(label="Help", command=self.donothing) helpmenu.add_command(label="About...", command=self.show_info) # setting menu menubar.add_cascade(label="File", menu=filemenu) menubar.add_cascade(label="Edit", menu=editmenu) menubar.add_cascade(label="Run", menu=runmenu) menubar.add_cascade(label="Help", menu=helpmenu) ide.config(menu=menubar) # setting editor area self.tabs = Notebook(ide) f1 = Frame(self.tabs) self.tabs.add(f1, text="+") self.tabs.pack(side="top", fill="both", expand=True, padx=10, pady=0) self.tabs.bind("<<NotebookTabChanged>>", self.addTab) # setting terminal area self.terminal= Text(ide) ft = tkFont.Font(family="Lucinda Console", size=10) self.terminal["font"] = ft self.terminal["wrap"] = "word" self.terminal["fg"] = "white" self.terminal["bg"] = "black" self.terminal["insertbackground"] ="white" self.terminal["height"] = 5 self.terminal["width"] = 5 self.terminal.pack( side = "left", fill = "both", expand=True, padx=10, pady=10) terminal_scroll = Scrollbar(ide) terminal_scroll["orient"] = "vertical" terminal_scroll["command"] = self.terminal.yview terminal_scroll.pack(side="right", fill="y") self.terminal.configure(yscrollcommand=terminal_scroll.set) self.terminal.bind("<Return>", self.execute_command) def copy_to_clipboard(self): selectedTab = self.tabs.index("current") currentTextArea = self.tabs.winfo_children()[selectedTab+1].textarea try: selected_text= currentTextArea.get("sel.first", "sel.last") currentTextArea.clipboard_append(selected_text) except: pass def show_grammar(self): if self.__sym_table: window = Toplevel() window['bg'] = 'black' productions = self.__sym_table.getGrammar() keys = list(productions.keys()) keys.sort() grammar = Message(window) txt = '' for production in keys: txt += productions[production] + '\n' grammar['fg'] = 'white' grammar['bg'] = 'black' grammar['text'] = txt grammar.pack(side='left') def show_error(self): if self.__sym_table: if self.__sym_table.error != '': window = Toplevel() window['bg'] = 'black' grammar = Message(window) grammar['fg'] = 'white' grammar['bg'] = 'black' grammar['text'] = self.__sym_table.error grammar.pack(side='left') else: window = Toplevel() window['bg'] = 'black' grammar = Message(window) grammar['fg'] = 'white' grammar['bg'] = 'black' grammar['text'] = 'Not Errors Found' grammar.pack(side='left') # TODO fix it def show_sym_table(self): if self.__sym_table: showTable(self.__sym_table) def show_ast(self): self.__ast.graph() showAST() codeGenerated = None def show3D(self): if self.codeGenerated != None: window = Toplevel() window['bg'] = 'black' grammar = Text(window) grammar['fg'] = 'white' grammar['bg'] = 'black' grammar.insert(1.0, self.codeGenerated) grammar.pack(side='left') def show_info(self): window = Toplevel() window['bg'] = 'black' grammar = Message(window) grammar['fg'] = 'white' grammar['bg'] = 'black' grammar['text'] = 'Augus intermediate code by Engr. Espino\nTenorC 1.23.2a Developed by @danii_mor\n 201314810' grammar.pack(side='left') def update_line_debugg(self, event= None): self.count["text"] = "Line: %s" % str(self.c+1) lines = self.codeGenerated.split('\n') # start execute line by self.c counter ply_left_3d = titus.parse() if self.c < len(lines): if "main:" not in lines[self.c]: line = "main:" + lines[self.c] result = ply_left_3d(titus, line) if result: ast = result[0] ast.setType("LABEL") ast.setValue("S") ast.root = result[0] if self.__sym_table_3d != None: new_table = {**self.__sym_table_3d.printTable(), **result[1].printTable()} for sym_id in new_table: sym = new_table[sym_id] if sym != None: if type(sym) == dict: continue if sym.getValue() == None: try: new_table[sym_id] = self.__sym_table_3d.printTable()[sym_id] except: pass self.__sym_table_3d.setTable({**self.__sym_table_3d.printTable(), **new_table}) else: self.__sym_table_3d = result[1] # define mode for syntax-tree know how to autoexecute self.__sym_table_3d.setMode(1) compute = [None, None] # start execute self.__sym_table_3d.terminal = self.terminal compute = ast.start_execute(self.__sym_table_3d, "MAIN") # lookup the last line index = self.terminal.search(r'\n', "insert", backwards=True, regexp=True) txt = self.terminal.get(str(index),'end-1c') if txt == "": index ="1.0" else: index = self.terminal.index("%s+1c" % index) if compute[0]: self.terminal.insert(str(float(index)+1), compute[0]) self.__sym_table_3d.cleanLog() if compute[1]: goto_line = 0 for l in lines: if (compute[1]+":") in l: break goto_line = goto_line + 1 self.c = goto_line - 1 if self.__sym_table != None: if self.__sym_table.error != '': # lookup the last line index = self.terminal.search(r'\n', "insert", backwards=True, regexp=True) txt = self.terminal.get(str(index),'end-1c') if txt == "": index ="1.0" else: index = self.terminal.index("%s+1c" % index) self.terminal.insert(str(float(index)+1), "\nTenorC>> Error Report Generated\n") self.c = self.c + 1 self.label_last_line["text"] = "Line: %s" % str(self.c+1) c = 0 def execute_debug(self, event = None): self.__sym_table_3d = None self.c = 0 # create debug player window = Toplevel() window['bg'] = 'black' label_count = Label(window, text="Execute Now:", borderwidth=0, width=10, bg = "black", fg = "white") label_count.grid(row=0, column=0, sticky="nsew", padx=1, pady=1) label_last = Label(window, text="Executed Before:", borderwidth=0, width=10, bg = "black", fg = "white") label_last.grid(row=0, column=2, sticky="nsew", padx=1, pady=1) self.label_last_line = Label(window, text="Line: 1", borderwidth=0, width=10, bg = "black", fg = "white") self.label_last_line.grid(row=1, column=0, sticky="nsew", padx=1, pady=1) execute = Button(window, text='>', command=self.update_line_debugg) execute.grid(row=1, column=1, sticky="nsew", padx=1, pady=1) self.count = Label(window, text="Line: 0", borderwidth=0, width=10, bg = "black", fg = "white") self.count.grid(row=1, column=2, sticky="nsew", padx=1, pady=1) window.grid_columnconfigure(0, weight=1) window.grid_columnconfigure(1, weight=1) window.grid_columnconfigure(2, weight=1) window.resizable(width=True, height=False) # get all txt from current tab selectedTab = self.tabs.index("current") currentTextArea = self.tabs.winfo_children()[selectedTab+1].textarea input = currentTextArea.get('1.0','end-1c') # new singleton symbol table self.__sym_table = table() # define mode for syntax-tree know how to autoexecute self.__sym_table.setMode(0) # start lex and sintactic analysis ply_left = tenorC.parse() self.__ast = ply_left(tenorC, input, self.__sym_table) # TODO sintax error recover if self.__ast != None: self.__ast.execute(self.__sym_table) self.codeGenerated = self.__ast.get3D() def execute_current_tab_lef(self): # get all txt from current tab selectedTab = self.tabs.index("current") currentTextArea = self.tabs.winfo_children()[selectedTab+1].textarea input = currentTextArea.get('1.0','end-1c') # new singleton symbol table self.__sym_table = table() # define mode for syntax-tree know how to autoexecute self.__sym_table.setMode(0) # start lex and sintactic analysis ply_left = tenorC.parse() self.__ast = ply_left(tenorC, input, self.__sym_table) # TODO sintax error recover if self.__ast != None: self.__ast.execute(self.__sym_table) self.codeGenerated = self.__ast.get3D() ## start executing ply_left_3d = titus.parse() result = ply_left_3d(titus, self.codeGenerated) if result: ast_3D = result[0] ast_3D.setType("LABEL") ast_3D.setValue("S") ast_3D.root = True self.__sym_table_3d = result[1] # define mode for syntax-tree know how to autoexecute self.__sym_table_3d.setMode(1) goto_called = True start_from = "MAIN" compute = [None, None] while goto_called: goto_called = False self.__sym_table_3d.terminal = self.terminal compute = ast_3D.start_execute(self.__sym_table_3d, start_from) # lookup the last line index = self.terminal.search(r'\n', "insert", backwards=True, regexp=True) txt = self.terminal.get(str(index),'end-1c') if txt == "": index ="1.0" else: index = self.terminal.index("%s+1c" % index) if compute[0]: self.terminal.insert(str(float(index)+1), compute[0]) self.__sym_table_3d.cleanLog() if compute[1]: goto_called = True start_from = compute[1] elif self.__sym_table.error != '': # lookup the last line index = self.terminal.search(r'\n', "insert", backwards=True, regexp=True) txt = self.terminal.get(str(index),'end-1c') if txt == "": index ="1.0" else: index = self.terminal.index("%s+1c" % index) self.terminal.insert(str(float(index)+1), "\nTenorC>> Error Report Generated\n") def execute_command(self, event): # lookup the last line index = self.terminal.search(r'\n', "insert", backwards=True, regexp=True) input = self.terminal.get(str(index),'end-1c') if input == "": index ="1.0" else: index = self.terminal.index("%s+1c" % index) input = self.terminal.get(index,'end-1c') # send the input to the calculate self.__sym_table_3d.read_input.set(input) def newFile(self): lastindex = self.tabs.index("end")-1 textarea = Editor(self.tabs) self.tabs.insert(lastindex, textarea, text="Tab" + str(lastindex+1)) self.tabs.select(lastindex) def exitTab(self): result = self.save_if_modified() if result != None: #None => Aborted or Save cancelled, False => Discarded, True = Saved or Not modified selectedTab = self.tabs.index("current") currentTab = self.tabs.winfo_children()[selectedTab+1] self.tabs.select(self.tabs.winfo_children()[selectedTab]) currentTab.destroy() def save_if_modified(self, event=None): selectedTab = self.tabs.index("current") currentTextArea = self.tabs.winfo_children()[selectedTab+1].textarea if currentTextArea.edit_modified(): #modified response = messagebox.askyesnocancel("Save?", "This document has been modified. Do you want to save changes?") #yes = True, no = False, cancel = None if response: #yes/save result = self.file_save() if result == "saved": #saved return True else: #save cancelled return None else: return response #None = cancel/abort, False = no/discard else: #not modified return True def file_open(self, event=None, filepath=None): if filepath == None: filepath = filedialog.askopenfilename() if filepath != None and filepath != '': with open(filepath, encoding="utf-8") as f: fileContents = f.read()# Get all the text from file. # Set current text to a new Tab file contents lastindex = self.tabs.index("end")-1 textarea = Editor(self.tabs) self.tabs.insert(lastindex, textarea, text="Tab" + str(lastindex+1)) self.tabs.select(lastindex) textarea.textarea.insert(1.0, fileContents) textarea.textarea.edit_modified(False) tab_tittle = os.path.basename(filepath) self.tabs.tab(lastindex, text = tab_tittle) def file_save(self, event=None): selectedTab = self.tabs.index("current") currentName = self.tabs.tab(selectedTab, "text") if 'Tab' in currentName: result = self.file_save_as() else: result = self.file_save_as(filepath='./' + currentName) return result def file_save_as(self, event=None, filepath=None): if filepath == None: filepath = filedialog.asksaveasfilename(filetypes=(('Text files', '*.txt'), ('C files', '*.mc'), ('All files', '*.*'))) #defaultextension='.txt' try: with open(filepath, 'wb') as f: selectedTab = self.tabs.index("current") currentTextArea = self.tabs.winfo_children()[selectedTab+1].textarea text = currentTextArea.get(1.0, "end-1c") f.write(bytes(text, 'UTF-8')) currentTextArea.edit_modified(False) tab_tittle = os.path.basename(filepath) self.tabs.tab(selectedTab, text = tab_tittle) return "saved" except FileNotFoundError: print('TenorC>> File Not Found Error') return "cancelled" def addTab(self, event): selectedTab = self.tabs.index("current") lastindex = self.tabs.index("end")-1 if selectedTab == lastindex : textarea = Editor(self.tabs) self.tabs.insert(lastindex, textarea, text="Tab" + str(lastindex+1)) self.tabs.select(lastindex) def donothing(self): print("clicked")
class Main(object): def __init__(self, title): root = Tk() root.title(title) root.focus_set() root.rowconfigure(0, weight=0) root.columnconfigure(0, weight=1) root.rowconfigure(1, weight=1) self._root = root self.menubar = Frame(root) self.menubar.grid(row=0, column=0, sticky=(W, E)) self.menubar['takefocus'] = False quit_button = Button(self.menubar, text='Quit', command=self.quit) quit_button.grid(row=0, column=0) self._menucolumn = 1 self.views = list() self.paned_win = PanedWindow(root, orient=HORIZONTAL) self.paned_win.grid(row=1, column=0, sticky=(N, S, W, E)) self._query = None self._accept_func = None self.sidebar_views = dict() self.sidebar_count = 0 self.sidebar = PanedWindow(self.paned_win) self.paned_win.add(self.sidebar, weight=1) self.tabs = Notebook(self.paned_win) self.tabs.enable_traversal() self.paned_win.add(self.tabs, weight=5) self.root = self.tabs def add_menubutton(self, label, action): button = Button(self.menubar, text=label, command=action) button.grid(row=0, column=self._menucolumn) self._menucolumn += 1 def add_sidebar(self, view, name): self.sidebar_views[name] = view self.sidebar.add(view.widget, weight=1) view.widget.focus_set() self.sidebar_count += 1 if self.sidebar_count == 1: self.sidebar_views['main'] = view def remove_sidebar_view(self, name): self.sidebar.forget(self.sidebar_views[name].widget) self.sidebar_count -= 1 del self.sidebar_views[name] if self.sidebar_count == 0: del self.sidebar_views['main'] def get_sidebar_view(self, name): return self.sidebar_views.get(name) def focus_sidebar(self): if 'main' in self.sidebar_views.keys(): self.sidebar_views['main'].widget.focus_set() def focus_main_view(self): self.get_current_view().widget.focus_set() def new_view(self, view): self.views.append(view) self.tabs.add(view.widget, text=" {}.".format(self.tabs.index('end'))) self.tabs.select(view.widget) view.widget.focus_set() self.view_changed() def remove_view(self, view): self.views.remove(view) self.tabs.forget(view.widget) if len(self.views) >= 1: widget = self.views[-1].widget self.tabs.select(widget) widget.focus_set() else: self.sidebar_views['main'].widget.focus_set() self.view_changed() def delete_current_view(self, event): if self.tabs.index('end') > 0: self.remove_view(self.get_current_view()) def close_query(self): if self._query is not None: self._query.event_generate('<<MainQueryClose>>') self._query.destroy() self._query = None self._accept_func = None self._menucolumn -= 1 def accept_query(self, event): if self._query is not None: if self._accept_func is not None: self._accept_func(event.widget.get(), event.widget.original_value) self.close_query() else: event.widget.event_generate('<<MainQueryAccept>>') def text_query(self, query_lable, original_text=None, accept_func=None): if self._query is not None: return frame = Frame(self.menubar) label = Label(frame, text=query_lable) label.grid(column=0, row=0, sticky=(N, S)) self._accept_func = accept_func entry = Entry(frame) if original_text is not None: entry.insert(0, original_text) entry.original_value = original_text entry.grid(column=1, row=0, sticky=(N, S, W, E)) kb.make_bindings(kb.text_query, { 'accept': self.accept_query, 'cancel': lambda e: self.close_query() }, entry.bind) frame.grid(column=self._menucolumn, row=0) self._menucolumn += 1 entry.focus_set() self._query = frame def get_current_view(self): if self.tabs.index('end') > 0: return self.views[self.tabs.index('current')] else: return self.sidebar_views['main'] def view_changed(self): self._root.event_generate('<<MainViewChanged>>') def display(self): self._root.mainloop() def quit(self): self._root.destroy()
class GuiMain: def __init__(self): """ Main Gui Entrance """ tkinter.Tk.report_callback_exception = self.throw # Main window self.destroyed = False LoggerGui.info("Initializing GUI") self.main_window = tkinter.Tk() self.main_window.wm_title("DRC Sim Server") icon = tkinter.PhotoImage(data=Resource("image/icon.gif").resource) self.main_window.tk.call("wm", "iconphoto", self.main_window, icon) self.main_window.protocol("WM_DELETE_WINDOW", self.on_closing) self.main_window.resizable(False, False) # Notebook self.tab_id = None self.notebook = Notebook(self.main_window, width=600, height=300) self.notebook.grid(column=0, row=0) self.notebook.bind("<<NotebookTabChanged>>", self.on_tab_changed) # Run Server Frame self.frame_run_server = FrameRunServer(self.notebook) self.notebook.add(self.frame_run_server, text="Run Server") # Get Key Frame self.frame_get_key = FrameGetKey(self.notebook) self.notebook.add(self.frame_get_key, text="Get Key") # Log Frame self.frame_log = FrameLog(self.notebook) self.notebook.add(self.frame_log, text="Log") # About Frame self.frame_about = FrameAbout(self.notebook) self.notebook.add(self.frame_about, text="About") @staticmethod def throw(*args): """ Throw exceptions from Tkinter :param args: arguments :return: None """ for arg in args: if isinstance(arg, Exception): LoggerGui.throw(arg) def after(self): """ Empty loop to catch KeyboardInterrupt :return: None """ self.main_window.after(1000, self.after) def start(self): """ Start the main window loop :return: """ LoggerGui.info("Opening GUI") self.after() self.main_window.mainloop() LoggerGui.info("GUI Closed") def stop(self): """ Convenience function to call on_closing() :return: None """ self.on_closing() def on_closing(self): """ Close the main window and current tab :return: None """ if self.destroyed: return self.destroyed = True LoggerGui.info("Closing GUI") if self.tab_id in self.notebook.children: self.notebook.children[self.tab_id].deactivate() try: self.main_window.destroy() except Exception as e: LoggerGui.exception(e) # noinspection PyUnusedLocal def on_tab_changed(self, event): """ Close the previous tab and initialize a new one :param event: tab event :return: None """ tab_id = self.notebook.select() tab_index = self.notebook.index(tab_id) tab_name = self.notebook.tab(tab_index, "text") LoggerGui.debug("Notebook tab changed to \"%s\" with id %d", tab_name, tab_index) self.tab_id = tab_id.split(".")[len( tab_id.split(".")) - 1] # Parse notebook/tab id to only tab id if self.notebook.children[self.tab_id].kill_other_tabs(): for tab in self.notebook.children: if tab != self.tab_id: self.notebook.children[tab].deactivate() self.notebook.children[self.tab_id].activate()
class App: def __init__(self): # создание основного окна Tkinter self.window = Tk() self.window.title("Треугольники") self.window.geometry('520x520') self.window.minsize(520, 520) self.window.maxsize(520, 520) self.press_x, self.press_y = 0, 0 self.release_x, self.release_y = 0, 0 # флаги нажатия/отжатия кнопки self.pressed = False # размещение элементов интерфейса # основной элемент - Canvas self.canvas_x = 470 self.canvas_y = 270 self.c = Canvas(self.window, width=self.canvas_x, height=self.canvas_y, bg='black') self.c.pack(side=TOP, padx=10, pady=(10, 10)) # элементы управления: настройки, отображение параметров, очистка/выход setup = Frame(self.window, width=470) setup_left = Frame(setup, width=270) setup_right = Frame(setup, width=200) self.setup_notebook = Notebook(setup_left) setup1 = Frame(setup_left, width=250, height=140) setup2 = Frame(setup_left, width=250, height=140) setup3 = Frame(setup_left, width=250, height=140) # элементы управления на вкладках # вкладка 1 Label(setup1, text='Длина стороны').pack(side=TOP, pady=(10, 0), padx=(10, 0), anchor=W) self.scal_b = Scale(setup1, orient=HORIZONTAL, length=200, from_=0, to=300, tickinterval=100, resolution=10) self.scal_b.pack(side=TOP) self.scal_b.set(100) self.scal_b.bind("<ButtonRelease-1>", self.draw_triangle) Label(setup1, text='Угол \u03b1').pack(side=TOP, pady=(10, 0), padx=(10, 0), anchor=W) self.scal_alpha = Scale(setup1, orient=HORIZONTAL, length=200, from_=0, to=180, tickinterval=30, resolution=15) self.scal_alpha.pack(side=TOP) self.scal_alpha.set(30) self.scal_alpha.bind("<ButtonRelease-1>", self.draw_triangle) # вкладка 2 Label(setup2, text='Угол \u03b1').pack(side=TOP, pady=(10, 0), padx=(10, 0), anchor=W) self.scal_alpha2 = Scale(setup2, orient=HORIZONTAL, length=200, from_=0, to=90, tickinterval=15, resolution=5) self.scal_alpha2.pack(side=TOP) self.scal_alpha2.set(60) self.scal_alpha2.bind("<ButtonRelease-1>", self.draw_triangle) Label(setup2, text='Угол \u03b2').pack(side=TOP, pady=(10, 0), padx=(10, 0), anchor=W) self.scal_beta = Scale(setup2, orient=HORIZONTAL, length=200, from_=0, to=90, tickinterval=15, resolution=5) self.scal_beta.pack(side=TOP) self.scal_beta.set(60) self.scal_beta.bind("<ButtonRelease-1>", self.draw_triangle) # вкладка 3 Label(setup3, text='Длина стороны 2').pack(side=TOP, pady=(10, 0), padx=(10, 0), anchor=W) self.scal_a = Scale(setup3, orient=HORIZONTAL, length=200, from_=0, to=300, tickinterval=100, resolution=10) self.scal_a.pack(side=TOP) self.scal_a.set(100) self.scal_a.bind("<ButtonRelease-1>", self.draw_triangle) Label(setup3, text='Длина стороны 3').pack(side=TOP, pady=(10, 0), padx=(10, 0), anchor=W) self.scal_b2 = Scale(setup3, orient=HORIZONTAL, length=200, from_=0, to=300, tickinterval=100, resolution=10) self.scal_b2.pack(side=TOP) self.scal_b2.set(100) self.scal_b2.bind("<ButtonRelease-1>", self.draw_triangle) setup1.pack() setup2.pack() setup3.pack() self.setup_notebook.add(setup1, text='Задача 1') self.setup_notebook.add(setup2, text='Задача 2') self.setup_notebook.add(setup3, text='Задача 3') self.setup_notebook.bind('<<NotebookTabChanged>>', self.draw_triangle) self.setup_notebook.pack(side=LEFT) columns = ('#1', '#2') self.params = Treeview(setup_right, show='headings', columns=columns, height=5) self.params.heading('#1', text='Параметр') self.params.heading('#2', text='Значение') self.params.column('#1', width=110, minwidth=50, stretch=NO, anchor=N) self.params.column('#2', width=110, minwidth=50, stretch=NO, anchor=N) self.params.pack(side=TOP, padx=(15, 0), pady=(15, 5)) butframe = Frame(setup_right) Button(butframe, text='Очистить', width=10, command=self.draw_base()).pack(side=LEFT, padx=(25, 5)) Button(butframe, text='Выход', width=10, command=lambda x=0: sys.exit(x)).pack(side=LEFT, padx=(0, 10)) butframe.pack(side=TOP, pady=(17, 5)) setup_left.pack(side=LEFT, padx=(0, 20)) setup_right.pack(side=LEFT) setup.pack(side=TOP, pady=(5, 10), padx=(5, 5)) self.window.bind('<Button-1>', self.press) self.window.bind('<ButtonRelease-1>', self.release) self.window.bind('<Motion>', self.motion) self.draw_base() self.window.mainloop() def motion(self, event): if self.pressed: if event.widget.master is not None: if event.widget.widgetName == 'canvas': self.draw_base() self.c.create_line( [self.press_x, self.press_y, event.x, event.y], dash=True, fill='yellow') def draw_base(self): self.c.delete("all") # базовые оси self.c.create_line([10, 250, 460, 250], arrow=LAST, fill='white') self.c.create_line([20, 260, 20, 10], arrow=LAST, fill='white') # метки for i in range(1, 5): self.c.create_line([100 * i + 20, 247, 100 * i + 20, 253], fill='white') for i in range(1, 3): self.c.create_line([17, 250 - 100 * i, 23, 250 - 100 * i], fill='white') # надписи # наименование осей self.c.create_text(457, 258, text='x', fill='white') self.c.create_text(30, 15, text='y', fill='white') # наименование меток for i in range(0, 5): self.c.create_text(100 * i + 25, 260, text=str(100 * i), fill='white') for i in range(1, 3): self.c.create_text(34, 250 - 100 * i, text=str(100 * i), fill='white') def press(self, event): """ Обработчик события "нажатие кнопки мыши" :param event: :return: """ if event.widget.master is not None: if event.widget.widgetName == 'canvas': self.draw_base() self.press_x, self.press_y = event.x, event.y self.pressed = True def release(self, event): if event.widget.master is not None: if event.widget.widgetName == 'canvas': self.release_x, self.release_y = event.x, event.y if (self.release_x in range(450)) & (self.release_y in range(250)): self.draw_triangle(None) self.pressed = False def check_coordinates(self, C): if (C[0] < 0) | (C[1] < 0): return False if (C[0] > self.canvas_x) | (C[1] > self.canvas_y): return False return True def draw_triangle(self, event): if (self.press_x > 0) & (self.press_y > 0) & (self.release_x > 0) & ( self.release_y > 0): self.draw_base() triangle = Math((self.press_x, self.press_y), (self.release_x, self.release_y), (20, 250)) task = self.setup_notebook.index(self.setup_notebook.select()) + 1 if task == 1: data_dict = { 'b': self.scal_b.get(), 'alpha': self.scal_alpha.get() } elif task == 2: data_dict = { 'alpha': self.scal_alpha2.get(), 'beta': self.scal_beta.get() } elif task == 3: data_dict = {'a': self.scal_a.get(), 'b': self.scal_b2.get()} else: return C1, C2, data_dict = triangle.get_c(task, data_dict) if self.check_coordinates(C1) & self.check_coordinates(C2): self.c.create_polygon([ self.press_x, self.press_y, self.release_x, self.release_y, C1[0], C1[1] ], fill='red') self.c.create_polygon([ self.press_x, self.press_y, self.release_x, self.release_y, C2[0], C2[1] ], fill='blue') self.update_treeview(data_dict) else: self.c.create_text(300, 100, text='Одна из точек вне области построения', fill='white') def update_treeview(self, data): """ запись параметров в элемент Treeview :return: None """ for x in self.params.get_children(): self.params.delete(x) for key in data.keys(): self.params.insert("", END, values=[key, data[key]])
class Root(ThemedTk): def __init__(self): ThemedTk.__init__(self) if platform.system() is 'Windows': self.set_theme("vista") else: self.set_theme("clearlooks") self.set_title() img = ImageTk.PhotoImage(file=Path(__file__).resolve().parent / 'images' / 'icon.png') self.iconphoto(True, img) self.menu_bar = MenuBar(self) self.config(menu=self.menu_bar) self.rowconfigure(0, weight=1) self.columnconfigure(1, weight=1) self.minutiae = [] self.file_path = Path() self.image_raw = Image.new('RGBA', (512, 512), (255, 255, 255, 255)) self.image_fingerprint = self.image_raw self.image_minutiae = None self.image = ImageTk.PhotoImage(self.image_raw) self.image_canvas = Canvas(self, bd=0, highlightthickness=0) self.image_canvas.create_image(0, 0, image=self.image, anchor=N + W, tags="IMG") self.image_canvas.grid(row=0, column=1, sticky=NSEW) self.notebook = Notebook(self) self.notebook.grid(row=0, column=0, sticky=NSEW) self.tabs = [ MindtctFrame(self, self.load_fingerprint_image), MinutiaeEditorFrame(self, self.load_fingerprint_image, self.load_minutiae_file, self.save_minutiae_file) ] self.notebook.add(self.tabs[0], text="MINDTCT") self.notebook.add(self.tabs[1], text="Minutiae Editor") self.image_canvas.bind("<Button-1>", self.on_canvas_mouse_left_click) self.image_canvas.bind("<Control-Button-1>", self.on_canvas_ctrl_mouse_left_click) self.image_canvas.bind("<B1-Motion>", self.on_canvas_mouse_left_drag) self.image_canvas.bind("<ButtonRelease-1>", self.on_canvas_mouse_left_release) self.image_canvas.bind("<Button-3>", self.on_canvas_mouse_right_click) self.bind("<Configure>", self.redraw) def set_title(self, title: str = None): """ Sets the main window's title. If a string is provided then the title will be set to "[string] - [programme name]". If None is supplied just the programme name is displayed. :param title: The text to set as the title. """ text = "" if title is None else "{0} - ".format(title) self.title(text + "Py Minutiae Viewer") def redraw(self, _=None): im = self.image_raw.copy() # Apply drawing from each active tab for tab in self.tabs: im = tab.fingerprint_drawing(im) # Scale image to fit canvas im, _ = scale_image_to_fit_minutiae_canvas(self.image_canvas, im) self.image_fingerprint = im self.image = ImageTk.PhotoImage(self.image_fingerprint) self.image_canvas.delete("IMG") self.image_canvas.create_image(0, 0, image=self.image, anchor=N + W, tags="IMG") # Draw minutiae self.draw_minutiae() def load_fingerprint_image(self): file_path = askopenfilename(filetypes=(("Image files", ('*.bmp', '*.jpeg', '*.jpg', '*.png')), ("All files", "*.*"))) if file_path: self.image_raw = Image.open(file_path).convert("RGBA") self.redraw() self.file_path = Path(file_path).resolve() self.set_title(self.file_path.name) self.minutiae = [] self.update_idletasks() self.tabs[self.notebook.index("current")].load_fingerprint_image(self.image_raw) def load_minutiae_file(self): file_path = askopenfilename(initialfile=self.file_path.stem, filetypes=(("All minutiae files", ('*.min', '*.sim', '*.xyt')), ("Simple minutiae file", '*.sim'), ("NBIST minutiae file", '*.min'), ("x y theta file", '*.xyt'), ("All files", "*.*"))) if file_path: # Select the correct file format if Path(file_path).suffix == '.sim': reader = MinutiaeReader(MinutiaeFileFormat.SIMPLE) elif Path(file_path).suffix == '.min': reader = MinutiaeReader(MinutiaeFileFormat.NBIST) elif Path(file_path).suffix == '.xyt': reader = MinutiaeReader(MinutiaeFileFormat.XYT) else: showerror("Read Minutiae File", "The chosen file had an extension of '{}', which can't be interpreted." .format(Path(file_path).suffix)) return try: self.minutiae = reader.read(file_path) self.draw_minutiae() self.tabs[self.notebook.index("current")].load_minutiae_file() except Exception as e: traceback.print_exc() showerror("Read Minutiae File", "There was an error in reading the minutiae file.\n\n" "The error message was:\n{}".format(e)) def save_minutiae_file(self): file_path = asksaveasfilename(initialfile=self.file_path.stem, filetypes=(("Simple minutiae file", '*.sim'), ("NBIST minutiae file", '*.min'), ("x y theta file", '*.xyt'))) if file_path: # Select the correct file format if Path(file_path).suffix == '.sim': writer = MinutiaeEncoder(MinutiaeFileFormat.SIMPLE) elif Path(file_path).suffix == '.min': writer = MinutiaeEncoder(MinutiaeFileFormat.NBIST) elif Path(file_path).suffix == '.xyt': writer = MinutiaeEncoder(MinutiaeFileFormat.XYT) else: showerror("Save Minutiae File", "The chosen file had an extension of '{}', which can't be interpreted." .format(Path(file_path).suffix)) return try: writer.write(file_path, self.minutiae, self.image_raw) except Exception as e: traceback.print_exc() showerror("Save Minutiae File", "There was an error in saving the minutiae file.\n\n" "The error message was:\n{}".format(e)) def exit_application(self): """ Attempts to exit the application. """ self.destroy() def draw_minutiae(self): # Apply filtering from each active module minutiae = [m.copy() for m in self.minutiae] for tab in self.tabs: minutiae = tab.minutiae_filtering(minutiae) # Scale minutiae ratio = self.image_fingerprint.width / self.image_raw.width minutiae = [Minutia(int(m.x * ratio), int(m.y * ratio), m.angle, m.minutia_type, m.quality) for m in minutiae] im = Image.new('RGBA', self.image_fingerprint.size, (0, 0, 0, 0)) self.image_minutiae = draw_minutiae(im, minutiae) im = self.image_fingerprint.copy() im.paste(self.image_minutiae, (0, 0), self.image_minutiae) self.image = ImageTk.PhotoImage(im) self.image_canvas.delete("IMG") self.image_canvas.create_image(0, 0, image=self.image, anchor=N + W, tags="IMG") self.update_idletasks() def draw_single_minutia(self, minutia: Minutia): temp_image = draw_minutiae(self.image_minutiae, [minutia]) im = self.image_fingerprint.copy() im.paste(temp_image, (0, 0), temp_image) self.image = ImageTk.PhotoImage(im) self.image_canvas.delete("IMG") self.image_canvas.create_image(0, 0, image=self.image, anchor=N + W, tags="IMG") self.update_idletasks() def number_of_minutiae(self): """ Returns the number of minutiae features. :return: Number of minutiae. """ return len(self.minutiae) def is_point_in_canvas_image(self, x: int, y: int) -> bool: """ Tests if a point is within the fingerprint image. :param x: x co-ordinate of the point to test. :param y: y co-ordinate of the point to test. :return: True if point is in the image, false otherwise. """ if x > self.image.width() or y > self.image.height(): return False else: return True def canvas_image_scale_factor(self) -> float: """ Calculates the ratio between the canvas image's actual size and its displayed size. :return: The ratio. """ return self.image_raw.width / self.image.width() def on_canvas_mouse_left_click(self, event): self.tabs[self.notebook.index('current')].on_canvas_mouse_left_click(event) def on_canvas_ctrl_mouse_left_click(self, event): self.tabs[self.notebook.index('current')].on_canvas_ctrl_mouse_left_click(event) def on_canvas_mouse_left_drag(self, event): self.tabs[self.notebook.index('current')].on_canvas_mouse_left_drag(event) def on_canvas_mouse_left_release(self, event): self.tabs[self.notebook.index('current')].on_canvas_mouse_left_release(event) def on_canvas_mouse_right_click(self, event): self.tabs[self.notebook.index('current')].on_canvas_mouse_right_click(event)
class OptionsView: def __init__(self, settings, root, textFrameManager): self._settings = settings self._root = root self._textFrameManager = textFrameManager self._highlightWorker = None self._guiWorker = None self._showing = False self._saving = False self._textFrame: TF.TextFrame = None def linkWorkers(self, workers): self._highlightWorker = workers.highlightWorker self._guiWorker = workers.guiWorker def linkTextFrame(self, textFrame): self._textFrame = textFrame def _onClosing(self, savingSettings=False): # Delete all variable observers for settingsLine in list(self._setsDict.values()): for entry in list(settingsLine.entries.values()): try: entry.var.trace_vdelete("w", entry.observer) except: pass # Delete all view elements del self._setsDict # Close window self._view.destroy() if not savingSettings: self._showing = False class SettingsLineTemplate: def __init__(self, setGroup, setId, setDisplayName, setType): self.setGroup = setGroup self.setId = setId self.setDisplayName = setDisplayName self.setType = setType class SettingsLine: def __init__(self, group): self.group = group self.entries = dict() class LineColorSettingsLine(SettingsLine): def __init__(self, group): super().__init__(group) self.lineFrame = None class Entry: def __init__(self, entryType, entryVar): self.label: tk.Label = None self.var = None self.observer = None self.input: tk.Entry = None self.button: tk.Button = None self.data = OptionsView.EntryData(entryType, entryVar) def isVarUpdated(self): updated = False try: updated = self.var.get() != self.data.entryVar except AttributeError: traceLog(LogLevel.ERROR, "Tkinter var in OptionsView not initiated") updated = True return updated class EntryData: def __init__(self, entryType, entryVar): self.entryType = entryType self.entryVar = entryVar self.validation = OptionsView.EntryDataValidation( OptionsView.ENTRY_VALIDATION_OK, "white", "") class EntryDataValidation: def __init__(self, status, backgroundColor, infoText): self.status = status self.backgroundColor = backgroundColor self.infoText = infoText ENTRY_TYPE_COLOR = "typeColor" ENTRY_TYPE_STRING = "typeString" ENTRY_TYPE_INT = "typeInt" ENTRY_TYPE_REGEX = "typeRegex" ENTRY_TYPE_TOGGLE = "typeToggle" ENTRY_TYPE_OTHER = "typeOther" ENTRY_VALIDATION_OK = "entryValidationOk" ENTRY_VALIDATION_FAILED = "entryValidationFailed" ENTRY_VALIDATION_DUPLICATE = "entryValidationDuplicate" GROUP_TEXT_AREA = "groupTextArea" GROUP_SEARCH = "groupSearch" GROUP_LOGGING = "groupLogging" GROUP_LINE_COLORING = "groupLineColoring" EDIT_UP = "editUp" EDIT_DOWN = "editDown" EDIT_DELETE = "editDelete" ROW_HIGHLIGHT_COLOR = "gray" LOG_EXAMPLE_FILE = r"appdata\log_example.txt" def _loadLogExample(self): log = "[12:34:56.789] Main::test\n[12:34:56.789] Main::TestTwo" try: with open( os.path.join(self._settings.get(Sets.CT_HOMEPATH_FULL), self.LOG_EXAMPLE_FILE), "r") as file: log = file.read() except FileNotFoundError: traceLog(LogLevel.WARNING, "Log example file not found. Using default example") return log def show(self): # Only allow one options view at a time # TODO Can be done with a global var, then a global instance of OptionsView is not needed if not self._showing: self._showing = True self._lineColorMap = self._textFrame.getLineColorMap() self._view = tk.Toplevel(self._root) self._view.title("Options") self._view.protocol("WM_DELETE_WINDOW", self._onClosing) self._view.iconbitmap(self._settings.get(Sets.ICON_PATH_FULL)) self._setsDict = dict() ############################## # TAB CONTROL self._tabsFrame = tk.Frame(self._view) self._tabsFrame.grid(row=0, column=0, sticky="nsew") self._view.columnconfigure(0, weight=1) self._view.rowconfigure(0, weight=1) self._tabControl = Notebook(self._tabsFrame, padding=10) self._tabControl.grid(row=0, column=0, sticky=tk.N) self._tabList = list() ############################## # TEXT EXAMPLE logExample = self._loadLogExample() exampleTextFrameHeightMin = 400 exampleTextFrameWidth = 650 self._exampleTextFrame = tk.Frame(self._tabsFrame, height=exampleTextFrameHeightMin, width=exampleTextFrameWidth) self._exampleTextFrame.grid(row=0, column=1, padx=(0, 10), pady=(10, 10), sticky="nsew") self._exampleTextFrame.grid_propagate(False) self._tabsFrame.columnconfigure(1, weight=1) self._tabsFrame.rowconfigure(0, weight=1) tFont = Font(family=self._settings.get(Sets.TEXTAREA_FONT_FAMILY), size=self._settings.get(Sets.TEXTAREA_FONT_SIZE)) self._exampleText = tk.Text(self._exampleTextFrame,height=1, width=2,\ background=self._settings.get(Sets.TEXTAREA_BACKGROUND_COLOR),\ selectbackground=self._settings.get(Sets.TEXTAREA_SELECT_BACKGROUND_COLOR),\ foreground=self._settings.get(Sets.TEXTAREA_COLOR),\ font=tFont) self._exampleText.grid(row=0, column=0, padx=(0, 0), pady=(10, 0), sticky="nsew") self._exampleTextFrame.columnconfigure(0, weight=1) self._exampleTextFrame.rowconfigure(0, weight=1) self._updateExampleTextLineWrap( self._settings.get(Sets.TEXTAREA_LINE_WRAP)) self._exampleText.insert(1.0, logExample) xscrollbar = tk.Scrollbar(self._exampleTextFrame, orient=tk.HORIZONTAL, command=self._exampleText.xview) xscrollbar.grid(row=1, column=0, sticky=tk.W + tk.E) self._exampleText["xscrollcommand"] = xscrollbar.set yscrollbar = tk.Scrollbar(self._exampleTextFrame, orient=tk.VERTICAL, command=self._exampleText.yview) yscrollbar.grid(row=0, column=1, sticky=tk.N + tk.S) self._exampleText["yscrollcommand"] = yscrollbar.set ############### # Tab: Line Coloring self._lineColoringFrame = tk.Frame(self._tabControl, padx=5, pady=5) self._lineColoringFrame.grid(row=0, column=0, sticky=tk.N) self._tabControl.add(self._lineColoringFrame, text="Line Coloring") self._tabList.append(self.GROUP_LINE_COLORING) self._setsDict.update( self._createLineColorRows(self._lineColoringFrame, self._lineColorMap)) upButton = tk.Button(self._lineColoringFrame, text="UP", command=partial(self._editLineColorRow, self.EDIT_UP)) upButton.grid(row=0, column=2, padx=2) downButton = tk.Button(self._lineColoringFrame, text="DOWN", command=partial(self._editLineColorRow, self.EDIT_DOWN)) downButton.grid(row=1, column=2, padx=2) deleteButton = tk.Button(self._lineColoringFrame, text="Delete", command=partial(self._editLineColorRow, self.EDIT_DELETE)) deleteButton.grid(row=2, column=2, padx=2) self._lastFocusInRowId = "" self._lastFocusOutRowId = "" self._newButtonRow = len(self._lineColorMap) self._newButton = tk.Button(self._lineColoringFrame, text="New Line", command=partial( self._addNewEmptyLineColor)) self._newButton.grid(row=self._newButtonRow, column=0, sticky=tk.W, padx=(2, 100), pady=2) self._deletedLineColorRows = list() ############### # Tab: Text Area self._textAreaFrame = tk.Frame(self._tabControl, padx=5, pady=5) self._textAreaFrame.grid(row=0, column=0, sticky=tk.N) self._tabControl.add(self._textAreaFrame, text="Text Area") self._tabList.append(self.GROUP_TEXT_AREA) setLines = list() setLines.append( self.SettingsLineTemplate(self.GROUP_TEXT_AREA, Sets.TEXTAREA_BACKGROUND_COLOR, "Background Color", self.ENTRY_TYPE_COLOR)) setLines.append( self.SettingsLineTemplate( self.GROUP_TEXT_AREA, Sets.TEXTAREA_SELECT_BACKGROUND_COLOR, "Background Color Select", self.ENTRY_TYPE_COLOR)) setLines.append( self.SettingsLineTemplate(self.GROUP_TEXT_AREA, Sets.TEXTAREA_COLOR, "Text Color", self.ENTRY_TYPE_COLOR)) setLines.append( self.SettingsLineTemplate(self.GROUP_TEXT_AREA, Sets.TEXTAREA_FONT_FAMILY, "Font Family", self.ENTRY_TYPE_STRING)) setLines.append( self.SettingsLineTemplate(self.GROUP_TEXT_AREA, Sets.TEXTAREA_FONT_SIZE, "Font Size", self.ENTRY_TYPE_INT)) setLines.append( self.SettingsLineTemplate(self.GROUP_TEXT_AREA, Sets.TEXTAREA_LINE_WRAP, "Line Wrap", self.ENTRY_TYPE_TOGGLE)) self._setsDict.update( self._createStandardRows(self._textAreaFrame, setLines, 0)) ############### # Tab: Search self._searchFrame = tk.Frame(self._tabControl, padx=5, pady=5) self._searchFrame.grid(row=0, column=0, sticky=tk.N) self._tabControl.add(self._searchFrame, text="Search") self._tabList.append(self.GROUP_SEARCH) setLines = list() setLines.append( self.SettingsLineTemplate(self.GROUP_SEARCH, Sets.SEARCH_MATCH_COLOR, "Search match background color", self.ENTRY_TYPE_COLOR)) setLines.append( self.SettingsLineTemplate(self.GROUP_SEARCH, Sets.SEARCH_SELECTED_COLOR, "Search selected background color", self.ENTRY_TYPE_COLOR)) setLines.append( self.SettingsLineTemplate( self.GROUP_SEARCH, Sets.SEARCH_SELECTED_LINE_COLOR, "Search selected line background color", self.ENTRY_TYPE_COLOR)) self._setsDict.update( self._createStandardRows(self._searchFrame, setLines, 0)) ############### # Tab: Logging self._loggingFrame = tk.Frame(self._tabControl, padx=5, pady=5) self._loggingFrame.grid(row=0, column=0, sticky=tk.N) self._tabControl.add(self._loggingFrame, text="Logging") self._tabList.append(self.GROUP_LOGGING) setLines = list() setLines.append( self.SettingsLineTemplate(self.GROUP_LOGGING, Sets.LOG_FILE_PATH, "Log file path", self.ENTRY_TYPE_OTHER)) setLines.append( self.SettingsLineTemplate(self.GROUP_LOGGING, Sets.LOG_FILE_BASE_NAME, "Log file base name", self.ENTRY_TYPE_OTHER)) setLines.append( self.SettingsLineTemplate(self.GROUP_LOGGING, Sets.LOG_FILE_TIMESTAMP, "Time stamp", self.ENTRY_TYPE_OTHER)) self._setsDict.update( self._createStandardRows(self._loggingFrame, setLines, 0)) ############################## # CONTROL ROW self._optionsControlFrame = tk.Frame(self._view) self._optionsControlFrame.grid(row=1, column=0, padx=(10, 10), pady=(0, 10), sticky=tk.W + tk.E) self._optionsInfoLabel = tk.Label(self._optionsControlFrame, text="", justify=tk.LEFT) self._optionsInfoLabel.grid(row=0, column=0, sticky=tk.W) self._optionsControlFrame.columnconfigure(0, weight=1) self._optionsCancelButton = tk.Button(self._optionsControlFrame, text="Cancel", command=self._onClosing) self._optionsCancelButton.grid(row=0, column=1, padx=5, sticky=tk.E) self._optionsSaveButton = tk.Button(self._optionsControlFrame, text="Save", command=self._saveSettings) self._optionsSaveButton.grid(row=0, column=2, sticky=tk.E) if self._saving: self._optionsSaveButton.config(state=tk.DISABLED) else: self._optionsSaveButton.config(state=tk.NORMAL) self._tabControl.bind("<<NotebookTabChanged>>", self._tabChanged) # print("Number of settings " + str(len(self._setsDict))) def _saveSettings(self): saveSettingsThread = threading.Thread(target=self._saveSettingsProcess, name="SaveSettings") saveSettingsThread.start() self._saving = True def _setSaveButtonState(self, state): if self._showing: try: self._optionsSaveButton.config(state=state) except: # Catch if function is called while save button does not exist traceLog(LogLevel.ERROR, "Error updating save button state") def _saveSettingsProcess(self): # Saving will block, so must be done in different thread # setsDict will be deleted in the onClosing function tempSetsDict = self._setsDict # Close options view self._root.after(10, self._onClosing, True) # Get registered textFrames textFrames = self._textFrameManager.getTextFrames() # Show saving message for textFrame in textFrames: textFrame.showSpinner("Reloading View") # Stop workers using the settings self._highlightWorker.stopWorker(emptyQueue=False) self._guiWorker.stopWorker() # Save all settings tempLineColorMap = dict() tempLineColorRows = dict() # Sort settings to guarantee right order of line coloring for rowId in sorted(tempSetsDict.keys()): if Sets.LINE_COLOR_MAP in rowId: tempLineColorMap[rowId] = dict() tempLineColorRows[rowId] = tempSetsDict[rowId] for entryName in tempSetsDict[rowId].entries.keys(): setting = tempSetsDict[rowId].entries[entryName].var.get() if Sets.LINE_COLOR_MAP in rowId: tempLineColorMap[rowId][entryName] = setting else: self._settings.setOption(rowId, setting) self._settings.setOption(Sets.LINE_COLOR_MAP, tempLineColorMap) # Once settings have been saved, allow for reopen of options view self._showing = False # Delete line color tags for deletedRowData in self._deletedLineColorRows: for textFrame in textFrames: textFrame.deleteTextTag(deletedRowData["tagName"]) # Process added or updated line color rows for rowId in tempLineColorRows.keys(): if tempLineColorRows[rowId].entries["regex"].isVarUpdated(): if tempLineColorRows[rowId].entries["regex"].data.entryVar: oldTagName = TF.createLineColorTagName( tempLineColorRows[rowId].entries["regex"].data.entryVar ) for textFrame in textFrames: textFrame.deleteTextTag(oldTagName) # print("Delete edited row id: " + rowId) for textFrame in textFrames: textFrame.createAndAddLineColorTag( tempLineColorRows[rowId].entries["regex"].var.get(), tempLineColorRows[rowId].entries["color"].var.get()) # print("Added line color row: " + rowId) elif tempLineColorRows[rowId].entries["color"].isVarUpdated(): tagName = TF.createLineColorTagName( tempLineColorRows[rowId].entries["regex"].var.get()) for textFrame in textFrames: textFrame.updateTagColor( tagName, tempLineColorRows[rowId].entries["color"].var.get()) # Reorder line color tags rowIds = sorted(tempLineColorRows.keys()) if rowIds: preTagName = TF.createLineColorTagName( tempLineColorRows[rowIds[0]].entries["regex"].var.get()) for rowId in rowIds[1:-1]: tagName = TF.createLineColorTagName( tempLineColorRows[rowId].entries["regex"].var.get()) for textFrame in textFrames: textFrame.textArea.tag_raise(tagName, aboveThis=preTagName) preTagName = tagName # print(*self._textFrame.textArea.tag_names(),sep=", ") # Reload main interface for textFrame in textFrames: textFrame.reloadLineColorMap() textFrame.reloadTextFrame() # Start workers self._highlightWorker.startWorker() self._guiWorker.startWorker() # Remove spinners for textFrame in textFrames: textFrame.closeSpinner() # Update save button, if window has been opened again self._root.after(10, self._setSaveButtonState, tk.NORMAL) self._saving = False #################################### # View Creation def _addNewEmptyLineColor(self): # print("New Button " + str(self.newButtonRow)) self._newButton.grid(row=self._newButtonRow + 1) rowId = self._getRowId(self._newButtonRow) self._setsDict[rowId] = self._createSingleLineColorRow( self._lineColoringFrame, self._newButtonRow, rowId, "", "white") self._newButtonRow += 1 def _editLineColorRow(self, edit): # print("Last focus in " + self.lastFocusInRowId) # print("Last focus out " + self.lastFocusOutRowId) # If lastFocusIn is not the same as lastFocusOut, # we know that lastFocusIn is currently selected. if self._lastFocusInRowId != self._lastFocusOutRowId: if Sets.LINE_COLOR_MAP in self._lastFocusInRowId: # print("EDIT: " + self.lastFocusInRowId) # Get row number # TODO Use getRowNum?? rowNum = int( self._lastFocusInRowId.replace(Sets.LINE_COLOR_MAP, "")) # Find index of rows to edit indexToChange = list() if edit == self.EDIT_UP: if rowNum > 0: indexToChange = [rowNum - 1, rowNum] elif edit == self.EDIT_DOWN: if rowNum < (self._newButtonRow - 1): indexToChange = [rowNum, rowNum + 1] elif edit == self.EDIT_DELETE: indexToChange = range(rowNum, self._newButtonRow) if indexToChange: tempTextColorMap = list() for i in indexToChange: # Save regex and color rowId = self._getRowId(i) tempTextColorMap.append((self._setsDict[rowId].entries["regex"].var.get(), \ self._setsDict[rowId].entries["regex"].data, \ self._setsDict[rowId].entries["color"].var.get(), \ self._setsDict[rowId].entries["color"].data)) # Remove rows to edit from view self._setsDict[rowId].lineFrame.destroy() del self._setsDict[rowId] # Reorder or delete saved rows newRowNum = -1 if edit == self.EDIT_UP: tempTextColorMap[1], tempTextColorMap[ 0] = tempTextColorMap[0], tempTextColorMap[1] newRowNum = rowNum - 1 elif edit == self.EDIT_DOWN: tempTextColorMap[1], tempTextColorMap[ 0] = tempTextColorMap[0], tempTextColorMap[1] newRowNum = rowNum + 1 elif edit == self.EDIT_DELETE: deletedRowData = dict() deletedRowData["tagName"] = TF.createLineColorTagName( tempTextColorMap[0][0]) self._deletedLineColorRows.append(deletedRowData) del tempTextColorMap[0] # Recreate saved rows for i, (regexVar, regexData, colorVar, colorData) in enumerate(tempTextColorMap): rowId = self._getRowId(indexToChange[i]) self._setsDict[rowId] = self._createSingleLineColorRow( self._lineColoringFrame, indexToChange[i], rowId, regexVar, colorVar) self._setsDict[rowId].entries["regex"].data = regexData self._setsDict[rowId].entries["color"].data = colorData # If move up or down, refocus if newRowNum > -1: rowId = self._getRowId(newRowNum) self._focusInSet(rowId) # If delete, update row count and move newButton else: self._newButtonRow = self._newButtonRow - 1 self._newButton.grid(row=self._newButtonRow) self._lastFocusInRowId = "" self._updateExampleText(self.GROUP_LINE_COLORING) def _createLineColorRows(self, parent, lineColorMap): setDict = dict() for rowId in sorted(lineColorMap.keys()): rowNum = int(rowId.replace(Sets.LINE_COLOR_MAP, "")) setDict[rowId] = self._createSingleLineColorRow( parent, rowNum, rowId, lineColorMap[rowId]["regex"], lineColorMap[rowId]["color"]) return setDict def _createSingleLineColorRow(self, parent, row, rowId, regex, color): colorLine = self.LineColorSettingsLine(self.GROUP_LINE_COLORING) colorLine.lineFrame = tk.Frame(parent, highlightcolor=self.ROW_HIGHLIGHT_COLOR, highlightthickness=2) colorLine.lineFrame.grid(row=row, column=0) colorLine.lineFrame.bind("<Button-1>", partial(self._focusInSet, rowId)) colorLine.lineFrame.bind("<FocusOut>", partial(self._focusOut, rowId)) regexEntry = self.Entry(self.ENTRY_TYPE_REGEX, regex) entryName = "regex" regexEntry.label = tk.Label(colorLine.lineFrame, text="Regex") regexEntry.label.grid(row=0, column=0) regexEntry.label.bind("<Button-1>", partial(self._focusInSet, rowId)) regexEntry.var = tk.StringVar(colorLine.lineFrame) regexEntry.var.set(regex) regexEntry.observer = regexEntry.var.trace( "w", partial(self._validateInput, rowId, entryName)) regexEntry.input = tk.Entry(colorLine.lineFrame, textvariable=regexEntry.var, width=30, takefocus=False) regexEntry.input.grid(row=0, column=1) regexEntry.input.bind("<Button-1>", partial(self._focusInLog, rowId)) colorLine.entries[entryName] = regexEntry colorEntry = self.Entry(self.ENTRY_TYPE_COLOR, color) entryName = "color" colorEntry.label = tk.Label(colorLine.lineFrame, text="Color") colorEntry.label.grid(row=0, column=2) colorEntry.label.bind("<Button-1>", partial(self._focusInSet, rowId)) colorEntry.var = tk.StringVar(colorLine.lineFrame) colorEntry.var.set(color) colorEntry.observer = colorEntry.var.trace( "w", partial(self._validateInput, rowId, entryName)) colorEntry.input = tk.Entry(colorLine.lineFrame, textvariable=colorEntry.var, width=10, takefocus=False) colorEntry.input.grid(row=0, column=3) colorEntry.input.bind("<Button-1>", partial(self._focusInLog, rowId)) colorEntry.button = tk.Button(colorLine.lineFrame, bg=color, width=3, command=partial(self._getColor, rowId, entryName, True)) colorEntry.button.grid(row=0, column=4, padx=4) colorEntry.button.bind("<Button-1>", partial(self._focusInSet, rowId)) colorLine.entries[entryName] = colorEntry return colorLine def _createStandardRows(self, parent, setLines, startRow): setDict = dict() # Find longest entry in settings maxLen = 0 for setLine in setLines: setLen = len(str(self._settings.get(setLine.setId))) if setLen > maxLen: maxLen = setLen row = startRow for setLine in setLines: setRow = self.SettingsLine(setLine.setGroup) entry = self.Entry(setLine.setType, self._settings.get(setLine.setId)) entryName = "entry" # TODO Add frame and highlight to colors (remember column widths and alignment) entry.label = tk.Label(parent, text=setLine.setDisplayName) entry.label.grid(row=row, column=0, sticky=tk.W) ######## # Entry variable if setLine.setType == self.ENTRY_TYPE_INT: entry.var = tk.IntVar(parent) else: entry.var = tk.StringVar(parent) # Init entry var entry.var.set(self._settings.get(setLine.setId)) # TODO use tkinter validateCommand entry.observer = entry.var.trace( "w", partial(self._validateInput, setLine.setId, entryName)) ######## # Input field if setLine.setType == self.ENTRY_TYPE_TOGGLE: # TODO create better toggle values (link to specific settings) toggleButtonFrame = tk.Frame(parent) toggleButtonFrame.grid(row=row, column=1, sticky=tk.E + tk.W) toggleButtonFrame.grid_columnconfigure(0, weight=1) toggleButtonFrame.grid_columnconfigure(1, weight=1) onButton = tk.Radiobutton(toggleButtonFrame, text="On", variable=entry.var, indicatoron=False, value="on") onButton.grid(row=0, column=0, sticky=tk.E + tk.W) offButton = tk.Radiobutton(toggleButtonFrame, text="Off", variable=entry.var, indicatoron=False, value="off") offButton.grid(row=0, column=1, sticky=tk.E + tk.W) else: # TODO Find better solution for entry width entry.input = tk.Entry(parent, textvariable=entry.var, width=int(maxLen * 1.5), takefocus=False) entry.input.grid(row=row, column=1) ######## # Color button if setLine.setType == self.ENTRY_TYPE_COLOR: entry.button = tk.Button(parent, bg=self._settings.get(setLine.setId), width=3, command=partial( self._getColor, setLine.setId, entryName)) entry.button.grid(row=row, column=2, padx=4) setRow.entries[entryName] = entry setDict[setLine.setId] = setRow row += 1 return setDict #################################### # View Interaction def _focusOut(self, rowId, event): self._lastFocusOutRowId = rowId def _focusInSet(self, rowId, event=0): self._setsDict[rowId].lineFrame.focus_set() self._focusInLog(rowId, event) def _focusInLog(self, rowId, event=0): self._lastFocusInRowId = rowId if self._lastFocusOutRowId == rowId: self._lastFocusOutRowId = "" def _getColor(self, rowId, entry, highlight=False): if highlight: hg = self._setsDict[rowId].lineFrame.cget("highlightbackground") self._setsDict[rowId].lineFrame.config( highlightbackground=self.ROW_HIGHLIGHT_COLOR) currentColor = self._setsDict[rowId].entries[entry].button.cget("bg") if not self._isValidColor(currentColor): currentColor = None color = askcolor(initialcolor=currentColor, parent=self._view) if color[1] != None: self._setsDict[rowId].entries[entry].var.set(color[1]) self._setsDict[rowId].entries[entry].button.config(bg=color[1]) if highlight: self._setsDict[rowId].lineFrame.config(highlightbackground=hg) self._focusInLog(rowId) # class WidgetSize: # def __init__(self,width,height,posx,posy): # self.width = width # self.height = height # self.posx = posx # self.posy = posy # def _getWidgetSize_(self,widget): # width = widget.winfo_width() # height = widget.winfo_height() # posx = widget.winfo_x() # posy = widget.winfo_y() # return self.WidgetSize(width,height,posx,posy) def _tabChanged(self, event): self._view.focus_set() self._exampleText.tag_remove("sel", 1.0, tk.END) self._updateExampleText( self._tabList[self._tabControl.index("current")]) def _updateExampleText(self, group): ##################### # Setup # Delete all search tags self._exampleText.tag_delete(Sets.SEARCH_SELECTED_LINE_COLOR) self._exampleText.tag_delete(Sets.SEARCH_MATCH_COLOR) self._exampleText.tag_delete(Sets.SEARCH_SELECTED_COLOR) # Delete all current line color tags tagNames = self._exampleText.tag_names() for tagName in tagNames: if Sets.LINE_COLOR_MAP in tagName: self._exampleText.tag_delete(tagName) entryName = "entry" if group == self.GROUP_TEXT_AREA: # General text area try: tFont = Font(family=self._setsDict[Sets.TEXTAREA_FONT_FAMILY].entries[entryName].var.get(),\ size=self._setsDict[Sets.TEXTAREA_FONT_SIZE].entries[entryName].var.get()) self._exampleText.config(background=self._setsDict[Sets.TEXTAREA_BACKGROUND_COLOR].entries[entryName].var.get(),\ selectbackground=self._setsDict[Sets.TEXTAREA_SELECT_BACKGROUND_COLOR].entries[entryName].var.get(),\ foreground=self._setsDict[Sets.TEXTAREA_COLOR].entries[entryName].var.get(),\ font=tFont) lineWrapString = self._setsDict[ Sets.TEXTAREA_LINE_WRAP].entries[entryName].var.get() if lineWrapString == "on": self._updateExampleTextLineWrap(Sets.LINE_WRAP_ON) elif lineWrapString == "off": self._updateExampleTextLineWrap(Sets.LINE_WRAP_OFF) except tk.TclError: pass elif group == self.GROUP_SEARCH: searchString = "Main" # Create search tags self._exampleText.tag_configure(Sets.SEARCH_SELECTED_LINE_COLOR, \ background=self._setsDict[Sets.SEARCH_SELECTED_LINE_COLOR].entries[entryName].var.get(),\ selectbackground=util.lightOrDarkenColor(self._setsDict[Sets.SEARCH_SELECTED_LINE_COLOR].entries[entryName].var.get(),Sets.SELECTED_LINE_DARKEN_COLOR)) self._exampleText.tag_configure(Sets.SEARCH_MATCH_COLOR, \ background=self._setsDict[Sets.SEARCH_MATCH_COLOR].entries[entryName].var.get(),\ selectbackground=util.lightOrDarkenColor(self._setsDict[Sets.SEARCH_MATCH_COLOR].entries[entryName].var.get(),Sets.SELECTED_LINE_DARKEN_COLOR)) self._exampleText.tag_configure(Sets.SEARCH_SELECTED_COLOR, \ background=self._setsDict[Sets.SEARCH_SELECTED_COLOR].entries[entryName].var.get(), \ selectbackground=util.lightOrDarkenColor(self._setsDict[Sets.SEARCH_SELECTED_COLOR].entries[entryName].var.get(),Sets.SELECTED_LINE_DARKEN_COLOR)) # Do search countVar = tk.StringVar() results = list() start = 1.0 while True: pos = self._exampleText.search(searchString, start, stopindex=tk.END, count=countVar, nocase=False, regexp=False) if not pos: break else: results.append((pos, pos + "+" + countVar.get() + "c")) start = pos + "+1c" # Add search tags first = True for result in results: self._exampleText.tag_add(Sets.SEARCH_MATCH_COLOR, result[0], result[1]) if first: first = False self._exampleText.tag_add(Sets.SEARCH_SELECTED_COLOR, result[0], result[1]) selectLine = result[0].split(".")[0] self._exampleText.tag_add(Sets.SEARCH_SELECTED_LINE_COLOR, selectLine + ".0", selectLine + ".0+1l") if group == self.GROUP_LINE_COLORING or group == self.GROUP_SEARCH: # Get line color map from view tempLineColorMap = list() for rowId in sorted(self._setsDict.keys()): if Sets.LINE_COLOR_MAP in rowId: lineInfo = dict() lineInfo["regex"] = self._setsDict[rowId].entries[ "regex"].var.get() lineInfo["color"] = self._setsDict[rowId].entries[ "color"].var.get() lineInfo["tagName"] = TF.createLineColorTagName( lineInfo["regex"]) tempLineColorMap.append(lineInfo) # Apply new line colors for lineInfo in tempLineColorMap: self._exampleText.tag_configure(lineInfo["tagName"], foreground=lineInfo["color"]) countVar = tk.StringVar() start = 1.0 while True: pos = self._exampleText.search(lineInfo["regex"], start, stopindex=tk.END, count=countVar, nocase=False, regexp=True) if not pos: break else: self._exampleText.tag_add( lineInfo["tagName"], pos, pos + "+" + countVar.get() + "c") start = pos + "+1c" def _updateExampleTextLineWrap(self, lineWrapState): if lineWrapState == Sets.LINE_WRAP_ON: self._exampleText.config(wrap=tk.CHAR) else: self._exampleText.config(wrap=tk.NONE) #################################### # Entry Validation def _validateInput(self, rowId, entryName, *args): # Get variable try: settingsLine: self.SettingsLine = self._setsDict[rowId] entry: self.Entry = settingsLine.entries[entryName] varIn = entry.var.get() validationStatus = self.ENTRY_VALIDATION_OK except tk.TclError: # print("Tcl Error") validationStatus = self.ENTRY_VALIDATION_FAILED if validationStatus == self.ENTRY_VALIDATION_OK: # Check Colors if entry.data.entryType == self.ENTRY_TYPE_COLOR: if self._isValidColor(varIn): # print("Color " + str(color)) entry.button.config(background=varIn) validationStatus = self.ENTRY_VALIDATION_OK else: validationStatus = self.ENTRY_VALIDATION_FAILED # Check regex if entry.data.entryType == self.ENTRY_TYPE_REGEX: # Validate regex if self._isValidRegex(varIn): entry.data.validation.status = self.ENTRY_VALIDATION_OK else: entry.data.validation.status = self.ENTRY_VALIDATION_FAILED self._updateAllRegexEntries() validationStatus = entry.data.validation.status # Check font family if rowId == Sets.TEXTAREA_FONT_FAMILY: if self._isValidFontFamily(varIn): validationStatus = self.ENTRY_VALIDATION_OK else: validationStatus = self.ENTRY_VALIDATION_FAILED # Check font size if rowId == Sets.TEXTAREA_FONT_SIZE: if self._isValidFontSize(varIn): validationStatus = self.ENTRY_VALIDATION_OK else: validationStatus = self.ENTRY_VALIDATION_FAILED ####### # Update validation info if validationStatus == self.ENTRY_VALIDATION_OK: entry.data.validation.status = self.ENTRY_VALIDATION_OK entry.data.validation.backgroundColor = "white" entry.data.validation.infoText = "" elif validationStatus == self.ENTRY_VALIDATION_FAILED: entry.data.validation.status = self.ENTRY_VALIDATION_FAILED entry.data.validation.backgroundColor = "red" entry.data.validation.infoText = "Non-valid input." if not entry.data.entryType == self.ENTRY_TYPE_TOGGLE: entry.input.config( background=entry.data.validation.backgroundColor) infoText = "" for key in self._setsDict.keys(): for (entryKey, entryItem) in self._setsDict[key].entries.items(): if entryItem.data.validation.status != self.ENTRY_VALIDATION_OK: entryId = key + "_" + entryKey if infoText: infoText += "\n" infoText += entryId + ": " + entryItem.data.validation.infoText if infoText: self._optionsInfoLabel.config(text=infoText) self._setSaveButtonState(tk.DISABLED) else: self._optionsInfoLabel.config(text="") self._setSaveButtonState(tk.NORMAL) self._updateExampleText(settingsLine.group) def _isValidColor(self, colorString): isValid = True try: tk.Label(None, background=colorString) except tk.TclError: # print("Color Error") isValid = False return isValid def _isValidFontFamily(self, family): fontList = tk.font.families() return family in fontList def _isValidFontSize(self, size): isValid = True try: Font(size=size) except tk.TclError: # print("Font Size Error") isValid = False if isValid: if int(size) < 1: isValid = False return isValid def _isValidRegex(self, regex): isValid = True try: # re.compile(regex) # Tkinter does not allow all regex, so this cannot be used self._exampleText.search(regex, 1.0, stopindex=tk.END, regexp=True) except: isValid = False return isValid def _updateAllRegexEntries(self): # Get all regex regexList = list() for key in self._setsDict.keys(): try: value = self._setsDict[key].entries["regex"].var.get() regexList.append(value) except KeyError: pass # Find any duplicate entries regexListCount = Counter(regexList) regexDuplicateList = [ regex for regex, count in regexListCount.items() if count > 1 ] # Update all duplicate regex entries for key in self._setsDict.keys(): try: regexEntry = self._setsDict[key].entries["regex"] # Only update status if entry validation status is not currently failed if regexEntry.data.validation.status != self.ENTRY_VALIDATION_FAILED: # Mark duplicates if regexEntry.var.get() in regexDuplicateList: # print("New duplicate: " + regexEntry.var.get()) regexEntry.data.validation.status = self.ENTRY_VALIDATION_DUPLICATE regexEntry.data.validation.backgroundColor = "yellow" regexEntry.data.validation.infoText = "Duplicate regex entry not allowed." else: # Clear previous duplicates that are now valid if regexEntry.data.validation.status == self.ENTRY_VALIDATION_DUPLICATE: # print("Clear duplicate: " + regexEntry.var.get()) regexEntry.data.validation.status = self.ENTRY_VALIDATION_OK regexEntry.data.validation.backgroundColor = "white" regexEntry.data.validation.infoText = "" regexEntry.input.config( background=regexEntry.data.validation.backgroundColor) except KeyError: pass #################################### # Misc def _getRowId(self, rowNum): return Sets.LINE_COLOR_MAP + "{:02d}".format(rowNum)
class Main(object): def __init__(self, title): root = Tk() root.title(title) root.focus_set() root.rowconfigure(0, weight=0) root.columnconfigure(0, weight=1) root.rowconfigure(1, weight=1) self._root = root self.menubar = Frame(root) self.menubar.grid(row=0, column=0, sticky=(W, E)) self.menubar['takefocus'] = False quit_button = Button(self.menubar, text='Quit', command=self.quit) quit_button.grid(row=0, column=0) self._menucolumn = 1 self.views = list() self.paned_win = PanedWindow(root, orient=HORIZONTAL) self.paned_win.grid(row=1, column=0, sticky=(N, S, W, E)) self._query = None self._accept_func = None self.sidebar_views = dict() self.sidebar_count = 0 self.sidebar = PanedWindow(self.paned_win) self.paned_win.add(self.sidebar, weight=1) self.tabs = Notebook(self.paned_win) self.tabs.enable_traversal() self.paned_win.add(self.tabs, weight=5) self.root = self.tabs def add_menubutton(self, label, action): button = Button(self.menubar, text=label, command=action) button.grid(row=0, column=self._menucolumn) self._menucolumn += 1 def add_sidebar(self, view, name): self.sidebar_views[name] = view self.sidebar.add(view.widget, weight=1) view.widget.focus_set() self.sidebar_count += 1 if self.sidebar_count == 1: self.sidebar_views['main'] = view def remove_sidebar_view(self, name): self.sidebar.forget(self.sidebar_views[name].widget) self.sidebar_count -= 1 del self.sidebar_views[name] if self.sidebar_count == 0: del self.sidebar_views['main'] def get_sidebar_view(self, name): return self.sidebar_views.get(name) def focus_sidebar(self): if 'main' in self.sidebar_views.keys(): self.sidebar_views['main'].widget.focus_set() def focus_main_view(self): self.get_current_view().widget.focus_set() def new_view(self, view): self.views.append(view) self.tabs.add(view.widget, text=" {}.".format(self.tabs.index('end'))) self.tabs.select(view.widget) view.widget.focus_set() self.view_changed() def remove_view(self, view): self.views.remove(view) self.tabs.forget(view.widget) if len(self.views) >= 1: widget = self.views[-1].widget self.tabs.select(widget) widget.focus_set() else: self.sidebar_views['main'].widget.focus_set() self.view_changed() def delete_current_view(self, event): if self.tabs.index('end') > 0: self.remove_view(self.get_current_view()) def close_query(self): if self._query is not None: self._query.event_generate('<<MainQueryClose>>') self._query.destroy() self._query = None self._accept_func = None self._menucolumn -= 1 def accept_query(self, event): if self._query is not None: if self._accept_func is not None: self._accept_func(event.widget.get(), event.widget.original_value) self.close_query() else: event.widget.event_generate('<<MainQueryAccept>>') def text_query(self, query_lable, original_text=None, accept_func=None): if self._query is not None: return frame = Frame(self.menubar) label = Label(frame, text=query_lable) label.grid(column=0, row=0, sticky=(N, S)) self._accept_func = accept_func entry = Entry(frame) if original_text is not None: entry.insert(0, original_text) entry.original_value = original_text entry.grid(column=1, row=0, sticky=(N,S,W,E)) kb.make_bindings(kb.text_query, {'accept': self.accept_query, 'cancel': lambda e: self.close_query()}, entry.bind) frame.grid(column=self._menucolumn, row=0) self._menucolumn += 1 entry.focus_set() self._query = frame def get_current_view(self): if self.tabs.index('end') > 0: return self.views[self.tabs.index('current')] else: return self.sidebar_views['main'] def view_changed(self): self._root.event_generate('<<MainViewChanged>>') def display(self): self._root.mainloop() def quit(self): self._root.destroy()
class CimApp(Frame): def __init__(self): Frame.__init__(self) self.file = None; self.master.title("Tiborcim") self.master.iconphoto(True, PhotoImage(file=ICON_PNG)) self.files = [] self.current_tab = StringVar() self.pack(expand=1, fill="both") self.master.minsize(300,300) self.master.geometry("500x500") self.menubar = Menu(self.master) self.fileMenu = Menu(self.master, tearoff=0) self.fileMenu.add_command(label="New", command=self.new_file, underline=0, accelerator="Ctrl+N") self.fileMenu.add_command(label="Open...", command=self.load_file, underline=0, accelerator="Ctrl+O") self.fileMenu.add_command(label="Save", command=self.file_save, underline=0, accelerator="Ctrl+S") self.fileMenu.add_command(label="Save As...", command=self.file_save_as, underline=5, accelerator="Ctrl+Alt+S") self.fileMenu.add_command(label="Close", command=self.close_file, underline=0, accelerator="Ctrl+W") self.fileMenu.add_separator() self.fileMenu.add_command(label="Exit", command=self.file_quit, underline=1) self.menubar.add_cascade(label="File", menu=self.fileMenu, underline=0) self.edit_program = Menu(self.master, tearoff=0) self.edit_program.add_command(label="Undo", command=self.edit_undo, underline=0, accelerator="Ctrl+Z") self.edit_program.add_command(label="Redo", command=self.edit_redo, underline=0, accelerator="Ctrl+Y") self.edit_program.add_separator() self.edit_program.add_command(label="Cut", command=self.edit_cut, underline=2, accelerator="Ctrl+X") self.edit_program.add_command(label="Copy", command=self.edit_copy, underline=0, accelerator="Ctrl+C") self.edit_program.add_command(label="Paste", command=self.edit_paste, underline=0, accelerator="Ctrl+V") self.menubar.add_cascade(label="Edit", menu=self.edit_program, underline=0) self.menu_program = Menu(self.master, tearoff=0) self.menu_program.add_command(label="Convert", command=self.convert_file, underline=0, accelerator="Ctrl+T") self.menu_program.add_command(label="Flash", command=self.flash_file, underline=0, accelerator="Ctrl+B") self.menu_program.add_separator() self.menubar.add_cascade(label="Program", menu=self.menu_program, underline=0) self.menu_view = Menu(self.master, tearoff=0) self.viewmode = StringVar() self.viewmode.set("tiborcim") self.menu_view.add_radiobutton(label="Tiborcim", command=self.view_tiborcim, variable=self.viewmode, value="tiborcim", underline=0) self.menu_view.add_radiobutton(label="Python", command=self.view_python, variable=self.viewmode, value="python", underline=0) self.menubar.add_cascade(label="View", menu=self.menu_view, underline=0) self.menu_help = Menu(self.master, tearoff=0) self.menu_samples = Menu(self.master, tearoff=0) samples = tiborcim.resources.samples_list() def add_sample (sample): self.menu_samples.add_command(label=sample, command=lambda: self.help_sample(sample)) for sample in samples: add_sample(sample) self.menu_help.add_cascade(label="Samples", menu=self.menu_samples, underline=0) self.menu_help.add_separator() self.menu_help.add_command(label="README", command=self.help_readme, underline=0) self.menu_help.add_separator() self.menu_help.add_command(label="About", command=self.help_about, underline=0) self.menubar.add_cascade(label="Help", menu=self.menu_help, underline=0) self.master.config(width=450, height=400, menu=self.menubar) self.bind_all("<Control-o>", self.load_file) self.bind_all("<Control-s>", self.file_save) self.bind_all("<Control-Alt-s>", self.file_save_as) self.bind_all("<Control-t>", self.convert_file) self.bind_all("<Control-b>", self.flash_file) self.bind_all("<Control-w>", self.close_file) self.master.protocol("WM_DELETE_WINDOW", self.file_quit) self.file_tabs = Notebook(self) self.file_tabs.bind_all("<<NotebookTabChanged>>", self.file_changed) self.file_tabs.pack(expand=1, fill="both") def file_changed(self, event): if len(self.file_tabs.tabs()) <= 0: self.add_file() return title = str(event.widget.tab(event.widget.index("current"),"text")).upper().strip() self.menu_program.delete(3, END) for tab in self.file_tabs.tabs(): tabtext = self.file_tabs.tab(self.file_tabs.index(tab),"text") if tabtext.upper().strip() == title: self.current_tab.set(tab) self.menu_program.add_radiobutton(label=tabtext, command=self.program_switch, underline=1, value=tab, variable=self.current_tab) if title != "PYTHON" or title != "TIBORCIM": if self.current_file().filename is not None: self.master.title(self.current_file().get_file() + " - Tiborcim") else: self.master.title("Tiborcim") if str(self.current_file().tab(self.current_file().index("current"),"text")).upper().strip() == "TIBORCIM": self.menubar.entryconfig("Edit", state=NORMAL) else: self.menubar.entryconfig("Edit", state=DISABLED) self.viewmode.set(self.current_file().viewmode) if title == "PYTHON": self.menubar.entryconfig("Edit", state=DISABLED) self.current_file().viewmode = "python"; self.viewmode.set("python"); if title == "TIBORCIM": self.menubar.entryconfig("Edit", state=NORMAL) self.current_file().viewmode = "tiborcim"; self.viewmode.set("tiborcim"); def add_file(self, file=None): filepage = CimFilePage(self.file_tabs) if file is None: self.file_tabs.add(filepage, text="Unsaved Program") else: filepage.load_file(file) self.file_tabs.add(filepage, text=filepage.get_file()) self.files.append(filepage) self.file_tabs.select(filepage) def view_tiborcim(self, event=None): self.current_file().view_tiborcim() def view_python(self, event=None): self.current_file().view_python() def program_switch(self): self.file_tabs.select(self.current_tab.get()) def new_file(self, event=None): self.add_file() def load_file(self, event=None): fname = askopenfilename(filetypes=(("Tiborcim", "*.tibas"),("All files", "*.*") ), parent=self.master) if fname: self.add_file(fname) def file_save(self, event=None): self.current_file().save_file() self.file_tabs.tab(self.current_file(), text=self.current_file().get_file()) def file_save_as(self, event=None): self.current_file().save_file_as() self.file_tabs.tab(self.current_file(), text=self.current_file().get_file()) def convert_file(self, event=None): self.current_file().convert_file() def current_file(self, event=None): return self.files[int(self.file_tabs.index(self.file_tabs.select()))] def flash_file(self, event=None): from tiborcim.tibc import compiler as tibc from tiborcim.tibc import flash from tiborcim.tibc import TibcStatus as status com = tibc(self.current_file().text_tiborcim.get("1.0", "end")) res = flash(''.join(com.output)) if res is status.SUCCESS: showinfo(title='Success', message='File Flashed', parent=self.master) else: showerror(title='Failure', message='An Error Occured. Code: %s' % res, parent=self.master) def close_file(self, event=None): logging.debug("Close File") file = self.current_file() if file.close(): self.file_tabs.forget(file) self.files.remove(file) def edit_cut(self, event=None): self.current_file().text_tiborcim.event_generate('<Control-x>') def edit_copy(self, event=None): self.current_file().text_tiborcim.event_generate('<Control-c>') def edit_paste(self, event=None): self.current_file().text_tiborcim.event_generate('<Control-v>') def edit_redo(self, event=None): self.current_file().text_tiborcim.edit_redo() def edit_undo(self, event=None): self.current_file().text_tiborcim.edit_undo() def help_about(self, event=None): CimAbout.show(self) def help_readme(self, event=None): CimReadme.show(self) def help_sample(self, sam): print(sam) filepage = CimFilePage(self.file_tabs) filepage.load_file(tiborcim.resources.sample_path(sam)) filepage.filename = None self.file_tabs.add(filepage, text="Unsaved Program") self.files.append(filepage) self.file_tabs.select(filepage) def file_quit(self, event=None): for ndx, member in enumerate(self.files): logging.debug(self.files[ndx].saved) if not self.files[ndx].close(): return self.quit()
class CrickitGui(BanyanBase): """ The Crickit For Raspberry Pi Demo Station GUI """ def __init__(self, **kwargs): """ Build the main screen as a notebook. """ # initialize the parent class super(CrickitGui, self).__init__( back_plane_ip_address=kwargs['back_plane_ip_address'], subscriber_port=kwargs['subscriber_port'], publisher_port=kwargs['publisher_port'], process_name=kwargs['process_name']) self.set_subscriber_topic('report_from_hardware') self.main = Tk() self.main.title('Demo Station For Raspberry Pi Crickit') # gives weight to the cells in the grid rows = 0 while rows < 50: self.main.rowconfigure(rows, weight=1) self.main.columnconfigure(rows, weight=1) rows += 1 # Defines and places the notebook widget self.nb = Notebook(self.main, padding=20, height=300, width=800) self.nb.grid(row=1, column=0, columnspan=50, rowspan=49, sticky='NESW') # create the signal inputs tab self.signal_inputs_tab = Frame(self.nb, padding=10) self.nb.add(self.signal_inputs_tab, text='Signal Inputs') # create an instance of the SignalInputs class # to populate and control Signal inputs self.signal_inputs = SignalInputs(self.signal_inputs_tab, self) # create the signal outputs tab self.signal_outputs_tab = Frame(self.nb, padding=10) self.nb.add(self.signal_outputs_tab, text='Signal Outputs') # create an instance of the SignalOutputs class # to populate and control Signal outputs self.signal_outputs = SignalOutputs(self.signal_outputs_tab, self) # create the signals tab self.touch_tab = Frame(self.nb, padding=10) self.nb.add(self.touch_tab, text='Touch Inputs') self.touch_inputs = TouchInputs(self.touch_tab, self) # create the drives tab self.drives_tab = Frame(self.nb, padding=10) self.nb.add(self.drives_tab, text='Drive Outputs') self.drive_outputs = DriveOutputs(self.drives_tab, self) # create the dc motors tab self.motors_tab = Frame(self.nb, padding=10) self.nb.add(self.motors_tab, text='DC Motors') self.dc_motors = DcMotors(self.motors_tab, self) # create the servo motors tab self.servos_tab = Frame(self.nb, padding=10) self.nb.add(self.servos_tab, text='Servo Motors') self.servos = Servos(self.servos_tab, self) # create the stepper motors tab self.steppers_tab = Frame(self.nb, padding=10) self.nb.add(self.steppers_tab, text='Stepper Motors') self.stepper_motors = Steppers(self.steppers_tab, self) # create the stepper motors tab self.neopixels_tab = Frame(self.nb, padding=10) self.nb.add(self.neopixels_tab, text='NeoPixels') self.stepper_motors = NeoPixels(self.neopixels_tab, self) l = Label(self.main, text='Copyright (c) 2019 Alan Yorinks All Rights Reserved.') l.config(font="Helvetica 8 ") l.grid(row=48, column=0, padx=[505, 0]) self.main.after(5, self.get_message) try: self.main.mainloop() except KeyboardInterrupt: self.on_closing() def notebook_tab_selection(self): print(self.nb.tab(self.nb.select(), "text")) print(self.nb.index(self.nb.select())) def get_message(self): """ This method is called from the tkevent loop "after" method. It will poll for new zeromq messages within the tkinter event loop. """ try: data = self.subscriber.recv_multipart(zmq.NOBLOCK) self.incoming_message_processing( data[0].decode(), msgpack.unpackb(data[1], raw=False)) self.main.after(1, self.get_message) except zmq.error.Again: try: self.main.after(1, self.get_message) except KeyboardInterrupt: self.main.destroy() self.publisher.close() self.subscriber.close() self.context.term() sys.exit(0) except KeyboardInterrupt: self.main.destroy() self.publisher.close() self.subscriber.close() self.context.term() sys.exit(0) def incoming_message_processing(self, topic, payload): """ This method processes the incoming pin state change messages for GPIO pins set as inputs. :param topic: :param payload: Typical report: {'report': 'digital_input', 'pin': pin, 'value': level, 'timestamp': time.time()} """ # if the pin currently input active, process the state change pin = payload['pin'] value = payload['value'] timestamp = payload['timestamp'] if 0 <= pin < 8: self.signal_inputs.set_input_value(pin, value) self.signal_inputs.set_time_stamp_value(pin, timestamp) if 8 <= pin < 12: # we subtract eight to normalize the pin number self.touch_inputs.set_input_value(pin - 8, value) self.touch_inputs.set_time_stamp_value(pin - 8, timestamp) def on_closing(self): """ Destroy the window """ self.clean_up() self.main.destroy()
class Application(Frame): class TextFrameTab: def __init__(self, obj): self.text_frm = Frame(obj.notebook) self.text_frm.pack(side=TOP, expand=YES, fill=BOTH) self.text = Text(self.text_frm) self.text.config(fg='#111111', bg='#f2f2f2', bd=0, wrap=WORD, undo=True, maxundo=100, autoseparators=True, selectbackground='#bbbbcf') self.text.focus() self.yscroll = Scrollbar(self.text_frm, orient=VERTICAL) self.yscroll.config(cursor='arrow', command=self.text.yview, bg='#777777', activebackground='#6d6d6d', troughcolor='#c2c2c2') self.text['yscrollcommand'] = self.yscroll.set self.yscroll.pack(side=RIGHT, fill=Y) self.text.pack(side=LEFT, expand=YES, fill=BOTH) self.text.edit_modified(arg=False) obj.textwidgets.append(self.text) obj.notebook.add(self.text_frm, padding=1, text=obj.filenames[-1]) obj.notebook.select(self.text_frm) self.file_menu = obj.file_menu self.notebook = obj.notebook self.filenames = obj.filenames self.save_btn = obj.save_btn self.statusbar_line_lbl = obj.statusbar_line_lbl self.statusbar_col_lbl = obj.statusbar_col_lbl # Disable 'Save' menu item self.file_menu.entryconfigure(4, state=DISABLED) self.text.bind('<<StateChecking>>', self.check_state) self.text.bind('<<StateChecking>>', self.mark_line, add='+') def mark_line(self, event): if 'currline' in self.text.tag_names(): self.text.tag_delete('currline') self.text.tag_add('currline', 'insert linestart', 'insert linestart + 1 lines') # Highlight the current line self.text.tag_config('currline', background='#dadada') self.text.tag_lower('currline') def check_state(self, event): line_number = self.text.index(INSERT).split('.')[0] # Display the line number of the current cursor position # on the status bar self.statusbar_line_lbl.config(text='ln: ' + line_number) col_number = str(int(self.text.index(INSERT).split('.')[1]) + 1) # Display the column number of the current cursor position # on the status bar self.statusbar_col_lbl.config(text='col: ' + col_number) try: current_index = self.notebook.index('current') modified = self.text.edit_modified() if modified: # Enable 'Save' menu item self.file_menu.entryconfigure(4, state=NORMAL) # Add asterisk to the header of the tab self.notebook.tab(current_index, text='*' + self.filenames[current_index]) self.save_btn.config(state=NORMAL) else: # Disable 'Save' menu item self.file_menu.entryconfigure(4, state=DISABLED) # If there is asterisk at the header of the tab, # remove it self.notebook.tab(current_index, text=self.filenames[current_index]) self.save_btn.config(state=DISABLED) except TclError: pass def __init__(self, parent=None): Frame.__init__(self, parent) # A dictionary with paths to opened and/or saved files self.filepaths = {} # A list with names of files opened in separate tabs self.filenames = ['Untitled'] # A list with existing text widgets self.textwidgets = [] self.menubar = Frame(self) self.menubar.config(bg='#444444', bd=0, relief=FLAT, padx=2) self.menubar.pack(side=TOP, fill=X) self.toolbar = Frame(self) self.toolbar.config(bg='#444444', bd=1, relief=GROOVE, pady=6, padx=2) self.toolbar.pack(side=TOP, fill=X) self.statusbar = Frame(self) self.statusbar.config(bg='#222222', bd=0, relief=FLAT, padx=10) self.statusbar.pack(side=BOTTOM, fill=X) self.style = Style() self.style.configure('TNotebook', background='#606060') self.notebook = Notebook(self) self.notebook.enable_traversal() self.notebook.pack(side=TOP, expand=YES, fill=BOTH) self.file_menu_btn = Menubutton(self.menubar) self.file_menu_btn.config(text='File', bg='#444444', fg='#eeeeee', activeforeground='#eeeeee', activebackground='#647899', underline=0) self.file_menu_btn.pack(side=LEFT) self.file_menu = Menu(self.file_menu_btn, tearoff=0) self.file_menu_btn['menu'] = self.file_menu self.file_menu.config(fg='#eeeeee', activeforeground='#eeeeee', bg='#444444', activebackground='#647899') self.file_menu.add_command(label='New', command=self.create_new_doc, accelerator=' ' * 14 + 'Ctrl+N') self.file_menu.add_command(label='Open', command=self.open_file, accelerator=' ' * 14 + 'Ctrl+O') self.file_menu.add_command(label='Close', command=self.close_tab, accelerator=' ' * 14 + 'Ctrl+W') self.file_menu.add_separator() self.file_menu.add_command(label='Save', command=self.save_file, accelerator=' ' * 14 + 'Ctrl+S') self.file_menu.add_command(label='Save As', command=self.save_as_file, accelerator=' Ctrl+Shift+S') self.file_menu.add_separator() self.file_menu.add_command(label='Exit', command=self.quit_from_app, accelerator=' ' * 14 + 'Ctrl+Q') self.edit_menu_btn = Menubutton(self.menubar) self.edit_menu_btn.config(text='Edit', fg='#eeeeee', bg='#444444', activebackground='#647899', activeforeground='#eeeeee', underline=0) self.edit_menu_btn.pack(side=LEFT) self.edit_menu = Menu(self.edit_menu_btn, tearoff=0) self.edit_menu_btn['menu'] = self.edit_menu self.edit_menu.config(fg='#eeeeee', activeforeground='#eeeeee', bg='#444444', activebackground='#647899') self.edit_menu.add_command(label='Undo', command=self.undo, accelerator=' ' * 10 + 'Ctrl+Z') self.edit_menu.add_command(label='Redo', command=self.redo, accelerator='Ctrl+Shift+Z') self.edit_menu.add_separator() self.edit_menu.add_command(label='Cut', command=self.cut_text, accelerator=' ' * 10 + 'Ctrl+X') self.edit_menu.add_command(label='Copy', command=self.copy_text, accelerator=' ' * 10 + 'Ctrl+C') self.edit_menu.add_command(label='Paste', command=self.paste_text, accelerator=' ' * 10 + 'Ctrl+V') self.edit_menu.add_command(label='Delete', command=self.del_text) self.edit_menu.add_separator() self.edit_menu.add_command(label='Select All', command=self.select_all, accelerator=' ' * 10 + 'Ctrl+A') self.help_menu_btn = Menubutton(self.menubar) self.help_menu_btn.config(text='Help', fg='#eeeeee', bg='#444444', activeforeground='#eeeeee', activebackground='#647899', underline=0) self.help_menu_btn.pack(side=LEFT) self.help_menu = Menu(self.help_menu_btn, tearoff=0) self.help_menu_btn['menu'] = self.help_menu self.help_menu.config(fg='#eeeeee', activeforeground='#eeeeee', bg='#444444', activebackground='#647899') self.help_menu.add_command(label='About', command=self.show_about) self.file_tool_frm = Frame(self.toolbar) self.file_tool_frm.config(bg='#444444', bd=0, relief=FLAT, padx=4) self.file_tool_frm.pack(side=LEFT) self.edit_tool_frm = Frame(self.toolbar) self.edit_tool_frm.config(bg='#444444', bd=0, relief=FLAT, padx=12) self.edit_tool_frm.pack(side=LEFT) self.hint_lbl = Label(self.toolbar) self.hint_lbl.config(bg='#444444', fg='#eeeeee', font=('Sans', '10', 'italic'), bd=0, relief=FLAT, padx=12) self.hint_lbl.pack(side=LEFT) self.new_btn = Button(self.file_tool_frm) self.new_btn.config(text='\u2795', font=('Sans', '12'), fg='#eeeeee', bg='#333333', bd=0, activebackground='#555555', activeforeground='#ffffff', padx=4, pady=0, command=self.create_new_doc) self.new_btn.grid(row=0, column=0) self.new_btn.bind('<Enter>', lambda x: self.hint_lbl.config(text='New')) self.new_btn.bind('<Leave>', lambda x: self.hint_lbl.config(text='')) self.open_btn = Button(self.file_tool_frm) self.open_btn.config(text='\u21e9', font=('Sans', '12', 'bold'), fg='#eeeeee', bg='#333333', bd=0, activebackground='#555555', activeforeground='#ffffff', padx=4, pady=0, command=self.open_file) self.open_btn.grid(row=0, column=1, padx=20) self.open_btn.bind('<Enter>', lambda x: self.hint_lbl.config(text='Open')) self.open_btn.bind('<Leave>', lambda x: self.hint_lbl.config(text='')) self.save_btn = Button(self.file_tool_frm) self.save_btn.config(text='\u21e7', font=('Sans', '12', 'bold'), fg='#eeeeee', bg='#333333', bd=0, activebackground='#555555', activeforeground='#ffffff', padx=4, pady=0, state=DISABLED, command=self.save_file) self.save_btn.grid(row=0, column=2, padx=0) self.save_btn.bind('<Enter>', lambda x: self.hint_lbl.config(text='Save')) self.save_btn.bind('<Leave>', lambda x: self.hint_lbl.config(text='')) self.save_btn.bind('<Motion>', self.save_btn_handler) self.close_btn = Button(self.file_tool_frm) self.close_btn.config(text='\u2717', font=('Sans', '12', 'bold'), fg='#eeeeee', bg='#333333', bd=0, activebackground='#555555', activeforeground='#ffffff', padx=4, pady=0, command=self.close_tab) self.close_btn.grid(row=0, column=3, padx=20) self.close_btn.bind('<Enter>', lambda x: self.hint_lbl.config(text='Close')) self.close_btn.bind('<Leave>', lambda x: self.hint_lbl.config(text='')) self.undo_btn = Button(self.edit_tool_frm) self.undo_btn.config(text='\u21b6', font=('Sans', '12'), fg='#eeeeee', bg='#333333', bd=0, activebackground='#555555', activeforeground='#ffffff', padx=4, pady=0, command=self.undo) self.undo_btn.grid(row=0, column=0) self.undo_btn.bind('<Enter>', lambda x: self.hint_lbl.config(text='Undo')) self.undo_btn.bind('<Leave>', lambda x: self.hint_lbl.config(text='')) self.redo_btn = Button(self.edit_tool_frm) self.redo_btn.config(text='\u21b7', font=('Sans', '12'), fg='#eeeeee', bg='#333333', bd=0, activebackground='#555555', activeforeground='#ffffff', padx=4, pady=0, command=self.redo) self.redo_btn.grid(row=0, column=1, padx=20) self.redo_btn.bind('<Enter>', lambda x: self.hint_lbl.config(text='Redo')) self.redo_btn.bind('<Leave>', lambda x: self.hint_lbl.config(text='')) self.quit_btn = Button(self.toolbar) self.quit_btn.config(text='Quit', font=('Sans', '10'), fg='#eeeeee', bg='#333333', activebackground='#647899', activeforeground='#ffffff', bd=0, padx=4, pady=2, command=self.quit_from_app) self.quit_btn.pack(side=RIGHT) self.statusbar_col_lbl = Label(self.statusbar) self.statusbar_col_lbl.config(text='col: 1', fg='#ffffff', bg='#222222', activeforeground='#ffffff', activebackground='#222222', bd=0, font=('Sans', '11', 'italic'), padx=10) self.statusbar_col_lbl.pack(side=RIGHT) self.statusbar_line_lbl = Label(self.statusbar) self.statusbar_line_lbl.config(text='ln: 1', fg='#ffffff', bg='#222222', bd=0, padx=10, activeforeground='#ffffff', activebackground='#222222', font=('Sans', '11', 'italic')) self.statusbar_line_lbl.pack(side=RIGHT) self.tab = self.TextFrameTab(self) # Create the '<<StateChecking>>' virtual event self.event_add('<<StateChecking>>', '<Any-KeyPress>', '<Any-KeyRelease>', '<Any-ButtonRelease>', '<FocusIn>', '<FocusOut>') # Create event bindings for keyboard shortcuts self.bind_all('<Control-n>', self.create_new_doc) self.bind_all('<Control-o>', self.open_file) self.bind_all('<Control-w>', self.close_tab) self.bind_all('<Control-s>', self.save_file) self.bind_all('<Control-Shift-S>', self.save_as_file) self.bind_all('<Control-q>', self.quit_from_app) self.bind_all('<Control-a>', self.select_all) if sys.platform.startswith('win32'): self.bind_all('<Control-Shift-Z>', self.redo) def save_btn_handler(self, event): try: textwidget = self.focus_lastfor() current_index = self.notebook.index('current') if textwidget.edit_modified(): self.save_btn.config(state=NORMAL) # Add an asterisk to the header of the tab self.notebook.tab(current_index, text='*' + self.filenames[current_index]) else: self.save_btn.config(state=DISABLED) # If there is an asterisk at the header of the tab, # remove it self.notebook.tab(current_index, text=self.filenames[current_index]) except TclError: pass def show_progress(self, proc, fpath): """ Shows a message on the status bar about saving or downloading process """ label = Label(self.statusbar) msg = "{} {}... ".format(proc, fpath) label.config(text=msg, fg='#ffffff', bg='#222222', bd=0, font=('Sans', '9', 'italic')) label.pack(side=LEFT) timer = threading.Timer(3.0, label.destroy) timer.start() def create_new_doc(self, event=None): # 'event' is the '<Control-n>' probable event self.filenames.append('Untitled') self.TextFrameTab(self) def open_file(self, event=None): # If there is the '<Control-o>' event if event: textwidget = self.focus_lastfor() textwidget.edit_modified(arg=False) filepath = askopenfilename(filetypes=(('All files', '*'), )) if filepath: p = Path(filepath) filename = p.parts[-1] try: with open(filepath) as file: if self.notebook.index('end') > 0: textwidget = self.focus_lastfor() modified = textwidget.edit_modified() current_index = self.notebook.index('current') if (self.filenames[current_index] == 'Untitled' and not modified): self.filepaths[current_index] = filepath self.filenames[current_index] = filename textwidget.insert(1.0, file.read()) textwidget.edit_modified(arg=False) self.notebook.tab('current', text=filename) else: self.filepaths[current_index + 1] = filepath self.filenames.append(filename) tab = self.TextFrameTab(self) tab.text.insert(1.0, file.read()) tab.text.edit_modified(arg=False) self.notebook.tab('current', text=filename) else: self.filenames.append(filename) tab = self.TextFrameTab(self) tab.text.insert(1.0, file.read()) tab.text.edit_modified(arg=False) self.notebook.tab(0, text=filename) self.filepaths[0] = filepath except UnicodeDecodeError: msg = "Unknown encoding!".format(filename) showerror(message=msg) self.close_tab() self.create_new_doc() thread = threading.Thread(target=self.show_progress, args=('Downloading', filepath)) thread.start() def close_tab(self, event=None): # 'event' is the '<Control-w>' probable event def close(obj): try: current_index = self.notebook.index('current') del obj.filenames[current_index] del obj.textwidgets[current_index] if current_index in obj.filepaths: del obj.filepaths[current_index] obj.notebook.forget(current_index) except TclError: pass if self.notebook.index('end') > 0: textwidget = self.focus_lastfor() modified = textwidget.edit_modified() if modified: curr_index = self.notebook.index('current') msg = "'{}' has been modified. Do you want " \ "to save changes?".format(self.filenames[curr_index]) msgbox = Message(type=YESNOCANCEL, message=msg, icon=QUESTION) answer = msgbox.show() if answer == YES: self.save_file() close(self) elif answer == NO: close(self) else: close(self) def save_file(self, event=None): # 'event' is the '<Control-s>' probable event if self.notebook.index('end') > 0: current_index = self.notebook.index('current') if current_index in self.filepaths: filepath = self.filepaths[current_index] textwidget = self.focus_lastfor() text = textwidget.get(1.0, END) with open(filepath, 'w') as file: file.write(text) self.file_menu.entryconfigure(4, state=DISABLED) textwidget.edit_modified(arg=False) thread = threading.Thread(target=self.show_progress, args=('Saving', filepath)) thread.start() else: self.save_as_file() def save_as_file(self, event=None): # 'event' is the '<Control-Shift-S>' probable event if self.notebook.index('end') > 0: filepath = asksaveasfilename() if filepath: p = Path(filepath) filename = p.parts[-1] current_index = self.notebook.index('current') self.filepaths[current_index] = filepath self.notebook.tab('current', text=filename) self.filenames[current_index] = filename textwidget = self.focus_lastfor() text = textwidget.get(1.0, END) with open(filepath, 'w') as file: file.write(text) self.file_menu.entryconfigure(4, state=DISABLED) textwidget.edit_modified(arg=False) thread = threading.Thread(target=self.show_progress, args=('Saving', filepath)) thread.start() def undo(self): try: textwidget = self.focus_lastfor() textwidget.edit_undo() except TclError: pass def redo(self, event=None): # 'event' is the '<Control-Shift-Z>' probable event for Windows try: textwidget = self.focus_lastfor() textwidget.edit_redo() except TclError: pass def cut_text(self): try: textwidget = self.focus_lastfor() text = textwidget.get(SEL_FIRST, SEL_LAST) textwidget.delete(SEL_FIRST, SEL_LAST) textwidget.clipboard_clear() textwidget.clipboard_append(text) except TclError: pass def copy_text(self): try: textwidget = self.focus_lastfor() text = textwidget.get(SEL_FIRST, SEL_LAST) textwidget.clipboard_clear() textwidget.clipboard_append(text) except TclError: pass def paste_text(self): try: textwidget = self.focus_lastfor() text = textwidget.selection_get(selection='CLIPBOARD') textwidget.insert(INSERT, text) except TclError: pass def del_text(self): try: textwidget = self.focus_lastfor() textwidget.delete(SEL_FIRST, SEL_LAST) except TclError: pass def select_all(self, event=None): # 'event' is the '<Control-a>' probable event textwidget = self.focus_lastfor() textwidget.tag_add(SEL, 1.0, END) def show_about(self): txt_0 = "PyTEd 0.1.0" txt_1 = "PyTEd is a simple text editor based\n" \ "on the tkinter interface" txt_2 = "Copyright " + '\u00a9' + " 2019 Eugene Kapshuk" txt_3 = "Source code: " txt_4 = "https://github.com/eukap/pyted" txt_5 = "License: MIT" window = Toplevel() window.title('About PyTEd') window.geometry('350x280') window.transient(self) window.resizable(width=False, height=False) label_0 = Label(window) label_0.config(fg='#000000', bg='#ddddd8', text=txt_0, font=('Sans', '12', 'bold'), justify=CENTER, pady=10) label_0.pack(side=TOP, fill=X) label_1 = Label(window) label_1.config(fg='#000000', bg='#ddddd8', text=txt_1, font=('Sans', '11', 'normal'), justify=CENTER, pady=10) label_1.pack(side=TOP, fill=X) label_2 = Label(window) label_2.config(fg='#000000', bg='#ddddd8', text=txt_2, font=('Sans', '11', 'normal'), justify=CENTER, pady=10) label_2.pack(side=TOP, fill=X) frame_0 = Frame(window) frame_0.config(bg='#ddddd8', bd=0, relief=FLAT, padx=2, pady=2) frame_0.pack(side=TOP, fill=X) label_3 = Label(frame_0) label_3.config(fg='#000000', bg='#ddddd8', text=txt_3, font=('Sans', '11', 'normal'), justify=CENTER, padx=2, pady=10) label_3.pack(side=LEFT, fill=X) label_4 = Label(frame_0) label_4.config(fg='#1111cc', bg='#ddddd8', text=txt_4, font=('Sans', '11', 'normal'), cursor='hand1', justify=CENTER, padx=4, pady=0) label_4.pack(side=LEFT, fill=X) label_4.bind( '<Button-1>', lambda x: webbrowser.open_new_tab( 'https://github.com/eukap/pyted')) label_5 = Label(window) label_5.config(fg='#000000', bg='#ddddd8', text=txt_5, font=('Sans', '11', 'normal'), justify=CENTER, pady=8) label_5.pack(side=TOP, fill=X) frame_1 = Frame(window) frame_1.config(bg='#ddddd8', bd=0, relief=FLAT, padx=0, pady=12) frame_1.pack(side=BOTTOM, fill=BOTH, expand=YES) btn = Button(frame_1) btn.config(text='Close', fg='#000000', bg='#efefef', activeforeground='#000000', activebackground='#e9e9e9', font=('Sans', '10', 'normal'), command=window.destroy) btn.pack(side=TOP) def quit_from_app(self, event=None): # 'event' is the '<Control-q>' probable event modified = False for widget in self.textwidgets: if widget.edit_modified(): modified = True break if modified: msg = "Some changes haven't been saved. " \ "Do you really want to exit?" msgbox = Message(type=YESNO, message=msg, icon=QUESTION) answer = msgbox.show() if answer == YES: self.quit() else: self.quit()
class App(Tk): def __init__(self, args): Tk.__init__(self) self.height = '720' self.width = '1280' self.Editors = [] self.Filenames = {} self.lexer_selector = self.createLexers() self.Lexers = dict() self._icon = Image("photo", file='img/notebook/icon.png') self.code_font = Font(family="Latin Modern Mono", size=15) self.title_font = Font(family="Ubuntu Mono", size=15) self.underline_font = Font(underline=True) self.MainWindow = Frame(self, background='#282c34') self.notebook = Notebook(self) self.configureUI() self.args = args self.awk_img = PhotoImage(file='img/fext/awk.png') self.bash_img = PhotoImage(file='img/fext/bash.png') self.c_img = PhotoImage(file='img/fext/c.png') self.cs_img = PhotoImage(file='img/fext/c#.png') self.cmake_img = PhotoImage(file='img/fext/cmake.png') self.coffee_img = PhotoImage(file='img/fext/coffee.png') self.cpp_img = PhotoImage(file='img/fext/cpp.png') self.css_img = PhotoImage(file='img/fext/css.png') self.delphi_img = PhotoImage(file='img/fext/delphi.png') self.eiffel_img = PhotoImage(file='img/fext/eiffel.png') self.erlang_img = PhotoImage(file='img/fext/erlang.png') self.fs_img = PhotoImage(file='img/fext/f#.png') self.fortran_img = PhotoImage(file='img/fext/fortran.png') self.go_img = PhotoImage(file='img/fext/golang.png') self.haskell_img = PhotoImage(file='img/fext/haskell.png') self.html_img = PhotoImage(file='img/fext/html.png') self.java_img = PhotoImage(file='img/fext/java.png') self.js_img = PhotoImage(file='img/fext/js.png') self.json_img = PhotoImage(file='img/fext/json.png') self.kotlin_img = PhotoImage(file='img/fext/kotlin.png') self.lisp_img = PhotoImage(file='img/fext/lisp.png') self.makefile_img = PhotoImage(file='img/fext/makefile.png') self.matlab_img = PhotoImage(file='img/fext/matlab.png') self.mysql_img = PhotoImage(file='img/fext/mysql.png') self.perl_img = PhotoImage(file='img/fext/perl.png') self.php_img = PhotoImage(file='img/fext/php.png') self.prolog_img = PhotoImage(file='img/fext/prolog.png') self.python_img = PhotoImage(file='img/fext/python.png') self.ruby_img = PhotoImage(file='img/fext/ruby.png') self.sass_img = PhotoImage(file='img/fext/sass.png') self.scala_img = PhotoImage(file='img/fext/scala.png') self.swift_img = PhotoImage(file='img/fext/swift.png') self.tcl_img = PhotoImage(file='img/fext/tcl.png') self.ts_img = PhotoImage(file='img/fext/ts.png') self.txt_img = PhotoImage(file='img/fext/txt.png') self.verilog_img = PhotoImage(file='img/fext/verilog.png') self.vhdl_img = PhotoImage(file='img/fext/vhdl.png') self.xml_img = PhotoImage(file='img/fext/xml.png') self.yaml_img = PhotoImage(file='img/fext/yaml.png') for file_name in self.args: self.openFileByName(file_name) self.mainloop() def configureUI(self): self.configureWindow() self.configureMenu() self.configureNotebook() self.contextualMenu() self.bindWindowEvents() self.configureStatusBar() def configureWindow(self): self.geometry(self.width+'x'+self.height+'+0+0') self.title("Quode-IDE") self.wm_iconphoto(False, self._icon) self.MainWindow.pack() def configureMenu(self): self.menu_bar = Menu(self, background='#21252b', foreground='#ffffff') self.file_menu = Menu(self.menu_bar, tearoff=0) self.file_menu.add_command(label='New Tab', font=self.title_font, command=self.createNewEditor, activebackground='#123456', activeforeground='#ffffff', accelerator='Ctrl + N') self.file_menu.add_command(label="Open", font=self.title_font, command=self.openFile, activebackground='#123456', activeforeground='#ffffff', accelerator='Ctrl + O') self.file_menu.add_command(label="Save", font=self.title_font, command=self.saveFile, activebackground='#123456', activeforeground='#ffffff', accelerator='Ctrl + S') self.file_menu.add_command(label='Remove Tab', font=self.title_font, command=self.removeCurrentEditor, activebackground='#123456', activeforeground='#ffffff', accelerator='Ctrl + R') self.file_menu.add_separator() self.file_menu.add_command(label="Exit", font=self.title_font, command=self.askQuit, activebackground='#123456', activeforeground='#ffffff', accelerator='Ctrl + Q') self.menu_bar.add_cascade(label="File", font=self.title_font, activebackground='#123456', activeforeground='#ffffff', menu=self.file_menu) self.edit_menu = Menu(self.menu_bar, tearoff=0, activebackground='#123456') self.edit_menu.add_command(label="Cut", font=self.title_font, command=self.cutSelection, activebackground='#123456', activeforeground='#ffffff', accelerator="Ctrl + X") self.edit_menu.add_command(label="Copy", font=self.title_font, command=self.copySelection, activebackground='#123456', activeforeground='#ffffff', accelerator="Ctrl + C") self.edit_menu.add_command(label="Paste", font=self.title_font, command=self.pasteSelection, activebackground='#123456', activeforeground='#ffffff', accelerator="Ctrl + V") self.edit_menu.add_command(label="Replace (All)", font=self.title_font, activebackground='#123456', activeforeground='#ffffff', command=self.replaceAllWindow) self.menu_bar.add_cascade(label="Edit", font=self.title_font, activebackground='#123456', activeforeground='#ffffff', menu=self.edit_menu) self.view_menu = Menu(self.menu_bar, tearoff=0) self.view_menu.add_command(label="Increase Font Size", font=self.title_font, command=self.increaseFontSize, activebackground='#123456', activeforeground='#ffffff', accelerator="Ctrl + +/=") self.view_menu.add_command(label="Decrease Font Size", font=self.title_font, command=self.decreaseFontSize, activebackground='#123456', activeforeground='#ffffff', accelerator="Ctrl + -/_") self.view_menu.add_command(label="Find", font=self.title_font, command=self.searchWindow, activebackground='#123456', activeforeground='#ffffff', accelerator="Ctrl + f") self.view_menu.add_command(label="File tree", font=self.title_font, command=self.showFileTree, activebackground='#123456', activeforeground='#ffffff', accelerator="Ctrl + t") self.menu_bar.add_cascade(label="View", font=self.title_font, activebackground='#123456', activeforeground='#ffffff', menu=self.view_menu) self.run_menu = Menu(self.menu_bar, tearoff=0) self.run_menu.add_command(label='Run', activebackground='#123456', activeforeground='#ffffff', command=self.runCurrentEditor, font=self.title_font) self.menu_bar.add_cascade(label='Run', activebackground='#123456', activeforeground='#ffffff', font=self.title_font, menu=self.run_menu) self.help_menu = Menu(self.menu_bar, tearoff=0) self.help_menu.add_command(label="About", activebackground='#123456', activeforeground='#ffffff', font=self.title_font, command=self.printAbout) self.menu_bar.add_cascade(label="Help", font=self.title_font, activebackground='#123456', activeforeground='#ffffff', menu=self.help_menu) self.config(menu=self.menu_bar) def configureNotebook(self): self.notebook.place_configure(relx=0, rely=0, relheight=0.97, relwidth=1) self.notebook.enable_traversal() def configureStatusBar(self): self.status_bar_label = Label(self) self.status_bar_label.place_configure(relx=0, rely=0.97, relheight=0.03, relwidth=1) self.status_bar_label.config(text=self.getStatusText()) def langImg(self, ext): img = None if (ext == '.c'): img = self.c_img elif(ext == '.h'): img = self.c_img elif(ext == '.cpp'): img = self.cpp_img elif(ext == '.hpp'): img = self.cpp_img elif(ext == '.css'): img = self.css_img elif(ext == '.sass'): img = self.sass_img elif(ext == '.yaml'): img = self.yaml_img elif(ext == '.yml'): img = self.yaml_img elif(ext == '.json'): img = self.json_img elif(ext == '.cs'): img = self.cs_img elif(ext == '.fs'): img = self.fs_img elif(ext == '.e'): img = self.eiffel_img elif(ext == '.erl'): img = self.erlang_img elif(ext == '.hrl'): img = self.erlang_img elif(ext == '.es'): img = self.erlang_img elif(ext == '.f03'): img = self.fortran_img elif(ext == '.f90'): img = self.fortran_img elif(ext == '.F03'): img = self.fortran_img elif(ext == '.F90'): img = self.fortran_img elif(ext == '.go'): img = self.go_img elif(ext == '.hs'): img = self.haskell_img elif(ext == '.v'): img = self.verilog_img elif(ext == '.vhdl'): img = self.vhdl_img elif(ext == '.vhd'): img = self.vhdl_img elif(ext == '.html'): img = self.html_img elif(ext == '.htm'): img = self.html_img elif(ext == '.xhtml'): img = self.html_img elif(ext == '.xml'): img = self.xml_img elif(ext == '.js'): img = self.js_img elif(ext == '.tex'): img = self.ts_img elif(ext == '.coffee'): img = self.coffee_img elif(ext == '.java'): img = self.java_img elif(ext == '.scala'): img = self.scala_img elif(ext == '.kt'): img = self.kotlin_img elif(ext == '.ktm'): img = self.kotlin_img elif(ext == '.kts'): img = self.kotlin_img elif(ext == '.lisp'): img = self.lisp_img elif(ext == 'make'): img = self.makefile_img elif(ext == 'Make'): img = self.makefile_img elif(ext == 'cmake'): img = self.cmake_img elif(ext == 'CMake'): img = self.cmake_img elif(ext == '.m'): img = self.matlab_img elif(ext == '.mat'): img = self.matlab_img elif(ext == '.dpr'): img = self.delphi_img elif(ext == '.perl'): img = self.perl_img elif(ext == '.php'): img = self.php_img elif(ext == '.pr'): img = self.prolog_img elif(ext == '.py'): img = self.python_img elif(ext == '.rb'): img = self.ruby_img elif(ext == '.sh'): img = self.bash_img elif(ext == '.sql'): img = self.mysql_img elif(ext == '.mysql'): img = self.mysql_img elif(ext == '.tcl'): img = self.tcl_img elif(ext == '.awk'): img = self.awk_img else: img = self.txt_img return img def _get_current_editor(self): try: index = self.notebook.index('current') curr_editor = self.Editors[index] return curr_editor except _tkinter.TclError: messagebox.showerror('Error', 'No Editors Opened!!') return None def createNewEditor(self): new_tab_name = askstring('New Tab', 'Enter name of new tab') if new_tab_name: f_name, ext = os.path.splitext(new_tab_name) scrolled_text = ScrolledText(font=self.code_font, undo=True, tabs=('2c'), background='#282c34', insertbackground='#ffffff', foreground='#abb2a4') self.Editors.append(scrolled_text) self.Filenames[scrolled_text] = None try : lexer = self.lexer_selector[ext] except KeyError: lexer = None self.Lexers[scrolled_text] = lexer self.notebook.add(scrolled_text, image=self.langImg(ext), text=os.path.split(new_tab_name)[1], compound='left') self.notebook.select(scrolled_text) self.createTags() self.recolorize(None) self.setStatusText(self.getStatusText()) self.miscBindings(scrolled_text) scrolled_text.focus_set() scrolled_text.edit_reset() def openFile(self): opened_file_name = fd.askopenfilename(initialdir=".", title="Select file", filetypes=(('all files', '*.*'),)) if not isinstance(opened_file_name, tuple): ext = os.path.splitext(opened_file_name)[1] scrolled_text = ScrolledText(font=self.code_font, undo=True, tabs=('2c'), background='#282c34', insertbackground='#ffffff', foreground='#abb2a4') self.Editors.append(scrolled_text) self.Filenames[scrolled_text] = opened_file_name self.notebook.add(scrolled_text, image=self.langImg(ext), text=os.path.split(opened_file_name)[1], compound='left') with open(opened_file_name) as f: file_text = f.read() f.close() scrolled_text.insert('end', file_text) self.notebook.select(scrolled_text) try : lexer = self.lexer_selector[ext] except KeyError: lexer = None self.Lexers[scrolled_text] = lexer self.createTags() self.recolorize(None) scrolled_text.focus_set() scrolled_text.edit_reset() self.miscBindings(scrolled_text) self.setStatusText(self.getStatusText()) def openFileByName(self, path): opened_file_name = path if opened_file_name[0] != '/': opened_file_name = os.path.dirname(os.path.abspath(__file__)) + '/' + path if not os.path.isdir(opened_file_name): ext = os.path.splitext(opened_file_name)[1] scrolled_text = ScrolledText(font=self.code_font, undo=True, tabs=('1.28c'), background='#282c34', insertbackground='#ffffff', foreground='#abb2a4') self.Editors.append(scrolled_text) self.Filenames[scrolled_text] = opened_file_name print(opened_file_name) self.notebook.add(scrolled_text, image=self.langImg(ext), text=os.path.split(opened_file_name)[1], compound='left') with open(opened_file_name) as f: file_text = f.read() f.close() scrolled_text.insert('end', file_text) self.notebook.select(scrolled_text) try: lexer = self.lexer_selector[ext] except KeyError: lexer = None self.Lexers[scrolled_text] = lexer self.createTags() self.recolorize(None) scrolled_text.focus_set() scrolled_text.edit_reset() self.miscBindings(scrolled_text) self.setStatusText(self.getStatusText()) def saveFile(self): try: editor_index = self.notebook.index('current') curr_editor = self.Editors[editor_index] curr_text = curr_editor.get("1.0", "end") if self.Filenames[curr_editor] is not None: with open(self.Filenames[curr_editor], 'r+') as f: f.write(curr_text) messagebox.showinfo('Save', 'File Saved!!!') curr_editor.edit_reset() else: new_file_path = fd.asksaveasfilename(initialdir='.', filetypes=[("All files", "*")]) if not isinstance(new_file_path, tuple): self.Filenames[curr_editor] = new_file_path new_file = open(new_file_path, 'w+') new_file.close() new_file = open(new_file_path, 'r+') new_file.write(curr_text) messagebox.showinfo('Save', 'File Saved!!!') self.Filenames[curr_editor] = new_file_path curr_editor.edit_reset() except _tkinter.TclError: messagebox.showerror("Save File", "No file to save") def removeCurrentEditor(self): try: curr_editor = self._get_current_editor() if messagebox.askyesno('Remove Current Tab', 'Are you sure you want to remove this Editor?', icon='warning'): index = self.notebook.select('current') self.notebook.forget(index) self.Editors.remove(curr_editor) self.Filenames.pop(curr_editor, None) self.Lexers.pop(curr_editor, None) except _tkinter.TclError: messagebox.showerror('Remove Tab', 'Oops!! No tabs to remove!!') def askQuit(self): if messagebox.askyesno('Remove Current Tab', 'Do you really wanna Exit?', icon='warning'): self.destroy() def bindWindowEvents(self): self.bind('<Control-n>', func=lambda e:self.createNewEditor()) self.bind('<Control-o>', func=lambda e:self.openFile()) self.bind('<Control-s>', func=lambda e:self.saveFile()) self.bind('<Control-r>', func=lambda e:self.removeCurrentEditor()) self.bind('<Control-q>', func=lambda e:self.askQuit()) self.bind('<Control-x>', func=lambda e:self.cutSelection()) self.bind('<Control-c>', func=lambda e:self.copySelection()) self.bind('<Control-v>', func=lambda e:self.pasteSelection()) self.bind('<Control-a>', func=lambda e:self.selectAll()) self.bind('<Control-f>', func=lambda e:self.searchWindow()) self.bind('<Control-z>', func=lambda e:self.undoChange()) self.bind('<Control-y>', func=lambda e:self.redoChange()) self.bind('<Control-t>', func=lambda e:self.showFileTree()) self.bind('<Control-N>', func=lambda e:self.createNewEditor()) self.bind('<Control-O>', func=lambda e:self.openFile()) self.bind('<Control-S>', func=lambda e:self.saveFile()) self.bind('<Control-R>', func=lambda e:self.removeCurrentEditor()) self.bind('<Control-Q>', func=lambda e:self.askQuit()) self.bind('<Control-X>', func=lambda e:self.cutSelection()) self.bind('<Control-C>', func=lambda e:self.copySelection()) self.bind('<Control-V>', func=lambda e:self.pasteSelection()) self.bind('<Control-A>', func=lambda e:self.selectAll()) self.bind('<Control-F>', func=lambda e:self.searchWindow()) self.bind('<Control-Z>', func=lambda e:self.undoChange()) self.bind('<Control-Y>', func=lambda e:self.redoChange()) self.bind('<Control-T>', func=lambda e:self.showFileTree()) self.bind('<Control-plus>', func=lambda e:self.increaseFontSize()) self.bind('<Control-minus>', func=lambda e:self.decreaseFontSize()) self.bind('<Control-KP_Add>', func=lambda e:self.increaseFontSize()) self.bind('<Control-KP_Subtract>', func=lambda e:self.decreaseFontSize()) self.bind("<Key>", func=lambda e:self.anyKeyBindings(e)) # self.bind("<Key>", func=lambda e:self.setStatusText(self.getStatusText())) self.bind('<Button>', func=lambda e: self.anyButtonBindings(e)) self.bind('<Button-3>', func=lambda e: self.contextual_menu.post(e.x_root, e.y_root)) def anyKeyBindings(self, event): self.recolorize(None) self.setStatusText(self.getStatusText()) def anyButtonBindings(self, event): self.contextual_menu.unpost() self.setStatusText(self.getStatusText()) def cutSelection(self): curr_editor = self._get_current_editor() curr_editor.event_generate('<<Cut>>') def copySelection(self): curr_editor = self._get_current_editor() curr_editor.event_generate('<<Copy>>') def pasteSelection(self): curr_editor = self._get_current_editor() curr_editor.event_generate('<<Paste>>') def contextualMenu(self): self.contextual_menu = Menu(self, tearoff=False) self.contextual_menu.add_command(label='New Editor', command=self.createNewEditor) self.contextual_menu.add_command(label='Open File', command=self.openFile) self.contextual_menu.add_command(label='Save Editor', command=self.saveFile) self.contextual_menu.add_separator() self.contextual_menu.add_command(label='Remove Editor', command=self.removeCurrentEditor) self.contextual_menu.add_command(label='Change title', command=self.changeEditorTitle) def unpostContextMenu(self): self.contextual_menu.unpost() def changeEditorTitle(self): curr_editor = self._get_current_editor() new_title = askstring('New Editor', "Enter new title") new_ext = os.path.splitext(new_title)[1] self.notebook.tab(curr_editor, text=new_title, image=self.langImg(new_ext)) try : new_lexer = self.lexer_selector[new_ext] except KeyError: new_lexer = None self.Lexers[curr_editor] = new_lexer self.recolorize(None) def increaseFontSize(self): curr_editor = self._get_current_editor() # FIXME curr_font = Font(curr_editor, curr_editor.cget("font")) curr_size = curr_font.cget('size') new_size = curr_size+1 curr_font.configure(size=new_size) new_font = curr_font curr_editor.configure(font=new_font) # print(Font(curr_editor, curr_editor.cget("font")).cget("size")) def decreaseFontSize(self): curr_editor = self._get_current_editor() # FIXME curr_font = Font(curr_editor, curr_editor.cget("font")) curr_size = curr_font.cget('size') if curr_size > 1: new_size = curr_size-1 curr_font.configure(size=new_size) def undoChange(self): try: curr_editor = self._get_current_editor() curr_editor.edit_undo() self.createTags() self.recolorize(None) except _tkinter.TclError: pass def redoChange(self): try: curr_editor = self._get_current_editor() curr_editor.edit_redo() except _tkinter.TclError: pass def selectAll(self): curr_editor = self._get_current_editor() curr_editor.tag_add('sel', '1.0', 'end') return "break" def searchWindow(self): self.search_window = SearchWindow(self, 0) def removeAllTags(self): curr_editor = self._get_current_editor() for tag in curr_editor.tag_names(): curr_editor.tag_delete(tag) def replaceAllWindow(self): self.replace_window = ReplaceTextWindow(self) def replaceAll(self, a, b): curr_editor = self._get_current_editor() start = "1.0" text = curr_editor.get(start, "end") if curr_editor: pos = curr_editor.search(a, start, stopindex="end") while pos: length = len(text) row, col = pos.split('.') end = int(col) + length end = row + '.' + str(end) curr_editor.tag_add('found', pos, end) start = end pos = curr_editor.search(a, start, stopindex="end") replaced = 0 if a: coordinates = [] index_list = list(curr_editor.tag_ranges("found")) index_list.reverse() while index_list: coordinates.append([index_list.pop(), index_list.pop()]) for start, end in coordinates: curr_editor.delete(start, end) curr_editor.insert(start, b) replaced += 1 curr_editor.tag_delete("found") return replaced def createTags(self): curr_editor = self._get_current_editor() bold_font = Font(curr_editor, curr_editor.cget("font")) bold_font.configure(weight='bold') italic_font = Font(curr_editor, curr_editor.cget("font")) italic_font.configure(slant='italic') bold_italic_font = Font(curr_editor, curr_editor.cget("font")) bold_italic_font.configure(weight='bold', slant='italic') style = get_style_by_name('default') for ttype, ndef in style: tag_font = None if ndef['bold'] and ndef['italic']: tag_font = bold_italic_font elif ndef['bold']: tag_font = bold_font elif ndef['italic']: tag_font = italic_font if ndef['color']: foreground = "#%s" % ndef['color'] else: foreground = None curr_editor.tag_configure(str(ttype), foreground=foreground, font=tag_font) def recolorize(self, event): if len(self.Editors) != 0: curr_editor = self._get_current_editor() code = curr_editor.get("1.0", "end-1c") lexer = self.Lexers[curr_editor] if lexer is not None: tokensource = lexer.get_tokens(text=code) start_line = 1 start_index = 0 end_line = 1 end_index = 0 for ttype, value in tokensource: if "\n" in value: end_line += value.count("\n") end_index = len(value.rsplit("\n", 1)[1]) else: end_index += len(value) if value not in (" ", "\n"): index1 = "%s.%s" % (start_line, start_index) index2 = "%s.%s" % (end_line, end_index) for tagname in curr_editor.tag_names(index1): curr_editor.tag_remove(tagname, index1, index2) curr_editor.tag_add(str(ttype), index1, index2) start_line = end_line start_index = end_index # self.underlineComplement() def createLexers(self): lex = {} lex['.c'] = CFamilyLexer() lex['.h'] = CFamilyLexer() lex['.cpp'] = CppLexer() lex['.hpp'] = CppLexer() lex['.css'] = CssLexer() lex['.sass'] = SassLexer() lex['.yaml'] = YamlLexer() lex['.yml'] = YamlLexer() lex['.json'] = JsonLexer() lex['.cs'] = CSharpLexer() lex['.fs'] = FSharpLexer() lex['.e'] = EiffelLexer() lex['.erl'] = ErlangLexer() lex['.hrl'] = ErlangLexer() lex['.es'] = ErlangLexer() lex['.f03'] = FortranLexer() lex['.f90'] = FortranLexer() lex['.F03'] = FortranLexer() lex['.F90'] = FortranLexer() lex['.go'] = GoLexer() lex['.hs'] = HaskellLexer() lex['.v'] = VerilogLexer() lex['.vhdl'] = VhdlLexer() lex['.vhd'] = VhdlLexer() lex['.html'] = HtmlLexer() lex['.htm'] = HtmlLexer() lex['.xhtml'] = HtmlLexer() lex['.xml'] = XmlLexer() lex['.js'] = JavascriptLexer() lex['.tex'] = TypeScriptLexer() lex['.coffee'] = CoffeeScriptLexer() lex['.java'] = JavaLexer() lex['.scala'] = ScalaLexer() lex['.kt'] = KotlinLexer() lex['.ktm'] = KotlinLexer() lex['.kts'] = KotlinLexer() lex['.lisp'] = CommonLispLexer() lex['make'] = MakefileLexer() lex['Make'] = MakefileLexer() lex['CMake'] = CMakeLexer() lex['cmake'] = CMakeLexer() lex['.m'] = MatlabLexer() lex['.mat'] = MatlabLexer() lex['.dpr'] = DelphiLexer() lex['.perl'] = PerlLexer() lex['.php'] = PhpLexer() lex['.pr'] = PrologLexer() lex['.py'] = Python3Lexer() lex['.rb'] = RubyLexer() lex['.sh'] = BashLexer() lex['.sql'] = MySqlLexer() lex['.mysql'] = MySqlLexer() lex['.tcl'] = TclLexer() lex['.awk'] = AwkLexer() return lex def getStatusText(self): if len(self.Editors): curr_editor = self._get_current_editor() cursor_position = curr_editor.index('insert') if cursor_position is not None: row, col = cursor_position.split('.') return "Row: %s Col: %s"%(row, col) else: return "Quode-IDE" else: return "Quode-IDE" def setStatusText(self, text): if len(self.Editors): self.status_bar_label.config(text=text) else: self.status_bar_label.config(text="Quode-IDE") def miscBindings(self, scrolled_text): scrolled_text.bind('<Control-z>', func=self.undoChange) scrolled_text.bind('<Control-y>', func=self.redoChange) scrolled_text.bind('<KeyRelease-quoteright>', func=lambda e: self.completeQuoteright(scrolled_text)) scrolled_text.bind('<KeyRelease-quotedbl>', func=lambda e: self.completeQuotedbl(scrolled_text)) scrolled_text.bind('<KeyRelease-parenleft>', func=lambda e: self.completeParen(scrolled_text)) scrolled_text.bind('<KeyRelease-bracketleft>', func=lambda e: self.completeBracket(scrolled_text)) scrolled_text.bind('<KeyRelease-braceleft>', func=lambda e: self.completeBrace(scrolled_text)) scrolled_text.bind('<KeyRelease-less>', func=lambda e: self.completeAngles(scrolled_text)) scrolled_text.bind('<KeyRelease-Return>', func=lambda e: self.returnNewLine(scrolled_text)) scrolled_text.bind('<parenright>', func=lambda e: self.skipRParen(scrolled_text)) scrolled_text.bind('<bracketright>', func=lambda e: self.skipRBracket(scrolled_text)) scrolled_text.bind('<braceright>', func=lambda e: self.skipRBrace(scrolled_text)) scrolled_text.bind('<greater>', func=lambda e: self.skipRAngle(scrolled_text)) scrolled_text.bind('<BackSpace>', func=lambda e: self.erasePair(scrolled_text)) def completeParen(self, scrolled_text): scrolled_text.mark_gravity('insert', 'left') scrolled_text.insert('insert', ')') scrolled_text.mark_gravity('insert', 'right') self.recolorize(None) def completeBracket(self, scrolled_text): scrolled_text.mark_gravity('insert', 'left') scrolled_text.insert('insert', ']') scrolled_text.mark_gravity('insert', 'right') self.recolorize(None) def completeBrace(self, scrolled_text): scrolled_text.mark_gravity('insert', 'left') scrolled_text.insert('insert', '}') scrolled_text.mark_gravity('insert', 'right') self.recolorize(None) def completeQuoteright(self, scrolled_text): scrolled_text.mark_gravity('insert', 'left') scrolled_text.insert('insert', '\'') scrolled_text.mark_gravity('insert', 'right') self.recolorize(None) def completeQuotedbl(self, scrolled_text): scrolled_text.mark_gravity('insert', 'left') scrolled_text.insert('insert', '"') scrolled_text.mark_gravity('insert', 'right') self.recolorize(None) def completeAngles(self, scrolled_text): scrolled_text.mark_gravity('insert', 'left') scrolled_text.insert('insert', '>') scrolled_text.mark_gravity('insert', 'right') self.recolorize(None) def returnNewLine(self, scrolled_text): curr_editor = self._get_current_editor() cursor_position = curr_editor.index('insert') row_index = int(cursor_position.split('.')[0]) prev_row_index = str(row_index - 1) + '.0' prev_line = curr_editor.get(prev_row_index, prev_row_index + ' lineend') this_line = curr_editor.get(cursor_position + ' linestart', cursor_position + ' lineend') if(len(prev_line) > 1): left_char = prev_line[-1] new_line = ''.join(self.notAlphaLine(prev_line)) if len(this_line) > 0: right_char = this_line[0] if self._are_braces_paired(left_char, right_char): curr_editor.insert('insert', new_line + '\t') curr_editor.mark_gravity('insert', 'left') curr_editor.insert('insert', '\n\t') curr_editor.mark_gravity('insert', 'right') else: curr_editor.insert('insert', new_line) elif(len(prev_line) == 1): if(len(this_line) == 0): if (prev_line == '\t'): curr_editor.insert('insert', '\t') if(len(this_line) > 0): left_char = prev_line[0] right_char = this_line[0] if self._are_braces_paired(left_char, right_char): curr_editor.insert('insert', '\t') curr_editor.mark_gravity('insert', 'left') curr_editor.insert('insert', '\n') curr_editor.mark_gravity('insert', 'right') def _are_braces_paired(self, lchar, rchar): if(lchar == '(' and rchar == ')'): return True if(lchar == '[' and rchar == ']'): return True if(lchar == '{' and rchar == '}'): return True return False def notAlphaLine(self, line): new_line = [] line = ''.join(line) for char in line: if (char == ' ' or char == '\t'): new_line.append(char) else: return new_line return new_line def skipRParen(self, scrolled_text): try: cursor_position = scrolled_text.index('insert') row, col = map(int, cursor_position.split('.')) next_position = str(row) + '.' + str(col+1) if scrolled_text.get(cursor_position, next_position) == ')': scrolled_text.delete(cursor_position, next_position) except: pass def skipRBracket(self, scrolled_text): try: cursor_position = scrolled_text.index('insert') row, col = map(int, cursor_position.split('.')) next_position = str(row) + '.' + str(col+1) if scrolled_text.get(cursor_position, next_position) == ']': scrolled_text.delete(cursor_position, next_position) except: pass def skipRBrace(self, scrolled_text): try: cursor_position = scrolled_text.index('insert') row, col = map(int, cursor_position.split('.')) next_position = str(row) + '.' + str(col+1) if scrolled_text.get(cursor_position, next_position) == '}': scrolled_text.delete(cursor_position, next_position) except: pass def skipRAngle(self, scrolled_text): try: cursor_position = scrolled_text.index('insert') row, col = map(int, cursor_position.split('.')) next_position = str(row) + '.' + str(col+1) if scrolled_text.get(cursor_position, next_position) == '>': scrolled_text.delete(cursor_position, next_position) except: pass def erasePair(self, scrolled_text): try: cursor_position = scrolled_text.index('insert') row, col = map(int, cursor_position.split('.')) prev_position = str(row) + '.' + str(col-1) next_position = str(row) + '.' + str(col+1) curr_char = scrolled_text.get(cursor_position, next_position) prev_char = scrolled_text.get(prev_position, cursor_position) if curr_char == ')' and prev_char == '(': scrolled_text.delete(cursor_position, next_position) if curr_char == ']' and prev_char == '[': scrolled_text.delete(cursor_position, next_position) if curr_char == '}' and prev_char == '{': scrolled_text.delete(cursor_position, next_position) if curr_char == '>' and prev_char == '<': scrolled_text.delete(cursor_position, next_position) except: pass def runCurrentEditor(self): if len(self.Editors): curr_editor = self._get_current_editor() if self.Filenames[curr_editor] is not None: os.system('gnome-terminal --working-directory=%s' % os.path.dirname(self.Filenames[curr_editor])) else: messagebox.showerror(title="Could Not Open Terminal", message="Please save the File to run it") def showFileTree(self): if len(self.Editors): curr_editor = self._get_current_editor() directory = os.path.dirname(self.Filenames[curr_editor]) FileTree(curr_editor, directory) def printAbout(self): about = """ qqqqq dd ii ddddddd eeeeeee qqq qqq dd ii dd dd ee qqq qqq dd ii dd dd ee qqq qqq dd ii dd dd ee qqq qqq uu uu oo ddddd eeeee ii dd dd eeeeeee qqq qqq uu uu oo oo dd dd ee ee ii dd dd ee qqq qqq uu uu oo oo dd dd ee ee zzzzz ii dd dd ee qqq qqq uu uu oo oo dd dd eeeeeeeee ii dd dd ee qqq qqq uu uu oo oo dd dd ee ii dd dd ee qqqqq uuuu oo ddddd eeeeee ii dddddd eeeeeee qqqqqq Quode-IDE(Integrated Development Environment) Author : Yash Khasbage DISCLAIMER: Identifiers used in this software are purely fictious and bear no resembalance to any person living or dead. Any resembalace is purely co-incidental. No animals were harmed in coding of this software. """ print(about) messagebox.showinfo(title="About", message="Check terminal") def _get_int_row_col(self): curr_editor = self._get_current_editor() return list(map(int, curr_editor.index('insert').split('.'))) def _get_str_row_col(self): curr_editor = self._get_current_editor() return curr_editor.index('insert') def _str_to_int(self, pos): return list(map(int, pos.split('.'))) def _get_current_char(self): row, col = self._get_int_row_col() curr_pos = str(row)+'.'+str(col) next_pos = str(row)+'.'+str(col+1) curr_editor = self._get_current_editor() return curr_editor.get(curr_pos, next_pos) def _next_position(self, pos): row, col = list(map(int, pos.split('.'))) return '%d.%d'%(row, col+1) def underlineComplement(self): if len(self.Editors): pos = self._get_str_row_col() curr_char = self._get_current_char() if(curr_char == '('): self.underlineRParen(pos) elif(curr_char == ')'): self.underlineLParen(pos) elif(curr_char == '['): self.underlineRBracket(pos) elif(curr_char == ']'): self.underlineLBracket(pos) elif(curr_char == '{'): self.underlineRBrace(pos) elif(curr_char == '}'): self.underlineLBrace(pos) elif(curr_char == '<'): self.underlineRAngle(pos) elif(curr_char == '>'): self.underlineLAngle(pos) def underlineRParen(self, l_pos): return curr_editor = self._get_current_editor() text = curr_editor.get(l_pos, 'end') count = 0 index = 0 for i in range(len(text)): if(text[i] == ')' and count == 0): index = i elif(text[i] == '('): count += 1 elif(text[i] == ')'): count -= 1 j = 0 line = self._str_to_int(l_pos)[0] line_number = 0 start = l_pos end = '%d.end' % line j_prev = 0 while(index > j): j_prev = j j += len(curr_editor.get(start, end)) - 1 line_number += 1 start = "%d.0" % (line+line_number) r_paren_index = '%d.%d' % (line+line_number, index-j_prev) print("index", index) print("j", j) print("linenumber", line_number) print("r_paren_index", r_paren_index) curr_editor.tag_add('underline', l_pos, self._next_position(l_pos)) curr_editor.tag_add('underline', r_paren_index, self._next_position(r_paren_index)) curr_editor.tag_configure('underline', underline=True) def underlineLParen(self, r_pos): pass def underlineRBracket(self, l_pos): pass def underlineLBracket(self, r_pos): pass def underlineRbrace(self, l_pos): pass def underlineLBrace(self, r_pos): pass def underlineRAngle(self, l_pos): pass def underlineLAngle(self, r_pos): pass
class ViewResults(Observer, View): """Takes care of the presentation of the Flow diagram.""" def __init__(self, parent, col=0, row=0, root=None): super().__init__(parent, col=col, row=row, sticky=NSEW, scrollbars=False, root=root) self._notebook = Notebook(self._frame, name="nb") self._notebook.columnconfigure(0, weight=1) self._notebook.rowconfigure(0, weight=1) self._notebook.config() self._notebook.grid(sticky=NSEW) self._notebook.grid(sticky=NSEW) self._tabs = [] # type: List[ImageTab] self._results = None self._parent = parent self.image_overlay = np.empty(shape=(0, 0, 0), dtype=np.uint8) # TODO: Work around below should be fixed; need to create a tab first and delete it, or background is old image in constant scale. name_init = "initialization_image" self.add_to_tab(np.zeros((1, 1, 3), dtype=np.uint8), name_init) self.RemoveTab(name_init) self.__temp_data_queue = Queue( ) # used for update function to store data in # add custom event handler to let updates be taken care of in tk main loop parent.root.bind("<<ViewResults.Update>>", self.__update) self._notebook.bind("<<NotebookTabChanged>>", self.__on_tabchange) self.__last_selected_tab = None def __on_tabchange(self, event): try: tab_index = self._notebook.index(self._notebook.select()) except TclError as ex: return # there are no tabs yet if self.__last_selected_tab is not None: self.__last_selected_tab.on_tab_deselect() self.__last_selected_tab = self._tabs[tab_index] self.__last_selected_tab.on_tab_selected() def RemoveTab(self, name): for tab in self._tabs: if name is tab.GetName(): tab.destroy() self._tabs.remove(tab) def add_to_tab(self, npimage, name): if npimage is None: return # check if one exists; if so use that for tab in self._tabs: if tab.GetName() == name: tab.SetImage(npimage=npimage) return #create new tab one tab = ImageTab(name=name, notebook=self._notebook, parent=self) self._tabs.append(tab) self.add_to_tab(npimage=npimage, name=name) def __findAllImagesAndShow(self, flowblock_name): if self._results is None: return imageVars = self._results.FindAllOfType(ImageVar().name) for key, var in imageVars.items(): name = key.split(".")[0] if name == flowblock_name: self.add_to_tab(var.value, name) def __draw_all_drawables_until(self, flowblock_name=None): ''' Draws al drawable results to image_overlay. Images is as large as to fit all drawables. :param flowblock_name: draw until this block. :return: ''' results = self.get_controller().results.get_result_dict( ) # type: Dict[str,Dict[str,Var]] self.image_overlay = np.empty(shape=(0, 0, 0), dtype=np.uint8) logging.debug("Starting drawing of drawables...") for key, value in results.items(): for key2, value2 in value.items(): if type(value2) == list: for value3 in value2: self.image_overlay = value3.draw(self.image_overlay) else: self.image_overlay = value2.draw(self.image_overlay) logging.debug("Finished drawing of drawables.") def __update(self, event): self._results = self.get_controller().results.get_results_for_block() try: flowblock_name = self.__temp_data_queue.get_nowait( )["flowblock_name"] except Empty: flowblock_name = None if not flowblock_name is None: # redraw al drawables self.__draw_all_drawables_until(flowblock_name) # a flowblock has just updated, go and show all containing images self.__findAllImagesAndShow(flowblock_name) def Update(self, *args, **kwargs): self.__temp_data_queue.put_nowait( {"flowblock_name": kwargs.get("flowblock_name", None)}) self._parent.root.event_generate("<<ViewResults.Update>>")