class ChooseDifficultyView: def __init__(self, update_slider_range: Callable[[Event], None], draw_field: Callable[[Event], None]) -> None: # programs can only have one window, but can # create multiple "Toplevel"s (effectively new windows) self.window = Toplevel() self.window.title('HexSweeper - Choose Difficulty') self.window.geometry('400x473') self.window.bind('<Configure>', draw_field) # these statements allow the hexgrid (in col 1, row 3) # to stretch as the window is resized # stretch the second column horizontally: Grid.columnconfigure(self.window, 1, weight=1) # stretch the fourth row vertically: Grid.rowconfigure(self.window, 3, weight=1) Label(self.window, text='Board Size:').grid(row=0, column=0, sticky=W) # TkInter "Scale" objects are sliders # Default slider resolution/accuracy is 1 self.game_size_slider = Scale( self.window, # from_ because from is a Python keyword from_=2, to=15, orient=HORIZONTAL, command=update_slider_range) self.game_size_slider.grid(row=0, column=1, sticky=E + W) Label(self.window, text='Number of mines:') \ .grid(row=1, column=0, sticky=W) self.mine_count_slider = Scale(self.window, from_=1, to=315, orient=HORIZONTAL, command=draw_field) self.mine_count_slider.grid(row=1, column=1, sticky=E + W) self.canvas = Canvas(self.window, bg='white') self.canvas.grid( row=3, column=0, # span columns 0 and 1 columnspan=2, # resize with the window sticky=E + N + W + S) def close_window(self): self.window.destroy() def set_mine_count_slider_upper_bound(self, upper_bound): self.mine_count_slider.config(to=upper_bound)
class Slider(ScheduleMixin, DestroyMixin, EnableMixin, FocusMixin, DisplayMixin, ReprMixin): def __init__(self, master, start=0, end=100, horizontal=True, command=None, grid=None, align=None): # If you specify a command to the slider, it must take one argument as it will be given # the slider's current value # Description of this object (for friendly error messages) self.description = "[Slider] object from " + str(start) + " to " + str( end) # Set the direction orient = HORIZONTAL if horizontal else VERTICAL # Create a tk Scale object within this object self.tk = Scale(master.tk, from_=start, to=end, orient=orient, command=command) # Pack this object try: utils.auto_pack(self, master, grid, align) except AttributeError: utils.error_format( self.description + "\n" + "Could not add to interface - check first argument is [App] or [Box]" ) # PROPERTIES # ---------------- # Get/set the value @property def value(self): return (self.tk.get()) @value.setter def value(self, value): self.tk.set(value) # METHODS # ---------------- # Calls the given function when the slider value is changed def add_command(self, command): self.tk.config(command=command)
def demo(): root = tkinter.Tk() root.title("Motion Planning") universal_height = 1000 nb = ttk.Notebook(root) page1 = ttk.Frame(nb, width= 1080,height = universal_height) page2 = ttk.Frame(nb,width = 1080,height = universal_height) nb.add(page1, text='Workspace') nb.add(page2, text='Configspace') nb.grid(column=0) workspace = Workspace("./resources/robot_BW_small.bmp", "./resources/Room_BW_small.bmp", page1) configspace = Configspace(page2) controller = Controller(workspace,configspace) workspace.drawAll(workspace.currentPos[0],workspace.currentPos[1]) def callback(event): # print ("clicked at", event.x, event.y) controller.drawMouseOffSet(event.x, event.y) if controller.isInCollision(): setBackgroundColor(page1,"red") else: setBackgroundColor(page1,"green") workspace.label.bind("<Button-1>", callback) def moveRobotOnPath(val): if controller.isAllInitialized(): controller.setSolutionPathOnCurrentPos(int(val)) controller.drawCurrentPos() if controller.isInCollision(): setBackgroundColor(page1,"red") else: setBackgroundColor(page1,"green") slider = Scale(page1, from_=0, to=200, orient=HORIZONTAL, command=moveRobotOnPath) slider.config(length=600) def set_goal(): controller.setCurrentPosAsGoal() slider['from_'] = 0 slider['to_'] = len(configspace.solutionPath)-1 setGoalButton = ttk.Button(page1, text = 'Set Goal',command = set_goal) setGoalButton.pack(side=tkinter.RIGHT) def set_init(): controller.setCurrentPosAsInit() setInitButton = ttk.Button(page1, text = 'Set Init',command = set_init) setInitButton.pack(side=tkinter.RIGHT) slider.pack() root.mainloop()
def Slider(root, style="", horizontal=False, **options): props = {'troughcolor': load_color(style)['bgColor']} scale = Scale(root) props.update(**options) scale.config(props) if horizontal: scale.config(orient=HORIZONTAL) scale.pack()
class Slider: def __init__(self, parent, label, valueCarrier, groupLabel=""): self.instance = Scale(parent, from_=2, to=0, resolution=0.02, length=100, showvalue=0, command=valueCarrier.saveVal) self.valueCarrier = valueCarrier self.groupLabel = groupLabel self._label = None if isinstance(label, str): self._label = Label(parent, text=label) elif isinstance(label, PhotoImage): self._label = Label(parent, image=label) else: raise TypeError( 'Wrong Type. Label must be of type String or PhotoImage!') @property def label(self): return self._label.cget('text') # set pos for slider with label/icon def pos(self, row, column): self.instance.grid(row=row, column=column, padx=(21, 21), pady=5) self._label.grid(row=row + 1, column=column) # activate or deactivate slider def changeState(self, state): if state == DISABLED: self.instance.config(state=DISABLED, fg="#808080") self._label.config(fg="#808080") elif state == NORMAL: self.instance.config(state=NORMAL, fg="#000000") self._label.config(fg="#000000") else: raise ValueError( 'Wrong State. State can be either DISABLED or NORMAL!')
class Visualiser(object): ''' Generic Offline Visualiser. Subclasses need to be used that specify how to handle a data source. ''' ### Public functions ### def __init__(self, title="Visualisation", width=400, height=400, recording=False, recordPattern=None, paused=False, source=None): ''' Constructor. Params: title: string - Title of the visualisation window width: int - Width of the visualisation window height: int - Height of the visualisation window recording: boolean - Start with recording enabled? recordPattern: string - Pattern for recorded images, e.g., cylinders%05g.png paused: boolean - Start with playback paused? source:- The data source to read. What is required here varies by visualiser. ''' # Visualisation options self.vis_features = [] self.vis_frame = 0 self.vis_frameStep = 1 self.vis_jumping = False self.vis_recording = recording self.vis_recordPattern = recordPattern self.vis_paused = paused self.vis_source = source # VTK structures self.vtk_cells = vtkCellArray() self.vtk_renderer = vtkRenderer() # Tk structures self.tk_root = Tk() self.tk_root.title(title) self.tk_root.grid_rowconfigure(0, weight=1) self.tk_root.grid_columnconfigure(0, weight=3) self.tk_root.bind('<Destroy>', self.destroyed) if not self.vis_paused: self.tk_root.after(100, self.animate) self.tk_renderWidget = vtkTkRenderWidget(self.tk_root, width=width, height=height) self.tk_renderWidget.grid(row=0, column=0, sticky=N+S+E+W) self.tk_renderWidget.GetRenderWindow().AddRenderer(self.vtk_renderer) self.tk_featureFrame = Frame(self.tk_root) self.tk_featureFrame.grid(row=0, column=1, rowspan=2) Label(self.tk_featureFrame, text='Features:').grid(row=0, column=0) self.tk_controlFrame = Frame(self.tk_root) self.tk_controlFrame.grid(row=1, column=0) self.tk_quit = Button(self.tk_controlFrame, text="Quit", command=self.shutdown) self.tk_quit.grid(row=0, column=0, columnspan=2) def pause(): if self.vis_paused: self.tk_pause.config(text='Pause') self.tk_root.after(100, self.animate) else: self.tk_pause.config(text='Resume') self.vis_paused ^= True self.tk_pause = Button(self.tk_controlFrame, text="Pause", command=pause) self.tk_pause.grid(row=0, column=2, columnspan=2) if self.vis_recordPattern is not None: def record(): if self.vis_recording: self.tk_record.config(text='Start Recording') else: self.tk_record.config(text='Stop Recording') self.vis_recording ^= True self.tk_record = Button(self.tk_controlFrame, text="Start Recording", command=record) self.tk_record.grid(row=0, column=4, columnspan=2) if self.vis_recording: self.tk_record.config(text="Stop Recording") def make_seek_button(label, column, frame): def jump(): self.jumpTo(frame) b = Button(self.tk_controlFrame, text=label, command=jump) b.grid(row=1, column=column, sticky=W+E) return b self.tk_seek_start = make_seek_button("|<", 0, 0) self.tk_seek_back10 = make_seek_button("<<", 1, lambda: self.vis_frame - 10) self.tk_seek_back1 = make_seek_button("<", 2, lambda: self.vis_frame - 1) self.tk_seek_forward1 = make_seek_button(">", 3, lambda: self.vis_frame + 1) self.tk_seek_forward10 = make_seek_button(">>", 4, lambda: self.vis_frame + 10) self.tk_seek_end = make_seek_button(">|", 5, self.getMaxFrameNumber) Label(self.tk_controlFrame, text='Frame').grid(row=2, column=0, sticky=W+E) def changeFrame(frame): if not self.vis_jumping: self.vis_jumping = True self.jumpTo(self.tk_frame.get()) self.vis_jumping = False self.tk_frame = Scale(self.tk_controlFrame, command=changeFrame, from_=0, to=0, orient=HORIZONTAL) self.tk_frame.grid(row=2, column=1, columnspan=2, sticky=W+E) Label(self.tk_controlFrame, text='Step').grid(row=2, column=3, sticky=W+E) def changeFrameStep(step): self.vis_frameStep = int(step) self.tk_frameStep = Scale(self.tk_controlFrame, command=changeFrameStep, from_=1, to=1, orient=HORIZONTAL) self.tk_frameStep.grid(row=2, column=4, columnspan=2, sticky=W+E) self.setupGrid() def add_feature(self, feature): '''Add a feature to this visualiser''' self.vis_features.append(feature) feature.button(self.tk_featureFrame).grid(row=len(self.vis_features), column=0, sticky=W+E) feature.visualiser = self def run(self): '''Start the visualiser''' self.redraw() self.tk_root.mainloop() ### Private funcitions ### def resetSliders(self): ''' Recalculate the upper bound on the frame and frameStep sliders. ''' maxFrame = self.getMaxFrameNumber() self.tk_frame.config(to=maxFrame) self.tk_frameStep.config(to=maxFrame) def jumpTo(self, frame): ''' Jump to a given frame. If frame is a function, jump to the return value of frame(). ''' oldFrame = self.vis_frame if hasattr(frame, '__call__'): self.vis_frame = frame() else: self.vis_frame = frame maxFrame = self.getMaxFrameNumber() if self.vis_frame < 0: self.vis_frame = 0 elif self.vis_frame > maxFrame: self.vis_frame = maxFrame self.vis_paused = True self.tk_pause.config(text='Resume') self.tk_frame.set(self.vis_frame) self.redraw(oldFrame != self.vis_frame) def redraw(self, update=False): self.resetSliders() for feature in [ f for f in self.vis_features if not f.dynamic ]: feature.draw(self.vtk_renderer) if update: for feature in [ f for f in self.vis_features if f.dynamic]: f.redraw(self.vtk_renderer) self.tk_renderWidget.GetRenderWindow().Render() self.tk_root.update_idletasks() ### Gui events ### def destroyed(self, event): if event.widget == self.tk_root: self.shutdown() def shutdown(self): self.tk_root.withdraw() self.tk_root.destroy() def animate(self): if not self.vis_paused: self.jumpTo(self.vis_frame + self.vis_frameStep) if self.vis_recording and self.vis_recordPattern is not None: self.save_image() self.tk_root.after(100, self.animate) def save_image(self): extmap = {'.jpg' : vtkJPEGWriter, '.jpeg' : vtkJPEGWriter, '.png' : vtkPNGWriter, '.pnm' : vtkPNMWriter} _, ext = splitext(self.vis_recordPattern) try: writer = extmap[ext.lower()]() except KeyError: print('ERROR: Can\'t handle %s extension. Recording disabled.' % ext) self.vis_recordPattern = None return win = self.vtk_renderer.GetRenderWindow() w2i = vtkWindowToImageFilter() w2i.SetInput(win) w2i.Update() writer.SetInput(w2i.GetOutput()) writer.SetFileName(self.vis_recordPattern % self.vis_frame) win.Render() writer.Write() ### Things subclasses need to override ### def setupGrid(self): ''' Populate the vtkCellArray instance at self.vtk_cells. Subclasses are required to override this function to read from their source as appropriate. ''' raise NotImplementedError('Subclass needs to override Visualiser::setupGrid!') def getMaxFrameNumber(self): ''' Return the maximum frame number. This will need to be defined by a subclass. ''' raise NotImplementedError('Subclass needs to override Visualiser::getMaxFrameNumber!') def getQuantityPoints(self, quantityName, dynamic=True, frameNumber=0): ''' Return the points of a quantity at a given frame as a list [float]. Subclasses need to override this. ''' raise NotImplementedError('Subclass needs to override Visualiser::getQuantityPoints!') def getQuantityDict(self): ''' Return the values of all quantities at a given time as a dictionary. Sublclasses need to override this. ''' raise NotImplementedError('Subclass needs to override Visualiser::getQuantityDict!')
class Report(Frame): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.root = self.winfo_toplevel() self.root.title(" ".join((APPNAME, VERSION, "| Report"))) self.root.resizable(0, 0) self.items = None headers = ('Page', 'Rank', 'PageRank Value', 'Incoming') self.t = Scale(self, from_=0, to=1, label='n-th iteration', bd=1, width=7, orient='horizontal', command=self.__set_t) self.t.pack(fill='x') self.label = Label(self, anchor='e') self.label.pack(fill='x') self.tree = Treeview(self, columns=headers, show="headings", height=REPORT_HEIGHT) self.tree.column(0, anchor='center', width=55) self.tree.column(1, anchor='center', width=55) self.tree.column(2, width=175) self.tree.column(3, anchor='center', width=55) self.scroll = Scrollbar(self, command=self.tree.yview) self.scroll.pack(side='right', fill='y') self.tree.config(yscrollcommand=self.scroll.set) for col in headers: self.tree.heading(col, text=col.title()) self.root.master.focus_force() self.root.bind('<Key>', self.__keys) def __keys(self, event): if event.char == 'i': self.t.set(self.t.get() - 1) elif event.char == 'I': self.t.set(0) elif event.char == 'o': self.t.set(self.t.get() + 1) elif event.char == 'O': self.t.set(self.pagerank[1]) def __set_t(self, t=None): self.render(self.pagerank, self.edge_counts, self.d, self.e, t=int(t)) def render(self, pagerank, edge_counts, d, e, t=None): self.d, self.e = d, e self.edge_counts = edge_counts self.pagerank = pagerank if t is not None: pr = pagerank[0][t] else: self.t.config(command=None) self.t.config(to=self.pagerank[1]) self.t.set(self.pagerank[1]) self.t.config(command=self.__set_t) pr = pagerank[0][-1] label_text = '# of ranks:{0:>3} # of iterations:{1:>3}'.format( len(set(pr.values())), self.pagerank[1]) self.label.config(text=label_text) in_map = {k: 0 for k in pr.keys()} # incoming edges by nodes for (tail, head), n in edge_counts.items(): if tail is not head: in_map[head] += n get_rank = lambda k: sorted(pr.values())[::-1].index(pr[k]) + 1 data = tuple( (k, get_rank(k), pr[k], in_map[k]) for k in sorted(pr.keys())) if self.items: self.tree.delete(*self.items) self.items = [ self.tree.insert('', 'end', values=line) for line in data ] self.tree.pack(side='left', fill='y') #self.root.master.focus_force() def destroy(self): super().destroy() self.root.master.r_win = False self.root.master.windowsmenu.entryconfig(0, label='[ ] Report window') self.root.destroy()
class MainWindow: def __init__(self, master): self.FORMAT = {0: '.pdf', 1: '.jpg'} self.SETTINGS_DPI = {0: 72, 1: 100, 2: 150} self.SETTINGS_COLOR = {0: '1', 1: 'RGB'} self.list_files = '' self.filename = '' self.lenght_list_files = '' #################################################################################################### # A1 # 1 # 2 # 3 # 4 # 5 # 6 # 7 # 8 # 9 # 10 # # A2 # BUTTON # TEXT # TEXT # TEXT # TEXT # TEXT # TEXT # TEXT # VSCROLL # 10 # # A3 # BUTTON # TEXT # TEXT # TEXT # TEXT # TEXT # TEXT # TEXT # VSCROLL # 10 # # A4 # BUTTON # TEXT # TEXT # TEXT # TEXT # TEXT # TEXT # TEXT # VSCROLL # 10 # # A5 # BUTTON # TEXT # TEXT # TEXT # TEXT # TEXT # TEXT # TEXT # VSCROLL # 10 # # A6 # 1 # HSCROLL # HSCROLL # HSCROLL # HSCROLL # HSCROLL # HSCROLL # HSCROLL # HSCROLL # 10 # # A7 # FORMAT # SPLIT # SPLIT # DPI # DPI # COLOR # COLOR # QUALITY # QUALITY # 10 # # A8 # FORMAT # SPLIT # SPLIT # DPI # DPI # COLOR # COLOR # QUALITY # QUALITY # 10 # # A9 # FORMAT # SPLIT # SPLIT # DPI # DPI # COLOR # COLOR # QUALITY # QUALITY # 10 # # A10# P_BAR # P_BAR # P_BAR # P_BAR # P_BAR # P_BAR # P_BAR # P_BAR # P_BAR # 10 # #################################################################################################### self.text_box = Text(master, wrap='char', width=80, height=16) self.vscroll_text_box = Scrollbar( master, orient='vertical', command=self.text_box.yview()) self.hscroll_text_box = Scrollbar( master, orient='horizontal', command=self.text_box.xview()) self.text_box.config(yscrollcommand=self.vscroll_text_box.set, xscrollcommand=self.hscroll_text_box.set) self.text_box.grid(row=2, column=2, rowspan=4, columnspan=7) self.vscroll_text_box.grid( row=2, column=9, rowspan=4, sticky=('se', 'ne')) self.hscroll_text_box.grid( row=6, column=2, columnspan=8, sticky=('we', 'ne')) # # # Настройки формата файла self.labelframe_format = LabelFrame( master, text="Формат файла") self.labelframe_format.grid( row=7, column=1, columnspan=1, rowspan=3, sticky='wens') self.format_output_file = IntVar() self.format_output_file.set(0) self.radiobutton_pdf = Radiobutton(self.labelframe_format, text='Формат PDF', variable=self.format_output_file, value=0, command=self.shutdown_button) self.radiobutton_jpg = Radiobutton(self.labelframe_format, text='Формат JPEG', variable=self.format_output_file, value=1, command=self.shutdown_button) self.radiobutton_pdf.pack(fill='x') self.radiobutton_jpg.pack(fill='x') # # # Задаём фрейм в котором будем размещать настройки разделения self.labelframe_split = LabelFrame(master, text="Развибка на страницы") self.labelframe_split.grid( row=7, column=2, columnspan=2, rowspan=3, sticky='wens') self.scale_split = Scale(self.labelframe_split, from_=0, to=100, resolution=5, orient="horizontal") self.scale_split.pack(fill='both') # # # Задаём фрейм в котором будем размещать настройки качества файла self.labelframe_dpi = LabelFrame(master, text="Настройки качества") self.labelframe_dpi.grid( row=7, column=4, columnspan=2, rowspan=3, sticky='wens') self.dpi = IntVar() self.dpi.set(2) self.radiobutton_dpi_72 = Radiobutton(self.labelframe_dpi, text='Среднее', variable=self.dpi, value=0) self.radiobutton_dpi_100 = Radiobutton(self.labelframe_dpi, text='Хорошее', variable=self.dpi, value=1) self.radiobutton_dpi_150 = Radiobutton(self.labelframe_dpi, text='Отличное', variable=self.dpi, value=2) self.radiobutton_dpi_72.pack(fill='both') self.radiobutton_dpi_100.pack(fill='both') self.radiobutton_dpi_150.pack(fill='both') # # # Фрейм с настройками выбора цветное или ч/б self.labelframe_color = LabelFrame(master, text="Настройки цвета") self.labelframe_color.grid( row=7, column=6, columnspan=2, rowspan=3, sticky='wens') self.color = IntVar() self.color.set(0) self.radiobutton_bw = Radiobutton(self.labelframe_color, text='Чёрно/белое', variable=self.color, value=0) self.radiobutton_color = Radiobutton(self.labelframe_color, text='Цветное', variable=self.color, value=1) self.radiobutton_bw.pack(fill='both') self.radiobutton_color.pack(fill='both') # # # Фрейм с настройками качества сжатия JPEG self.labelframe_quality = LabelFrame( master, text="Настройка сжатия файла") self.labelframe_quality.grid( row=7, column=8, columnspan=2, rowspan=3, sticky='nw ne') self.scale_quality = Scale(self.labelframe_quality, label='Хуже Лучше', from_=1, to=100, resolution=1, orient="horizontal", state='active') self.scale_quality.set(100) self.scale_quality.pack(fill='both') # # # Чекбокс настройки оптимизации качества self.optimize_image = BooleanVar() self.optimize_image.set(False) self.checkbutton_optimize = Checkbutton( self.labelframe_quality, text='Автоматически', variable=self.optimize_image, onvalue=True, offvalue=False) self.checkbutton_optimize.pack() self.checkbutton_optimize.bind( '<Button>', lambda event: self.change_state(event)) # # # Задаем фрейм в котором будем размещать основные кнопки команд self.button_frame = Frame(master) self.button_frame.grid(row=2, column=1, rowspan=4) self.button_open = Button(self.button_frame, text="Добавить файлы", command=self.listFiles, state='active', pady=5, padx=8) self.button_open.pack() self.button_save = Button(self.button_frame, text="Сохранить файл", command=self.savefileName, state='active', pady=5, padx=9) self.button_save.pack() self.button_run = Button(self.button_frame, text="Запустить", command=lambda x=True: ConvertFile().process( input_file=self.list_files, output_file=self.filename, format_file=self.FORMAT[self.format_output_file.get( )], dpi=self.SETTINGS_DPI[self.dpi.get()], color=self.SETTINGS_COLOR[self.color.get()], optimize=self.optimize_image.get(), quality=self.scale_quality.get(), split_step=self.scale_split.get()), state='active', pady=5, padx=26) self.button_run.pack() # # # Progressbar self.pbar = ttk.Progressbar( master, orient='horizontal', mode='determinate', length=100, maximum=100) self.pbar.grid(row=10, column=1, columnspan=9, sticky='wens') # # # Меню программы self.menu = Menu(master) master.config(menu=self.menu) self.sub_menu1 = Menu(self.menu) self.menu.add_cascade(label='Файл', menu=self.sub_menu1) self.sub_menu1.add_command(label='Выход', command=self.closed_window) self.sub_menu2 = Menu(self.menu) self.menu.add_cascade(label='Информация', menu=self.sub_menu2) self.sub_menu2.add_command( label='О программе', command=self.show_about) def frange(self, start, stop, step): while start < stop: yield start start += step def change_state(self, event): if self.optimize_image.get() is False: self.scale_quality.config(state='disable') else: self.scale_quality.config(state='active') def shutdown_button(self): if self.format_output_file.get() == 1: self.button_save.config(state='disable') else: self.button_save.config(state='active') def update_progressbar(self, page): step = 100 / self.lenght_list_files step_range = list(self.frange(0, 100, step)) self.pbar['value'] = step_range[page] + step root.update() def show_about(self): messagebox.showinfo( title='О программе', message=ABOUT) def show_error(self, message): messagebox.showerror(title='ОШИБКА', message=message) def closed_window(self): root.quit() def listFiles(self): """ Функция привязана к кнопке "Добавить файлы". Результат работы функции - список файлов, который отображается в поле text_box """ self.list_files = filedialog.askopenfilenames() self.lenght_list_files = len(self.list_files) self.text_box.delete(1.0, END) for i in self.list_files: self.text_box.insert(END, i) self.text_box.insert(END, '\n') return self.list_files def savefileName(self): """ Функция привязана к кнопке "Сохранить файл". Результат работы функции - полный путь к выходному файлу """ default = self.FORMAT[0] setting_format = self.FORMAT[self.format_output_file.get()] self.filename = filedialog.asksaveasfilename(filetypes=[(f"Формат файла *{setting_format}", f"*{setting_format}")], defaultextension=default) return self.filename
class ControlAppGUI: def __init__(self, master): self.master = master # GUI layout setup self.menu = Menu(self.master) self.master.config(menu=self.menu) master.grid_rowconfigure(0, weight=1) master.grid_columnconfigure(0, weight=1) filemenu = Menu(self.menu) self.menu.add_cascade(label="File", menu=filemenu) filemenu.add_command(label="New", command=self.NewFile) openmenu = Menu(self.menu) openmenu.add_command(label="Gcode", command=self.OpenGcodeFile) openmenu.add_command(label="CSV", command=self.OpenCsvFile) savemenu = Menu(self.menu) savemenu.add_command(label="Gcode", command=self.SaveGcodeFile) savemenu.add_command(label="CSV", command=self.SaveCsvFile) filemenu.add_cascade(label='Open...', menu=openmenu, underline=0) filemenu.add_cascade(label="Save...", menu=savemenu, underline=0) #filemenu.add_command(label="Reload current file", command=None) filemenu.add_command(label="Set color", command=self.AskColor) filemenu.add_separator() def updatePortList(): ports = serial.serial_ports() self.portCombo['values'] = ports if len(ports) > 0: self.portCombo.current(0) filemenu.add_command(label="Refresh port list", command=updatePortList) filemenu.add_command(label="Exit", command=self.Quit) editmenu = Menu(self.menu) self.menu.add_cascade(label="Edit", menu=editmenu) editmenu.add_command(label="Settings", command=self.Settings) helpmenu = Menu(self.menu) self.menu.add_cascade(label="Help", menu=helpmenu) helpmenu.add_command(label="About...", command=self.About) master.title("Embroiderino frontend") self.controls = ttk.Notebook(master) tab1 = Frame(self.controls) tab2 = Frame(self.controls) self.controls.add(tab1, text="Machine control") self.controls.add(tab2, text="Path manipulation") self.controls.grid(row=0, column=1, sticky=N) self.controls.grid_rowconfigure(0, weight=1) self.controls.grid_columnconfigure(0, weight=1) # MACHINE TAB ports = serial.serial_ports() self.portCombo = ttk.Combobox(tab1, values=ports) if len(ports) > 0: self.portCombo.current(0) self.portCombo.grid(row=1, column=0) self.baudCombo = ttk.Combobox(tab1, state='readonly', values=("115200", "9600"), width=10) self.baudCombo.current(0) self.baudCombo.grid(row=1, column=1) self.connectButton = Button(tab1, text="Connect", command=self.ToggleConnect, width=10) self.connectButton.grid(row=1, column=2) self.startButton = Button(tab1, text="Start job", command=self.ToggleStart, state=DISABLED) self.startButton.grid(row=2, column=1) self.homeButton = Button(tab1, text="Home machine", command=lambda: serial.queue_command("G28\n"), state=DISABLED) self.homeButton.grid(row=2, column=0) testNavigation = Frame(tab1) leftButton = Button( testNavigation, text="<", command=lambda: serial.queue_command("G91\nG0 X-2\nG90\n"), state=DISABLED) leftButton.grid(row=1, column=0) rightButton = Button( testNavigation, text=">", command=lambda: serial.queue_command("G91\nG0 X2\nG90\n"), state=DISABLED) rightButton.grid(row=1, column=2) upButton = Button( testNavigation, text="/\\", command=lambda: serial.queue_command("G91\nG0 Y2\nG90\n"), state=DISABLED) upButton.grid(row=0, column=1) downButton = Button( testNavigation, text="\\/", command=lambda: serial.queue_command("G91\nG0 Y-2\nG90\n"), state=DISABLED) downButton.grid(row=2, column=1) testNavigation.grid(row=3, column=0) self.navigationButtons = [ leftButton, rightButton, upButton, downButton ] self.testButton = Button(tab1, text="Test border path", command=self.TestBorder, state=DISABLED) self.testButton.grid(row=3, column=1) self.gotoButton = Button(tab1, text="Go to", command=self.GoTo, state=DISABLED, relief=RAISED) self.gotoButton.grid(row=3, column=2) self.stopButton = Button(tab1, text="STOP", command=self.StopAll, state=DISABLED) self.stopButton.grid(row=4, column=0) self.pauseOnToolChange = IntVar() self.pauseOnTrim = IntVar() self.toolChangeCheck = Checkbutton(tab1, variable=self.pauseOnToolChange, onvalue=1, offvalue=0, text="Pause on tool change") self.toolChangeCheck.grid(row=4, column=1) self.toolChangeCheck.select() self.trimCheck = Checkbutton(tab1, variable=self.pauseOnTrim, onvalue=1, offvalue=0, text="Pause on trim") self.trimCheck.grid(row=4, column=2) self.trimCheck.select() progressFrame = Frame(tab1) Label(progressFrame, text="Tool changes: ", bd=1).grid(row=0, column=0, pady=(10, 1)) self.toolChangesLabel = Label(progressFrame, text="0/0", bd=1, relief=SUNKEN) self.toolChangesLabel.grid(row=1, column=0) Label(progressFrame, text="Tool points: ", bd=1).grid(row=0, column=2, pady=(10, 1)) self.toolPointsLabel = Label(progressFrame, text="0/0", bd=1, relief=SUNKEN) self.toolPointsLabel.grid(row=1, column=2) Label(progressFrame, text="Estimated endtime: ", bd=1).grid(row=0, column=4, pady=(10, 1)) self.timeLabel = Label(progressFrame, text="0/0", bd=1, relief=SUNKEN) self.timeLabel.grid(row=1, column=4) progressFrame.grid(row=5, column=0, columnspan=3) colorsFrame = Frame(tab1, bd=1) colorsFrame.grid(row=6, column=0, columnspan=3, pady=(10, 1)) Label(colorsFrame, text="Current color: ", bd=1).pack(side=LEFT) self.currentColorIndicator = Label(colorsFrame, text=" ", relief=RAISED, bd=1) self.currentColorIndicator.pack(side=LEFT, padx=(1, 40)) Label(colorsFrame, text="Next color: ", bd=1).pack(side=LEFT, padx=2) self.nextColorIndicator = Label(colorsFrame, text=" ", relief=RAISED, bd=1) self.nextColorIndicator.pack(side=LEFT) Label(tab1, text="SPM speed limit: ", bd=1).grid(row=7, column=0) self.speedSlider = Scale(tab1, from_=80, to=800, command=None, orient=HORIZONTAL, length=200) self.speedSlider.set(400) self.speedSlider.bind( "<ButtonRelease-1>", lambda _: serial.queue_command( "M222 S%d\n" % self.speedSlider.get(), priority=-1)) self.speedSlider.grid(row=7, column=1, columnspan=2) # PATH TAB tab2.grid_columnconfigure(0, weight=1) Label(tab2, text="Display progress: ", bd=1).grid(row=0) self.slider = Scale(tab2, from_=0, to=0, command=self.UpdatePath, orient=HORIZONTAL, length=300) self.slider.grid(row=1) toolbar = Frame(tab2, bd=1, relief=RAISED) toolbar.grid(row=2) self.panButton = Button(toolbar, relief=RAISED, command=self.TogglePan, text="Move path") self.panButton.pack(side=LEFT, padx=2, pady=2) self.rotateButton = Button(toolbar, relief=RAISED, command=self.ToggleRotate, text="Rotate path") self.rotateButton.pack(side=LEFT, padx=2, pady=2) self.mirrorButton = Button(toolbar, relief=RAISED, command=self.ToggleMirror, text="Mirror path") self.mirrorButton.pack(side=LEFT, padx=2, pady=2) self.scaleButton = Button(toolbar, relief=RAISED, command=self.ToggleScale, text="Scale path") self.scaleButton.pack(side=LEFT, padx=2, pady=2) # CANVAS canvasFrame = Frame(master) canvasFrame.grid(row=0, column=0, sticky='NWES') self.canvas = ResizingCanvas(canvasFrame, width=400, height=400, bg="white", highlightthickness=0) self.canvas.bind("<B1-Motion>", self.CanvasDrag) self.canvas.bind("<Button-1>", self.CanvasClick) self.canvas.bind("<ButtonRelease-1>", self.CanvasRelease) self.canvas.pack(expand=YES, anchor=N + W) #STATUS BAR self.status = Label(master, text="Not connected", bd=1, relief=SUNKEN, anchor=W) self.status.grid(row=2, columnspan=2, sticky='WE') # PROGRAM VARIABLES self.SETTINGSFNAME = "settings.pickle" self.commands = [] self.transform = (0, 0) self.isConnected = False self.isJobRunning = False self.isJobPaused = False self.lastSendCommandIndex = -1 self.lastMove = None self.currentColor = 'black' self.currentToolChange = 0 self.toolChangesTotal = 0 self.currentToolPoint = 0 self.toolPointsTotal = 0 self.distancesList = [] self.distanceTraveled = 0 self.positionResponseRegex = re.compile( "X:(\-?\d+\.\d+),Y:(\-?\d+\.\d+)") self.machineSetups = {} self.currentSetupName = None self.workAreaSize = [100, 100] self.workAreaOrigin = [0, 0] # LOAD SOME SETTIGS self.loadSettings() self.canvas.setArea(self.workAreaSize[0], self.workAreaSize[1]) self.canvas.setOrigin(self.workAreaOrigin[0], self.workAreaOrigin[1]) # UI LOGIC def Quit(self): if messagebox.askyesno('Confirm', 'Really quit?'): self.master.quit() return True return False def AskColor(self): color = colorchooser.askcolor(title="Colour Chooser") def NewFile(self): if self.isJobRunning: return self.toolChangesTotal = 0 self.toolPointsTotal = 0 self.distancesList = [] self.lastSendCommandIndex = -1 self.lastMove = None self.commands = [] self.canvas.clear() self.slider.config(to=0) def OpenGcodeFile(self): if self.isJobRunning: return with filedialog.askopenfile(filetypes=(("Machine G-code", "*.gcode"), )) as f: self.commands = load_gcode_file(f) self.FinishLoading() def SaveGcodeFile(self): if not self.commands: return with filedialog.asksaveasfile(filetypes=(("Machine G-code", "*.gcode"), ), defaultextension='.gcode') as f: save_gcode_file(f, self.commands) def OpenCsvFile(self): if self.isJobRunning: return with filedialog.askopenfile(filetypes=(("Comma separated values", "*.csv"), )) as f: self.commands = load_csv_file(f) self.FinishLoading() def SaveCsvFile(self): pass def FinishLoading(self): points_count = len(self.commands) # file loaded if points_count > 2: self.testButton.config(state=NORMAL) self.startButton.config(state=NORMAL) # prepare color indicators self.currentColorIndicator.configure(background=self.currentColor) self.currentColorIndicator.configure(background=None) # center loaded path rectangle = toolpath_border_points(self.commands) rwidth = rectangle[2][0] - rectangle[0][0] rheight = rectangle[2][1] - rectangle[0][1] center = (rectangle[2][0] - (rwidth) / 2, rectangle[2][1] - (rheight) / 2) transform = (self.workAreaSize[0] / 2 - center[0] + self.workAreaOrigin[0], self.workAreaSize[1] / 2 - center[1] + self.workAreaOrigin[1]) self.commands = translate_toolpath(self.commands, transform) # check if design is bigger than available workarea if (rwidth > self.workAreaSize[0] or rheight > self.workAreaSize[1]): messagebox.showinfo( 'Size Warning!', "Looks like this design is bigger than available work area." ) self.slider.config(to=points_count) self.slider.set(points_count) self.toolPointsTotal, self.toolChangesTotal, self.distancesList = toolpath_info( self.commands) self.toolPointsLabel.config( text="%d/%d" % (self.currentToolPoint, self.toolPointsTotal)) self.toolChangesLabel.config( text="%d/%d" % (self.currentToolChange, self.toolChangesTotal)) self.UpdateTimeEstLabel() self.canvas.draw_toolpath(self.commands) def About(self): #self.PausePopup() messagebox.showinfo( 'About this software', 'This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nWritten in 2018 by markol.' ) def Settings(self): tl = Toplevel(root) tl.title("Global settings") frame = Frame(tl) frame.grid() machineFrame = LabelFrame(frame, text="Machine hoop workarea (mm)", relief=RIDGE) machineFrame.grid() Label(machineFrame, text="width ").grid(row=0, column=0, sticky=N) workareaWidth = Entry(machineFrame, text="1") workareaWidth.grid(row=0, column=1) Label(machineFrame, text="height ").grid(row=2, column=0, sticky=N) workareaHeight = Entry(machineFrame, text="2") workareaHeight.grid(row=2, column=1) hoopFrame = LabelFrame(frame, text="Machine hoop origin (mm)", relief=RIDGE) hoopFrame.grid() Label(hoopFrame, text="X ").grid(row=0, column=0, sticky=N) workareaOriginX = Entry(hoopFrame, text="3") workareaOriginX.grid(row=0, column=1) Label(hoopFrame, text="Y ").grid(row=2, column=0, sticky=N) workareaOriginY = Entry(hoopFrame, text="4") workareaOriginY.grid(row=2, column=1) Label(hoopFrame, text="Setup name ").grid(row=3, column=0, sticky=N) setupName = Entry(hoopFrame, text="Setup1") setupName.grid(row=3, column=1) def setupSelected(event=None): if event: selectedSetupName = setupsCombo.get() else: selectedSetupName = self.currentSetupName workareaWidth.delete(0, END) workareaWidth.insert( 0, str(self.machineSetups[selectedSetupName]["workAreaSize"][0])) workareaHeight.delete(0, END) workareaHeight.insert( 0, str(self.machineSetups[selectedSetupName]["workAreaSize"][1])) workareaOriginX.delete(0, END) workareaOriginX.insert( 0, str(self.machineSetups[selectedSetupName]["workAreaOrigin"] [0])) workareaOriginY.delete(0, END) workareaOriginY.insert( 0, str(self.machineSetups[selectedSetupName]["workAreaOrigin"] [1])) setupName.delete(0, END) setupName.insert(0, selectedSetupName) setupSelected() setupsCombo = ttk.Combobox(frame, values=list(self.machineSetups.keys()), state="readonly") setupsCombo.grid(row=2, column=0) setupsCombo.current(setupsCombo['values'].index(self.currentSetupName)) setupsCombo.bind("<<ComboboxSelected>>", setupSelected) def addSettings(): newSetupName = setupName.get() if newSetupName in self.machineSetups.keys(): if not messagebox.askyesno( "Machine setup exists", "Machine setup with this name exists. Overwrite?"): return newSetup = { "workAreaSize": (int(workareaWidth.get()), int(workareaHeight.get())), "workAreaOrigin": (int(workareaOriginX.get()), int(workareaOriginY.get())) } self.machineSetups[newSetupName] = newSetup setupsCombo['values'] = list(self.machineSetups.keys()) Button(frame, relief=RAISED, command=addSettings, text="Add setup").grid(row=2, column=1) def removeSetup(): self.machineSetups.pop(setupsCombo.get(), None) options = list(self.machineSetups.keys()) setupsCombo['values'] = options if len(options) > 0: self.currentSetupName = list(self.machineSetups.keys())[0] setupsCombo.current(0) setupSelected(1) Button(frame, relief=RAISED, command=removeSetup, text="Remove setup").grid(row=2, column=2) def saveSettings(): try: if len(setupsCombo.get()) > 0: self.workAreaSize = (int(workareaWidth.get()), int(workareaHeight.get())) self.workAreaOrigin = (int(workareaOriginX.get()), int(workareaOriginY.get())) self.currentSetupName = setupsCombo.get() except: messagebox.showerror( "Invalid numeric values", "Please provide correct workarea values!") return self.canvas.setArea(self.workAreaSize[0], self.workAreaSize[1]) self.canvas.setOrigin(self.workAreaOrigin[0], self.workAreaOrigin[1]) self.storeSettings() TmpDim = namedtuple('TmpDim', 'width height') tmp = TmpDim(self.canvas.width, self.canvas.height) self.canvas.on_resize(tmp) Button(frame, text="Save", command=saveSettings, width=10).grid(row=3, column=3) Button(frame, text="Close", command=lambda: tl.destroy(), width=10).grid(row=3, column=2) def loadSettings(self): try: with open(self.SETTINGSFNAME, "rb") as f: data = pickle.load(f) self.machineSetups = data["machineSetups"] self.currentSetupName = data["currentSetupName"] self.workAreaSize = self.machineSetups[ self.currentSetupName]["workAreaSize"] self.workAreaOrigin = self.machineSetups[ self.currentSetupName]["workAreaOrigin"] except Exception as e: print("Unable to restore program settings:", str(e)) def storeSettings(self): with open(self.SETTINGSFNAME, "wb") as f: try: data = { "machineSetups": self.machineSetups, "currentSetupName": self.currentSetupName } pickle.dump(data, f) except Exception as e: print("Error while saving settings:", str(e)) def ToggleConnect(self): if self.isConnected: serial.close_serial() self.connectButton.config(text="Connect") self.status.config(text="Not connected") self.homeButton.config(state=DISABLED) self.stopButton.config(state=DISABLED) self.gotoButton.config(state=DISABLED) self.SetNavButtonsState(False) self.isConnected = False else: if serial.open_serial(self.portCombo.get(), self.baudCombo.get()): self.connectButton.config(text="Disconnect") self.status.config(text="Connected") self.homeButton.config(state=NORMAL) self.stopButton.config(state=NORMAL) self.gotoButton.config(state=NORMAL) self.SetNavButtonsState(True) self.isConnected = True self.GetPositionTimerTaks() def TestBorder(self): rectangle = toolpath_border_points(self.commands) for point in rectangle: serial.queue_command("G0 X%f Y%f F5000\n" % point) def ToggleStart(self): if self.isJobPaused: serial.queue.clear() self.startButton.config(text="Resume job") self.status.config(text="Job paused") else: self.startButton.config(text="Pause job") self.status.config(text="Job in progress") startInstructionIndex = self.lastSendCommandIndex + 1 # job launch if not self.isJobRunning: self.canvas.clear() startInstructionIndex = 0 self.start = time.time() serial.queue_command("G0 F15000\n") self.isJobRunning = True self.QueueCommandsBlock(startInstructionIndex) self.isJobPaused = not self.isJobPaused def QueueCommandsBlock(self, startInstructionIndex): commandsCount = len(self.commands) def progressCallback(instruction_index): ''' after every move G0 or G1 or G28 command being sent, this callback is executed ''' point = self.commands[instruction_index] if self.lastMove: coord = (self.lastMove[1], self.lastMove[2], point[1], point[2]) color = self.currentColor # set color for jump move if "G0" == point[0] or "G28" == point[0]: color = "snow2" else: self.currentToolPoint += 1 self.toolPointsLabel.config( text="%d/%d" % (self.currentToolPoint, self.toolPointsTotal)) # calculate distance for material usage self.distanceTraveled += math.hypot( coord[0] - coord[2], coord[1] - coord[3]) # draw new line on canvas line = self.canvas.create_line(self.canvas.calc_coords(coord), fill=color) self.canvas.lift(self.canvas.pointer, line) self.UpdateTimeEstLabel() # store next start point and instruction index self.lastSendCommandIndex = instruction_index self.lastMove = point # update status bar if self.currentToolPoint % 10 == 0: progress = instruction_index / commandsCount * 100 self.status.config(text="Job in progress %.1f%%" % progress) def progressPauseCallback(instruction_index, trim=False): ''' this callback pauses the job ''' point = self.commands[instruction_index] self.lastSendCommandIndex = instruction_index # pause on color change if not trim: self.currentColor = _from_rgb((point[1], point[2], point[3])) self.currentToolChange += 1 self.currentColorIndicator.configure( background=self.currentColor) self.toolChangesLabel.config( text="%d/%d" % (self.currentToolChange, self.toolChangesTotal)) # pause enabled or not if self.pauseOnToolChange.get() == 1: self.ToggleStart() self.PausePopup(self.currentColor) else: self.QueueCommandsBlock(self.lastSendCommandIndex + 1) # pause on trim else: # pause enabled or not if self.pauseOnTrim.get() == 1: self.ToggleStart() self.PausePopup(self.currentColor, trim) else: self.QueueCommandsBlock(self.lastSendCommandIndex + 1) # all the commands until next tool change command, are queued at once # unsupported commands are ignored for i in range(startInstructionIndex, commandsCount): point = self.commands[i] # pause on color change if "M6" == point[0]: serial.queue_command( "M6\n", lambda _, index=i: progressPauseCallback(index)) self.nextColorIndicator.configure( background=_from_rgb((point[1], point[2], point[3]))) break # pause on trim elif "G12" == point[0]: # if next command is a color change, there is no need to pause now if i < commandsCount + 1 and self.commands[ i + 1][0] == "M6" and self.pauseOnToolChange.get() == 1: serial.queue_command("G12\n") else: serial.queue_command( "G12\n", lambda _, index=i: progressPauseCallback(index, trim=True)) break elif "G1" == point[0] or "G0" == point[0] or "G28" == point[0]: serial.queue_command( "%s X%f Y%f\n" % (point[0], point[1], point[2]), lambda _, index=i: progressCallback(index)) # queue job finish callback if i + 1 >= commandsCount: serial.queue_command("M114\n", self.JobFinished) def SetNavButtonsState(self, enabled=False): newState = NORMAL if enabled else DISABLED for b in self.navigationButtons: b.config(state=newState) def TogglePan(self): self.rotateButton.config(relief=RAISED) self.scaleButton.config(relief=RAISED) if self.isJobRunning: return if self.panButton.config('relief')[-1] == SUNKEN: self.panButton.config(relief=RAISED) else: self.panButton.config(relief=SUNKEN) def ToggleRotate(self): self.panButton.config(relief=RAISED) self.scaleButton.config(relief=RAISED) if self.isJobRunning: return if self.rotateButton.config('relief')[-1] == SUNKEN: self.rotateButton.config(relief=RAISED) else: self.rotateButton.config(relief=SUNKEN) def ToggleMirror(self): self.panButton.config(relief=RAISED) self.rotateButton.config(relief=RAISED) self.scaleButton.config(relief=RAISED) if self.isJobRunning: return self.commands = reflect_toolpath( self.commands, self.workAreaSize[0] / 2 + self.workAreaOrigin[0]) self.canvas.draw_toolpath(self.commands[0:int(self.slider.get())]) def ToggleScale(self): self.panButton.config(relief=RAISED) self.rotateButton.config(relief=RAISED) self.mirrorButton.config(relief=RAISED) if self.isJobRunning: return if self.scaleButton.config('relief')[-1] == SUNKEN: self.scaleButton.config(relief=RAISED) else: self.scaleButton.config(relief=SUNKEN) def GoTo(self): if self.isJobRunning: return if self.gotoButton.config('relief')[-1] == SUNKEN: self.gotoButton.config(relief=RAISED) else: self.gotoButton.config(relief=SUNKEN) def StopAll(self): serial.queue.clear() self.JobFinished(False) self.status.config(text="Job stopped on user demand") def JobFinished(self, messagePopup=True): self.isJobRunning = False self.isJobPaused = False self.lastSendCommandIndex = -1 self.lastMove = None self.distanceTraveled = 0 self.currentToolChange = 0 self.currentToolPoint = 0 self.currentColor = 'black' self.toolPointsLabel.config( text="%d/%d" % (self.currentToolPoint, self.toolPointsTotal)) self.toolChangesLabel.config( text="%d/%d" % (self.currentToolChange, self.toolChangesTotal)) self.UpdateTimeEstLabel() self.startButton.config(text="Start job") self.status.config(text="Job finished") timeTaken = time.time() - self.start # non blocking popup messagebox if messagePopup: tl = Toplevel(root) # this pop-up is always on top and other windows are deactivated tl.attributes('-topmost', 'true') tl.title("Job finished") tl.grab_set() frame = Frame(tl) frame.grid() Label(frame, text='Current job is finished and took %s.' % time.strftime("%H hours, %M minutes, %S seconds", time.gmtime(timeTaken))).grid(row=0, column=0, sticky=N) Button(frame, text="OK", command=lambda: tl.destroy(), width=10).grid(row=1, column=0) def CanvasClick(self, event): if self.isJobRunning: return self.dragStart = [event.x, event.y] #self.transform = math.atan2(event.x, event.y) self.transform = 0 # go to if self.gotoButton.config('relief')[-1] == SUNKEN: point = self.canvas.canvas_point_to_machine(self.dragStart) serial.queue_command("G0 X%f Y%f\n" % point) #print("Clicked at: ", self.dragStart) def CanvasRelease(self, event): if self.isJobRunning: return print("Applied transform", self.transform) def CanvasDrag(self, event): if self.isJobRunning: return vect = (self.dragStart[0] - event.x, self.dragStart[1] - event.y) # move event if self.panButton.config('relief')[-1] == SUNKEN: self.transform = self.canvas.canvas_vector_to_machine(vect) self.commands = translate_toolpath(self.commands, self.transform) self.canvas.draw_toolpath(self.commands[0:int(self.slider.get())]) self.dragStart[0] = event.x self.dragStart[1] = event.y # rotate event if self.rotateButton.config('relief')[-1] == SUNKEN: angle = math.atan2(vect[0], vect[1]) # atan2(y, x) or atan2(sin, cos) self.commands = rotate_toolpath( self.commands, (self.workAreaSize[0] / 2 + self.workAreaOrigin[0], self.workAreaSize[1] / 2 + self.workAreaOrigin[1]), -(self.transform - angle)) self.canvas.draw_toolpath(self.commands[0:int(self.slider.get())]) self.transform = angle # scale event if self.scaleButton.config('relief')[-1] == SUNKEN: factor = math.sqrt((vect[0])**2 + (vect[1])**2) / 500 f = factor - self.transform if vect[0] < 0: f = -f self.commands = scale_toolpath(self.commands, f) self.canvas.draw_toolpath(self.commands[0:int(self.slider.get())]) self.transform = factor def UpdatePath(self, val): if self.isJobRunning: return self.canvas.draw_toolpath(self.commands[0:int(val)]) def UpdateTimeEstLabel(self): # avg milimeters per second factor factor = 11.0 time_to_toolchange = (self.distancesList[self.currentToolChange] - self.distanceTraveled) / factor time_total = (self.distancesList[-1] - self.distanceTraveled) / factor self.timeLabel.config(text="%d m %d s/%d m %d s" % (time_to_toolchange / 60, time_to_toolchange % 60, time_total / 60, time_total % 60)) def GetPositionTimerTaks(self): if self.isConnected: def TimerCallback(response): response = self.positionResponseRegex.search(response) if response: pos = (float(response.group(1)), float(response.group(2))) self.canvas.move_pointer(pos) serial.queue_command("M114\n", TimerCallback, priority=-1) self.master.after(2000, self.GetPositionTimerTaks) def CleanUp(self): serial.close_serial() def PausePopup(self, newColor="black", trim=False): tl = Toplevel(root) tl.attributes('-topmost', 'true') tl.grab_set() tl.title("Tool change") msg = "change the tool for a next color" if trim: tl.title("Thread trim") msg = "cut the thread" frame = Frame(tl) frame.grid() canvas = Canvas(frame, width=64, height=64) canvas.grid(row=2, column=0) canvas.create_rectangle(0, 0, 65, 65, fill=newColor) msgbody = Label( frame, text= "There is the moment to %s. Resume the current job after change." % msg) msgbody.grid(row=1, column=0, sticky=N) okbttn = Button(frame, text="OK", command=lambda: tl.destroy(), width=10) okbttn.grid(row=2, column=2)
class ParametersWindow(Frame): def __init__(self, master=None, name=None, param_dict=None): Frame.__init__(self, master=master, name=name) self._master = master self._param_dict = param_dict self._name = name # Variable for keeping track regarding whether the body() was called before or not self._body_pack_checker = False self.misc_dir_path = "../bin/misc" self.config_file_path = self.misc_dir_path + "/sampling_config.yml" self.win_width = 1500 self.win_height = 750 self.sampling_width = 450 self.padding_x = 160 self.separator_pad = 50 self.param_width = self.win_width - self.sampling_width - self.separator_pad self.x_offset = int((self.winfo_screenwidth() - self.win_width) / 2) self.y_offset = int( (self.winfo_screenheight() - self.win_height - 50) / 2) self._geometry = "{:d}x{:d}+{:d}+{:d}".format(self.win_width, self.win_height, self.x_offset, self.y_offset) self.frame_width = self.param_width - self.padding_x self.frame_height = self.win_height / 13 self.config(width=self.win_width - self.padding_x, height=self.win_height - self.frame_height) self.pack_propagate(False) # For establishing which sample image will be affected by a parameter there will be three states as follows: # which = -1 - for parameters that affect only minimum # which = 0 - for parameters that affect both # which = 1 - for parameters that affect only maximum self.sampling_dict = { 'Template path': '', 'Output path': self.misc_dir_path, 'Which': 0, 'Counter': 0 } # Configuring sampling frame self.apply_filters = ApplyFiltersFrame(master=self, width=self.sampling_width, height=self.win_height) # Configuring title frame self.title_font = ('Segoe UI', 12) self.title_frame = Frame(self, width=self.frame_width, height=self.frame_height) self.title = "Parameters" self.W_geometry = {'orient': 'horizontal', 'length': 200} # Configuring main frame self.param_set_frame = Frame(self, name='param_set', width=self.frame_width, height=self.frame_height * 11) self.param_set_frame.pack_propagate(False) # Configuring radial distortion and halo frame self.distortion_n_halo_frame = Frame(self.param_set_frame, width=self.frame_width, height=self.frame_height) self.distortion_n_halo_frame.pack_propagate(False) self.radial_distortion_scale = Scale( self.distortion_n_halo_frame, self.W_geometry, name='dist', from_=0, to=0.0001, resolution=0.00001, command=lambda x: self.both_callback()) self.distortion_center_x = 0 self.distortion_center_y = 0 self.halo_scale = Scale(self.distortion_n_halo_frame, self.W_geometry, name='halo', from_=0, to=15, resolution=1, command=lambda x: self.both_callback()) # Configuring vertical perspective frame self.V_perspective_frame = Frame(self.param_set_frame, name='frame v_persp', width=self.frame_width, height=self.frame_height) self.V_perspective_frame.pack_propagate(False) self.min_V_perspective_scale = Scale( self.V_perspective_frame, self.W_geometry, name='min v_persp', from_=-0.5, to=0.5, resolution=0.1, command=lambda value: self.min_callback(value, 'min v_persp')) self.max_V_perspective_scale = Scale( self.V_perspective_frame, self.W_geometry, name='max v_persp', from_=-0.5, to=0.5, resolution=0.1, command=lambda value: self.max_callback(value, 'max v_persp')) # Configuring horizontal perspective frame self.H_perspective_frame = Frame(self.param_set_frame, name='frame h_persp', width=self.frame_width, height=self.frame_height) self.H_perspective_frame.pack_propagate(False) self.min_H_perspective_scale = Scale( self.H_perspective_frame, self.W_geometry, name='min h_persp', from_=-0.5, to=0.5, resolution=0.1, command=lambda value: self.min_callback(value, 'min h_persp')) self.max_H_perspective_scale = Scale( self.H_perspective_frame, self.W_geometry, name='max h_persp', from_=-0.5, to=0.5, resolution=0.1, command=lambda value: self.max_callback(value, 'max h_persp')) # Configuring brightness frame self.brightness_frame = Frame(self.param_set_frame, name='frame bright', width=self.frame_width, height=self.frame_height) self.brightness_frame.pack_propagate(False) self.min_brightness_scale = Scale( self.brightness_frame, self.W_geometry, name='min bright', from_=0, to=60, resolution=1, command=lambda value: self.min_callback(value, 'min bright')) self.max_brightness_scale = Scale( self.brightness_frame, self.W_geometry, name='max bright', from_=0, to=60, resolution=1, command=lambda value: self.max_callback(value, 'max bright')) # Configuring noise frame self.noise_frame = Frame(self.param_set_frame, name='frame noise', width=self.frame_width, height=self.frame_height) self.noise_frame.pack_propagate(False) self.min_noise_scale = Scale( self.noise_frame, self.W_geometry, name='min noise', from_=0, to=20, resolution=1, command=lambda value: self.min_callback(value, 'min noise')) self.max_noise_scale = Scale( self.noise_frame, self.W_geometry, name='max noise', from_=0, to=20, resolution=1, command=lambda value: self.max_callback(value, 'max noise')) # Configuring blur frame self.blur_frame = Frame(self.param_set_frame, name='frame blur', width=self.frame_width, height=self.frame_height) self.blur_frame.pack_propagate(False) self.min_blur_scale = Scale( self.blur_frame, self.W_geometry, name='min blur', from_=0, to=20, resolution=1, command=lambda value: self.min_callback(value, 'min blur')) self.max_blur_scale = Scale( self.blur_frame, self.W_geometry, name='max blur', from_=0, to=20, resolution=1, command=lambda value: self.max_callback(value, 'max blur')) # Configuring aberration frame self.aberration_frame = Frame(self.param_set_frame, width=self.frame_width, height=self.frame_height) self.aberration_frame.pack_propagate(False) self.V_aberration_scale = Scale(self.aberration_frame, self.W_geometry, name='v_aberration', from_=0, to=10, resolution=1, command=lambda x: self.both_callback()) self.H_aberration_scale = Scale(self.aberration_frame, self.W_geometry, name='h_aberration', from_=0, to=10, resolution=1, command=lambda x: self.both_callback()) # Configuring resize frame self.resize_frame = Frame(self.param_set_frame, name='frame resize', width=self.frame_width, height=self.frame_height) self.resize_frame.pack_propagate(False) self.min_resize_scale = Scale( self.resize_frame, self.W_geometry, name='min resize', from_=0, to=150, resolution=1, command=lambda value: self.min_callback(value, 'min resize')) self.max_resize_scale = Scale( self.resize_frame, self.W_geometry, name='max resize', from_=0, to=500, resolution=1, command=lambda value: self.max_callback(value, 'max resize')) # Configuring background enlargement frame self.enlarge_bg_frame = Frame(self.param_set_frame, width=self.frame_width, height=self.frame_height) self.enlarge_bg_frame.pack_propagate(False) self.V_max_enlargement_scale = Scale( self.enlarge_bg_frame, self.W_geometry, name='v_bg', from_=0, to=50, resolution=1, command=lambda x: self.calc_nr_of_samples_generated()) self.H_max_enlargement_scale = Scale( self.enlarge_bg_frame, self.W_geometry, name='h_bg', from_=0, to=50, resolution=1, command=lambda x: self.calc_nr_of_samples_generated()) # Configuring notes frame self.notes_frame = Frame(self.param_set_frame, width=self.frame_width, height=self.frame_height) self.notes_frame.pack_propagate(False) self.notes = "Note: Parameters that are set to 0 (at both minimum and maximum, where applicable) will not be " \ "applied." # Configuring buttons frame (SET DEFAULT, RESET and samples scale) self.buttons_frame = Frame(self.param_set_frame, width=self.frame_width, height=self.frame_height) self.buttons_frame.pack_propagate(False) self.generated_samples = 0 self.nr_of_samples_scale = Scale(self.buttons_frame, self.W_geometry) self.reset_button = Button(self.buttons_frame, text="Reset", width=10, command=self.reset_callback) self.default_button = Button(self.buttons_frame, text="Set default", width=15, command=self.default_callback) # Function sets geometry of master def set_geometry(self): self._master.geometry(self._geometry) # Function sets the body of the window def body(self): template_path = glob.glob(self._param_dict["Path to templates"] + '/*.png')[0].replace('\\', '/') self.sampling_dict["Template path"] = template_path # Setting title of the frame Label(self.title_frame, text=self.title, font=self.title_font).pack(side=TOP, anchor=CENTER) self.title_frame.pack(side=TOP, pady=10) # Setting sampling frame self.apply_filters.body_pack( side=LEFT, image_path=self.sampling_dict['Template path']) Separator(self, orient=VERTICAL).pack(side=LEFT, fill=Y, padx=self.separator_pad / 2) # Setting radial distortion and halo frame Label(self.distortion_n_halo_frame, text="Radial distortion:", width=13, anchor=W).pack(side=LEFT, anchor=S) self.radial_distortion_scale.pack(side=LEFT, anchor=S) self.halo_scale.pack(side=RIGHT, anchor=S) Label(self.distortion_n_halo_frame, text="Halo:", width=5, anchor=W).pack(side=RIGHT, anchor=S) self.distortion_n_halo_frame.pack(side=TOP) # Setting vertical perspective frame Label(self.V_perspective_frame, text="Vertical perspective:", width=30, anchor=W).pack(side=LEFT, anchor=S) Label(self.V_perspective_frame, text="Minimum:", width=10, anchor=W).pack(side=LEFT, anchor=S) self.min_V_perspective_scale.pack(side=LEFT, anchor=S) self.max_V_perspective_scale.pack(side=RIGHT, anchor=S) Label(self.V_perspective_frame, text="Maximum:", width=10, anchor=W).pack(side=RIGHT, anchor=S) self.V_perspective_frame.pack(side=TOP) # Setting horizontal perspective frame Label(self.H_perspective_frame, text="Horizontal perspective:", width=30, anchor=W).pack(side=LEFT, anchor=S) Label(self.H_perspective_frame, text="Minimum:", width=10, anchor=W).pack(side=LEFT, anchor=S) self.min_H_perspective_scale.pack(side=LEFT, anchor=S) self.max_H_perspective_scale.pack(side=RIGHT, anchor=S) Label(self.H_perspective_frame, text="Maximum:", width=10, anchor=W).pack(side=RIGHT, anchor=S) self.H_perspective_frame.pack(side=TOP) # Setting brightness frame Label(self.brightness_frame, text="Brightness:", width=30, anchor=W).pack(side=LEFT, anchor=S) Label(self.brightness_frame, text="Minimum:", width=10, anchor=W).pack(side=LEFT, anchor=S) self.min_brightness_scale.pack(side=LEFT, anchor=S) self.max_brightness_scale.pack(side=RIGHT, anchor=S) Label(self.brightness_frame, text="Maximum:", width=10, anchor=W).pack(side=RIGHT, anchor=S) self.brightness_frame.pack(side=TOP) # Setting noise frame Label(self.noise_frame, text="Noise:", width=30, anchor=W).pack(side=LEFT, anchor=S) Label(self.noise_frame, text="Minimum:", width=10, anchor=W).pack(side=LEFT, anchor=S) self.min_noise_scale.pack(side=LEFT, anchor=S) self.max_noise_scale.pack(side=RIGHT, anchor=S) Label(self.noise_frame, text="Maximum:", width=10, anchor=W).pack(side=RIGHT, anchor=S) self.noise_frame.pack(side=TOP) # setting blur frame Label(self.blur_frame, text="Blur:", width=30, anchor=W).pack(side=LEFT, anchor=S) Label(self.blur_frame, text="Minimum:", width=10, anchor=W).pack(side=LEFT, anchor=S) self.min_blur_scale.pack(side=LEFT, anchor=S) self.max_blur_scale.pack(side=RIGHT, anchor=S) Label(self.blur_frame, text="Maximum:", width=10, anchor=W).pack(side=RIGHT, anchor=S) self.blur_frame.pack(side=TOP) # Setting aberration frame Label(self.aberration_frame, text="Aberration:", width=30, anchor=W).pack(side=LEFT, anchor=S) Label(self.aberration_frame, text="Vertical:", width=10, anchor=W).pack(side=LEFT, anchor=S) self.V_aberration_scale.pack(side=LEFT, anchor=S) self.H_aberration_scale.pack(side=RIGHT, anchor=S) Label(self.aberration_frame, text="Horizontal:", width=10, anchor=W).pack(side=RIGHT, anchor=S) self.aberration_frame.pack(side=TOP) # Setting resize frame Label(self.resize_frame, text="Resize:", width=30, anchor=W).pack(side=LEFT, anchor=S) Label(self.resize_frame, text="Minimum:", width=10, anchor=W).pack(side=LEFT, anchor=S) self.min_resize_scale.pack(side=LEFT, anchor=S) self.max_resize_scale.pack(side=RIGHT, anchor=S) Label(self.resize_frame, text="Maximum", width=10, anchor=W).pack(side=RIGHT, anchor=S) self.resize_frame.pack(side=TOP) # Setting background enlargement frame Label(self.enlarge_bg_frame, text="Maximum background enlargement:", width=30, anchor=W).pack(side=LEFT, anchor=S) Label(self.enlarge_bg_frame, text="Vertical:", width=10, anchor=W).pack(side=LEFT, anchor=S) self.V_max_enlargement_scale.pack(side=LEFT, anchor=S) self.H_max_enlargement_scale.pack(side=RIGHT, anchor=S) Label(self.enlarge_bg_frame, text="Horizontal:", width=10, anchor=W).pack(side=RIGHT, anchor=S) self.enlarge_bg_frame.pack(side=TOP) # Setting notes frame Label(self.notes_frame, text=self.notes, fg='gray').pack(side=BOTTOM, anchor=W) self.notes_frame.pack(side=TOP) # Setting SET DEFAULT, RESET and samples scale buttons frame self.default_button.pack(side=LEFT, anchor=S) self.reset_button.pack(side=LEFT, anchor=S, padx=35) self.calc_nr_of_samples_generated() self.nr_of_samples_scale.pack(side=RIGHT, anchor=S) Label(self.buttons_frame, text="Samples generated per class:", anchor=W).pack(side=RIGHT, anchor=S) self.buttons_frame.pack(side=TOP) self.param_set_frame.pack(side=LEFT, fill=Y) self._body_pack_checker = True # Function verifies if body() was called before and packs up the frame def body_pack(self): self.set_geometry() if self._body_pack_checker is False: self.body() self.pack(side=TOP) # Callback function for the RESET button, setting all scales to 0(zero) def reset_callback(self): self.min_resize_scale.set(0) self.max_resize_scale.set(0) self.min_V_perspective_scale.set(0) self.max_V_perspective_scale.set(0) self.min_H_perspective_scale.set(0) self.max_H_perspective_scale.set(0) self.min_brightness_scale.set(0) self.max_brightness_scale.set(0) self.min_noise_scale.set(0) self.max_noise_scale.set(0) self.min_blur_scale.set(0) self.max_blur_scale.set(0) self.V_aberration_scale.set(0) self.H_aberration_scale.set(0) self.V_max_enlargement_scale.set(0) self.H_max_enlargement_scale.set(0) self.radial_distortion_scale.set(0.0) self.halo_scale.set(0) # Callback function for the SET DEFAULT button, setting all scales to values defined in default configuration file def default_callback(self): if self._param_dict: self.min_resize_scale.set(self._param_dict["Min resize"]) self.max_resize_scale.set(self._param_dict["Max resize"]) self.min_V_perspective_scale.set( self._param_dict["Min V perspective"]) self.max_V_perspective_scale.set( self._param_dict["Max V perspective"]) self.min_H_perspective_scale.set( self._param_dict["Min H perspective"]) self.max_H_perspective_scale.set( self._param_dict["Max H perspective"]) self.min_brightness_scale.set(self._param_dict["Min light"]) self.max_brightness_scale.set(self._param_dict["Max light"]) self.min_noise_scale.set(self._param_dict["Min noise value"]) self.max_noise_scale.set(self._param_dict["Max noise value"]) self.min_blur_scale.set(self._param_dict["Min blur amplitude"]) self.max_blur_scale.set(self._param_dict["Max blur amplitude"]) self.V_aberration_scale.set( self._param_dict["Vertical max aberration"]) self.H_aberration_scale.set( self._param_dict["Horizontal max aberration"]) self.radial_distortion_scale.set( self._param_dict["Radial distortion"]) self.halo_scale.set(self._param_dict["Halo amount"]) self.V_max_enlargement_scale.set( self._param_dict["Max enlarge background vertical"]) self.H_max_enlargement_scale.set( self._param_dict["Max enlarge background horizontal"]) # Function returns the distortion center used for the fish eye effect. Based on dims of template images def calc_distortion_center(self): # List containing template images images_path_list = glob.glob(self._param_dict["Path to templates"] + '/*.png') # Lists to collect dims of the images widths = [] heights = [] for image_path in images_path_list: image = Image.open(image_path) widths.append(image.size[0]) heights.append(image.size[1]) x_center_distortion = random.randint(1, min(widths)) y_center_distortion = random.randint(1, min(heights)) return x_center_distortion, y_center_distortion # Callback function for parameters that apply only to minimum sampling images def min_callback(self, value=None, name=None): self.correlate_buttons_by_min(value=value, name=name) self.sampling_dict['Which'] = -1 self.write_sampling_configuration() self.apply_filters.sampling_filters(self.config_file_path) self.calc_nr_of_samples_generated() # Function correlates a max button to a min value when min is higher than max def correlate_buttons_by_min(self, value=None, name=None): max_button_name = name.replace('min', 'max') frame_name = name.replace('min', 'frame') path_to_button = '.' + self.winfo_name( ) + '.param_set.' + frame_name + '.' + max_button_name if value.find('.'): value = float(value) else: value = int(value) if value > self.nametowidget(path_to_button).get(): self.nametowidget(path_to_button).set(value) # Callback function for parameters that apply only to maximum images def max_callback(self, value=None, name=None): self.correlate_buttons_by_max(value=value, name=name) self.sampling_dict['Which'] = 1 self.write_sampling_configuration() self.apply_filters.sampling_filters(self.config_file_path) self.calc_nr_of_samples_generated() # Function correlates a min button to a max value when max is lower than min def correlate_buttons_by_max(self, value=None, name=None): max_button_name = name.replace('max', 'min') frame_name = name.replace('max', 'frame') path_to_button = '.' + self.winfo_name( ) + '.param_set.' + frame_name + '.' + max_button_name if value.find('.'): value = float(value) else: value = int(value) if value < self.nametowidget(path_to_button).get(): self.nametowidget(path_to_button).set(value) # Callback function for parameters that apply to both sampling images def both_callback(self): self.sampling_dict['Which'] = 0 self.write_sampling_configuration() self.apply_filters.sampling_filters(self.config_file_path) self.calc_nr_of_samples_generated() # Function overwrites the sampling configuration file def write_sampling_configuration(self): import os if not os.path.exists(self.misc_dir_path): os.makedirs(self.misc_dir_path) yaml_file = open(self.config_file_path, 'w') yaml_file.write("%YAML 1.0\n") yaml_file.write("---") yaml_file.write("\nPaths:\n") yaml_file.write(" Template path: \"{:}\"\n".format( self.sampling_dict["Template path"])) yaml_file.write(" Output path: \"{:}\"\n".format( self.sampling_dict["Output path"])) yaml_file.write("\nWhich: {:}\n".format(self.sampling_dict["Which"])) self.sampling_dict["Counter"] = self.sampling_dict["Counter"] + 1 yaml_file.write("\nCounter: {:}\n".format( self.sampling_dict["Counter"])) yaml_file.write("\nMinimum:\n") yaml_file.write(" Resize: {:}\n".format( self.min_resize_scale.get())) yaml_file.write(" V perspective: {:}\n".format( self.min_V_perspective_scale.get())) yaml_file.write(" H perspective: {:}\n".format( self.min_H_perspective_scale.get())) yaml_file.write(" Brightness: {:}\n".format( self.min_brightness_scale.get())) yaml_file.write(" Noise: {:}\n".format(self.min_noise_scale.get())) yaml_file.write(" Blur: {:}\n".format(self.min_blur_scale.get())) yaml_file.write("\nMaximum:\n") yaml_file.write(" Resize: {:}\n".format( self.max_resize_scale.get())) yaml_file.write(" V perspective: {:}\n".format( self.max_V_perspective_scale.get())) yaml_file.write(" H perspective: {:}\n".format( self.max_H_perspective_scale.get())) yaml_file.write(" Brightness: {:}\n".format( self.max_brightness_scale.get())) yaml_file.write(" Noise: {:}\n".format(self.max_noise_scale.get())) yaml_file.write(" Blur: {:}\n".format(self.max_blur_scale.get())) if self.radial_distortion_scale.get() is not 0: if self.distortion_center_y is 0 and self.distortion_center_y is 0: self.distortion_center_x, self.distortion_center_y = self.calc_distortion_center( ) else: self.distortion_center_x = 0 self.distortion_center_y = 0 yaml_file.write("\nOther:\n") yaml_file.write(" V aberration: {:}\n".format( self.V_aberration_scale.get())) yaml_file.write(" H aberration: {:}\n".format( self.H_aberration_scale.get())) yaml_file.write(" Radial distortion: {:}\n".format( self.radial_distortion_scale.get())) yaml_file.write(" X center distortion: {:}\n".format( self.distortion_center_x)) yaml_file.write(" Y center distortion: {:}\n".format( self.distortion_center_y)) yaml_file.write(" Halo: {:}\n".format(self.halo_scale.get())) yaml_file.close() # Function calculates the number of samples that will be generated def calc_nr_of_samples_generated(self): if self.min_H_perspective_scale.get( ) == 0.0 and self.max_H_perspective_scale.get() == 0.0: h_perspective_cycles = 1 else: h_perspective_cycles = NUMBER_OF_PERSPECTIVES if self.min_V_perspective_scale.get( ) == 0.0 and self.max_V_perspective_scale.get() == 0.0: v_perspective_cycles = 1 else: v_perspective_cycles = NUMBER_OF_PERSPECTIVES if self.min_blur_scale.get() == 0 and self.max_blur_scale.get() == 0: blur_cycles = 1 else: blur_cycles = self.max_blur_scale.get() - self.min_blur_scale.get( ) + 1 self.generated_samples = NUMBER_OF_ILLUMINATIONS * h_perspective_cycles * v_perspective_cycles * blur_cycles if self.generated_samples < 0: self.nr_of_samples_scale.config(label='N/A', from_=0, to=0) else: self.nr_of_samples_scale.config(from_=self.generated_samples, to=(self.generated_samples * 5), label='', resolution=self.generated_samples) self.nr_of_samples_scale.set(self.generated_samples) # Functions passes configuration into parameter _dict def get_parameters(self, _dict): _dict["Min resize"] = self.min_resize_scale.get() _dict["Max resize"] = self.max_resize_scale.get() _dict["Min H perspective"] = self.min_H_perspective_scale.get() _dict["Max H perspective"] = self.max_H_perspective_scale.get() _dict["Min V perspective"] = self.min_V_perspective_scale.get() _dict["Max V perspective"] = self.max_V_perspective_scale.get() _dict["Min light"] = self.min_brightness_scale.get() _dict["Max light"] = self.max_brightness_scale.get() _dict["Min noise value"] = self.min_noise_scale.get() _dict["Max noise value"] = self.max_noise_scale.get() _dict["Min blur amplitude"] = self.min_blur_scale.get() _dict["Max blur amplitude"] = self.max_blur_scale.get() _dict["Vertical max aberration"] = self.V_aberration_scale.get() _dict["Horizontal max aberration"] = self.H_aberration_scale.get() _dict["Radial distortion"] = self.radial_distortion_scale.get() if _dict["Radial distortion"] == 0: _dict["X center distortion"] = 0 _dict["Y center distortion"] = 0 else: _dict["X center distortion"], _dict[ "Y center distortion"] = self.calc_distortion_center() _dict["Halo amount"] = self.halo_scale.get() _dict[ "Max enlarge background vertical"] = self.V_max_enlargement_scale.get( ) _dict[ "Max enlarge background horizontal"] = self.H_max_enlargement_scale.get( ) _dict["Number of samples per class"] = self.generated_samples _dict["Number of generations"] = self.nr_of_samples_scale.get( ) / self.generated_samples # Function calls Apply_filters_frame 'delete_misc_content' function, passing its on files to delete def delete_misc(self): self.apply_filters.delete_misc_content( other_files=self.config_file_path)
class Main(Frame): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.root = self.winfo_toplevel() self.root.option_add("*Font", "serif 10 bold") self.root.title(" ".join((APPNAME, VERSION, "| Main"))) self.root.resizable(0,0) self.root.bind('<Key>', self.__keys) # Menu menubar = Menu(self.root) self.windowsmenu = Menu(menubar, tearoff=0) self.windowsmenu.add_command(label="[ ] Report window", command=self.__toggle_report) self.windowsmenu.add_command(label="Plot current model", command=self.__plot) self.windowsmenu.add_separator() self.windowsmenu.add_command(label="Quit", command=self.destroy) menubar.add_cascade(label="Windows", menu=self.windowsmenu) settingsmenu = Menu(menubar, tearoff=0) settingsmenu.add_command(label="Spectrum range") settingsmenu.add_command(label="Color scheme") menubar.add_cascade(label="Settings", menu=settingsmenu) helpmenu = Menu(menubar, tearoff=0) helpmenu.add_command(label="Help", command=self.__help) helpmenu.add_separator() helpmenu.add_command(label="About", command=self.__about) menubar.add_cascade(label="Help", menu=helpmenu) self.root.config(menu=menubar) # disabled menu items settingsmenu.entryconfig(0, state='disabled') settingsmenu.entryconfig(1, state='disabled') helpmenu.entryconfig(0, state='disabled') # Scale ctrls = Labelframe(self, text='Control', relief='ridge') self.e = Scale(ctrls, from_=E_MIN, to=E_MAX, label='Links', bd=1, width=7, length=150, orient='horizontal') self.d = Scale(ctrls, from_=D_MIN, to=D_MAX, label='Pages', bd=1, width=7, length=150, orient='horizontal') self.e.set(E) self.d.set(D) self.e.config(command=self.__refresh) self.d.config(command=self.__refresh) self.e.pack(side='left') self.d.pack(side='right') self.r_win = False ctrls.pack(side='top', anchor='e', padx=7, pady=5) # initialize model self.generate_model() self.c = GraphViewer(self, D, self.edge_counts, self.pagerank) self.c.pack() # open windows self.__toggle_report() def generate_model(self): edges, self.edge_counts = randEdges(self.d.get(), self.e.get()) self.pagerank = pagerank(edges) def render(self): self.generate_model() self.c.render(self.d.get(), self.edge_counts, self.pagerank) try: self.r.render(self.pagerank, self.edge_counts, self.d.get(), self.e.get()) except: pass def __refresh(self, value=None): self.generate_model() self.render() def __toggle_report(self): if self.r_win: try: self.r.destroy() except: pass self.r_win = False self.windowsmenu.entryconfig(0, label='[ ] Report window') return self.r = Report(Toplevel(self)) self.r.pack() self.r.render(self.pagerank, self.edge_counts, self.d.get(), self.e.get()) self.r_win = True self.windowsmenu.entryconfig(0, label='[•] Report window ') def __plot(self): try: self.plot_root.destroy() except: pass self.plot_root = Toplevel(self) self.plot_root.title(" ".join((APPNAME, VERSION, "| Plot"))) self.plot_root.resizable(0,0) draw_figure(self.plot_root, self.pagerank) self.root.after(1, lambda : self.root.focus_force()) def __keys(self, event): if event.char == 'q': self.destroy() elif event.char == 'r': self.__toggle_report() elif event.char == 'p': self.__plot() elif event.char == 'x': try: self.plot_root.destroy() except: pass elif event.char == 'n': self.__refresh() elif event.char == 'j': self.d.set(self.d.get()-1) elif event.char == 'J': self.d.set(self.d.get()-5) elif event.char == 'k': self.d.set(self.d.get()+1) elif event.char == 'K': self.d.set(self.d.get()+5) elif event.char == 'h': self.e.set(self.e.get()-1) elif event.char == 'H': self.e.set(self.e.get()-40) elif event.char == 'l': self.e.set(self.e.get()+1) elif event.char == 'L': self.e.set(self.e.get()+40) def __help(self): pass def __about(self): try: self.a.destroy() except: pass about_text = "{} {}\n\nUW MATH308(Linear Algebra) Winter 2018".format(APPNAME, VERSION) self.a = Toplevel(self) self.a.title(" ".join((APPNAME, VERSION, "| About"))) f = Frame(self.a) l = Label(f, text=about_text) b = Button(f, text='Close', command=self.a.destroy) l.pack(padx=5) b.pack(pady=10) f.pack(padx=50, pady=20) def destroy(self): super().destroy() self.root.destroy() exit(1)
class UI(tk.Tk): def __init__(self): super().__init__() __width_label_lc = 16 self.result_type_dict = [('mass', 'mass'), ('mass density', 'mass_density'), ('stiffness', 'stf'), ('shear capacity', 's_c'), ('drift', 'drift')] self.result_type_dict_right = [('displacement ratio', 'dsp_r'), ('drift ratio', 'dft_r'), ('spectrum of time history curve', 'thc'), ('base shear of time history', 'sth'), ('drift of time history', 'dth')] self.result_type_dict_added = [('period', 'period'), ('seismic shear coefficient', 's_s_c'), ('seismic shear adjust factor', 's_a_f') ] # variables of app self.file_path = FilePath() self.yjk_dir = tk.StringVar() self.etabs_name = tk.StringVar() self.result_type = tk.StringVar() self.result_type.set('drift') self.cur_tower_number = tk.StringVar() self.cur_merge_df = None self.tower_number = None self.position = None self.cur_result_type = None self.font = dict(family='KaiTi', color='black', weight='normal', size=10.5) # variables of YJK self.cur_yjk_class = None self.cur_yjk_dic = None self.cur_yjk_df = None self.df_yjk_export = None self.cur_yjk_lc = tk.StringVar() self.cur_yjk_tow_num_name = tk.StringVar() self.cur_yjk_flr_num_name = tk.StringVar() self.cur_yjk_content_name = tk.StringVar() self.lbl_yjk_tree_content = tk.StringVar() # variables of ETABS self.cur_etabs_dic = None self.cur_etabs_df = None self.df_etabs_export = None self.cur_etabs_lc = tk.StringVar() self.cur_etabs_tow_num_name = tk.StringVar() self.cur_etabs_flr_num_name = tk.StringVar() self.cur_etabs_content_name = tk.StringVar() self.lbl_etabs_tree_content = tk.StringVar() self.yjk_disp = None # GUI self.minsize(1280, 800) self.title('ARP_AnalysisResultProcessing') # start widget in frame_yjk_path self.frame_yjk_path = Frame(self) self.entry_yjk_path = Entry(self.frame_yjk_path, textvariable=self.yjk_dir) self.entry_yjk_path.pack(side=tk.LEFT, padx=5, pady=5, expand=tk.YES, fill=tk.X) self.btn_yjk_path = Button(self.frame_yjk_path, text='YJK', width=16, command=self.get_path) self.btn_yjk_path.pack(side=tk.LEFT, padx=5, pady=5) self.frame_yjk_path.pack(side=tk.TOP, fill=tk.X) # end widget in frame_yjk_path # start widget in frame_etabs_name self.frame_etabs_name = Frame(self) self.entry_etabs_name = Entry(self.frame_etabs_name, textvariable=self.etabs_name) self.entry_etabs_name.pack(side=tk.LEFT, padx=5, pady=5, expand=tk.YES, fill=tk.X) self.btn_etabs_path = Button(self.frame_etabs_name, text='ETABS', width=16, command=self.get_file_name) self.btn_etabs_path.pack(side=tk.LEFT, padx=5, pady=5) self.frame_etabs_name.pack(side=tk.TOP, fill=tk.X) # end widget in frame_etabs_name # start widget in frame_result_list self.frame_result_list = Frame(self) # option buttons of results self.lf_result_name = tk.LabelFrame(self.frame_result_list, text='Result to processing') self.frame_result_name_left = Frame(self.lf_result_name) for name, value in self.result_type_dict: _ = tk.Radiobutton(self.frame_result_name_left, text=name, value=value, anchor=tk.W, command=self.get_type, variable=self.result_type) _.pack(side=tk.TOP, fill=tk.X, padx=5) self.frame_result_name_left.pack(side=tk.LEFT, expand=tk.YES, fill=tk.X) self.frame_result_name_right = Frame(self.lf_result_name) for name, value in self.result_type_dict_right: _ = tk.Radiobutton(self.frame_result_name_right, text=name, value=value, anchor=tk.W, command=self.get_type, variable=self.result_type) _.pack(side=tk.TOP, fill=tk.X, padx=5) self.frame_result_name_right.pack(side=tk.LEFT, expand=tk.YES, fill=tk.X) self.frame_result_name_added = Frame(self.lf_result_name) for name, value in self.result_type_dict_added: _ = tk.Radiobutton(self.frame_result_name_added, text=name, value=value, anchor=tk.NW, command=self.get_type, variable=self.result_type) _.pack(side=tk.TOP, fill=tk.X, padx=5) self.frame_result_name_added.pack(side=tk.LEFT, expand=tk.YES, fill=tk.X, anchor=tk.N) self.lf_result_name.pack(side=tk.TOP, fill=tk.X) # labelframe of yjk results self.lf_yjk = tk.LabelFrame(self.frame_result_list, text='YJK') # load combination cmb self.frame_yjk_lc = tk.Frame(self.lf_yjk) self.lbl_yjk_lc = tk.Label(self.frame_yjk_lc, width=__width_label_lc, text='Load Combination', anchor=tk.W) self.lbl_yjk_lc.pack(side=tk.LEFT, fill=tk.X) self.cmb_yjk_lc = ttk.Combobox(self.frame_yjk_lc, state="readonly", height=1, textvariable=self.cur_yjk_lc) self.cmb_yjk_lc.pack(side=tk.LEFT, expand=tk.YES, fill=tk.X, padx=5, pady=5) self.frame_yjk_lc.pack(side=tk.TOP, fill=tk.X) # tower number cmb self.frame_yjk_tower = tk.Frame(self.lf_yjk) self.lbl_yjk_tower = tk.Label(self.frame_yjk_tower, width=__width_label_lc, text='Tower Number', anchor=tk.W) self.lbl_yjk_tower.pack(side=tk.LEFT, fill=tk.X) self.cmb_yjk_tower = ttk.Combobox( self.frame_yjk_tower, state="readonly", height=1, textvariable=self.cur_yjk_tow_num_name) self.cmb_yjk_tower.pack(side=tk.LEFT, expand=tk.YES, fill=tk.X, padx=5, pady=5) self.frame_yjk_tower.pack(side=tk.TOP, fill=tk.X) # floor number cmb self.frame_yjk_floor = tk.Frame(self.lf_yjk) self.lbl_yjk_floor = tk.Label(self.frame_yjk_floor, width=__width_label_lc, text='Floor Number', anchor=tk.W) self.lbl_yjk_floor.pack(side=tk.LEFT, fill=tk.X) self.cmb_yjk_floor = ttk.Combobox( self.frame_yjk_floor, state="readonly", height=1, textvariable=self.cur_yjk_flr_num_name) self.cmb_yjk_floor.pack(side=tk.LEFT, expand=tk.YES, fill=tk.X, padx=5, pady=5) self.frame_yjk_floor.pack(side=tk.TOP, fill=tk.X) # result content cmb self.frame_yjk_content = tk.Frame(self.lf_yjk) self.lbl_yjk_content = tk.Label(self.frame_yjk_content, width=__width_label_lc, text='Content', anchor=tk.W) self.lbl_yjk_content.pack(side=tk.LEFT, fill=tk.X) self.cmb_yjk_content = ttk.Combobox( self.frame_yjk_content, state="readonly", height=1, textvariable=self.cur_yjk_content_name) self.cmb_yjk_content.pack(side=tk.LEFT, expand=tk.YES, fill=tk.X, padx=5, pady=5) self.frame_yjk_content.pack(side=tk.TOP, fill=tk.X) # self.yjk_tree self.yjk_tree = ttk.Treeview(self.lf_yjk, show='headings') self.yjk_tree['column'] = ['a', 'b', 'c'] self.yjk_tree.column('a', width=18) self.yjk_tree.column('b', width=18) self.yjk_tree.column('c', width=18) self.yjk_tree.heading('a', text='Tower No.') self.yjk_tree.heading('b', text='Floor No.') self.yjk_tree.heading('c', text='content') self.yjk_tree.pack(side=tk.TOP, expand=tk.YES, fill=tk.BOTH) self.scr_yjk_tree = tk.Scrollbar(self.yjk_tree) self.scr_yjk_tree.pack(side=tk.RIGHT, fill=tk.Y) self.yjk_tree.config(yscrollcommand=self.scr_yjk_tree.set) self.scr_yjk_tree.config(command=self.yjk_tree.yview) # self.lbl_yjk_tree self.lbl_yjk_tree = tk.Label(self.lf_yjk, anchor=tk.W, textvariable=self.lbl_yjk_tree_content) self.lbl_yjk_tree.pack(side=tk.TOP, fill=tk.X) self.lf_yjk.pack(side=tk.TOP, expand=tk.YES, fill=tk.BOTH) # end of labelframe yjk results # labelframe of etabs results self.lf_etabs = tk.LabelFrame(self.frame_result_list, text='ETABS') # load combination cmb self.frame_etabs_lc = tk.Frame(self.lf_etabs) self.lbl_etabs_lc = tk.Label(self.frame_etabs_lc, width=__width_label_lc, text='Load Combination', anchor=tk.W) self.lbl_etabs_lc.pack(side=tk.LEFT, fill=tk.X) self.cmb_etabs_lc = ttk.Combobox(self.frame_etabs_lc, state="readonly", height=1, textvariable=self.cur_etabs_lc) self.cmb_etabs_lc.pack(side=tk.LEFT, expand=tk.YES, fill=tk.X, padx=5, pady=5) self.frame_etabs_lc.pack(side=tk.TOP, fill=tk.X) # tower number cmb self.frame_etabs_tower = tk.Frame(self.lf_etabs) self.lbl_etabs_tower = tk.Label(self.frame_etabs_tower, width=__width_label_lc, text='Tower Number', anchor=tk.W) self.lbl_etabs_tower.pack(side=tk.LEFT, fill=tk.X) self.cmb_etabs_tower = ttk.Combobox( self.frame_etabs_tower, state="readonly", height=1, textvariable=self.cur_etabs_tow_num_name) self.cmb_etabs_tower.pack(side=tk.LEFT, expand=tk.YES, fill=tk.X, padx=5, pady=5) self.frame_etabs_tower.pack(side=tk.TOP, fill=tk.X) # floor number cmb self.frame_etabs_floor = tk.Frame(self.lf_etabs) self.lbl_etabs_floor = tk.Label(self.frame_etabs_floor, width=__width_label_lc, text='Floor Number', anchor=tk.W) self.lbl_etabs_floor.pack(side=tk.LEFT, fill=tk.X) self.cmb_etabs_floor = ttk.Combobox( self.frame_etabs_floor, state="readonly", height=1, textvariable=self.cur_etabs_flr_num_name) self.cmb_etabs_floor.pack(side=tk.LEFT, expand=tk.YES, fill=tk.X, padx=5, pady=5) self.frame_etabs_floor.pack(side=tk.TOP, fill=tk.X) # result content cmb self.frame_etabs_content = tk.Frame(self.lf_etabs) self.lbl_etabs_content = tk.Label(self.frame_etabs_content, width=__width_label_lc, text='Content', anchor=tk.W) self.lbl_etabs_content.pack(side=tk.LEFT, fill=tk.X) self.cmb_etabs_content = ttk.Combobox( self.frame_etabs_content, state="readonly", height=1, textvariable=self.cur_etabs_content_name) self.cmb_etabs_content.pack(side=tk.LEFT, expand=tk.YES, fill=tk.X, padx=5, pady=5) self.frame_etabs_content.pack(side=tk.TOP, fill=tk.X) # self.etabs_tree self.etabs_tree = ttk.Treeview(self.lf_etabs, show='headings') self.etabs_tree['column'] = ['a', 'b', 'c'] self.etabs_tree.column('a', width=18) self.etabs_tree.column('b', width=18) self.etabs_tree.column('c', width=18) self.etabs_tree.heading('a', text='Tower No.') self.etabs_tree.heading('b', text='Floor No.') self.etabs_tree.heading('c', text='content') self.etabs_tree.pack(side=tk.TOP, expand=tk.YES, fill=tk.BOTH) self.scr_etabs_tree = tk.Scrollbar(self.etabs_tree) self.scr_etabs_tree.pack(side=tk.RIGHT, fill=tk.Y) self.etabs_tree.config(yscrollcommand=self.scr_etabs_tree.set) self.scr_etabs_tree.config(command=self.etabs_tree.yview) # self.lbl_etabs_tree self.lbl_etabs_tree = tk.Label( self.lf_etabs, anchor=tk.W, textvariable=self.lbl_etabs_tree_content) self.lbl_etabs_tree.pack(side=tk.TOP, fill=tk.X) self.lf_etabs.pack(side=tk.TOP, expand=tk.YES, fill=tk.BOTH) # self.lf_data_import self.lf_data_import = tk.LabelFrame(self.frame_result_list, text='action') # button yjk import self.btn_yjk_import = Button(self.lf_data_import, text='add YJK data', width=18, command=self.import_yjk) self.btn_yjk_import.pack(side=tk.LEFT, anchor=tk.W, padx=36) # button etabs import self.btn_etabs_import = Button(self.lf_data_import, text='add ETABS data', width=18, command=self.import_etabs) self.btn_etabs_import.pack(side=tk.LEFT, anchor=tk.CENTER, padx=36) # button merge and import self.btn_merge = Button(self.lf_data_import, text='merge and add', width=18, command=self.merge_import) self.btn_merge.pack(side=tk.LEFT, anchor=tk.E, padx=36) self.lf_data_import.pack(side=tk.TOP, fill=tk.X) self.frame_result_list.pack(side=tk.LEFT, expand=tk.YES, fill=tk.BOTH, padx=5, pady=5) # end widget in frame_result_list # start widget in frame_data_table self.frame_data_table = Frame(self) self.frame_table = Frame(self.frame_data_table) self.data_table = Table(self.frame_table, rows=50, cols=10, showstatusbar=True) self.data_table.show() self.frame_table.pack(side=tk.TOP, expand=tk.YES, fill=tk.BOTH) self.frame_tower = Frame(self.frame_data_table) self.lbl_tower = Label(self.frame_data_table, text='tower No.').pack(side=tk.BOTTOM) self.tower_scale = Scale(self.frame_data_table, orient=tk.HORIZONTAL, length=300, width=16, sliderlength=10, from_=0, to=10, tickinterval=1, variable=self.cur_tower_number, command=self.tower_select) self.tower_scale.pack(side=tk.BOTTOM, fill=tk.BOTH) self.frame_tower.pack(side=tk.TOP, fill=tk.X) self.frame_data_table.pack(side=tk.LEFT, expand=tk.YES, fill=tk.BOTH) # end widget in frame_data_table # start widget in frame_image self.frame_image = Frame(self) self.fig = Figure(figsize=(3.378, 4.331), dpi=150) self.ax = self.fig.add_subplot(111) self.canvas = FigureCanvasTkAgg(self.fig, master=self.frame_image) self.canvas.get_tk_widget().pack(side=tk.TOP) self.canvas.draw() toolbar = NavigationToolbar2Tk(self.canvas, self.frame_image) toolbar.update() self.canvas.get_tk_widget().pack(side=tk.TOP) self.lf_plot_control = tk.LabelFrame(self.frame_image, text='plot') self.lbl = Label(self.lf_plot_control, text='none').pack(side=tk.LEFT, fill=tk.X) self.btn_batch_plot = Button(self.lf_plot_control, text='batch plot', command=self.batch_plot) self.btn_batch_plot.pack(side=tk.RIGHT) self.lf_plot_control.pack(side=tk.BOTTOM, fill=tk.X) self.frame_image.pack(side=tk.LEFT, expand=tk.YES, fill=tk.BOTH) # end widget in frame_image self.data_table.zoomOut() self.data_table.zoomOut() self.action_bind() def action_bind(self): # action when yjk cmb selected self.cmb_yjk_lc.bind("<<ComboboxSelected>>", self.update_yjk_cmb) self.cmb_yjk_tower.bind("<<ComboboxSelected>>", self.update_yjk_tree) self.cmb_yjk_floor.bind("<<ComboboxSelected>>", self.update_yjk_tree) self.cmb_yjk_content.bind("<<ComboboxSelected>>", self.update_yjk_tree) # action when etabs cmb selected self.cmb_etabs_lc.bind("<<ComboboxSelected>>", self.update_etabs_cmb) self.cmb_etabs_tower.bind("<<ComboboxSelected>>", self.update_etabs_tree) self.cmb_etabs_floor.bind("<<ComboboxSelected>>", self.update_etabs_tree) self.cmb_etabs_content.bind("<<ComboboxSelected>>", self.update_etabs_tree) def get_path(self): """ get path of yjk result by FilePath class. update yjk widgets. :return: """ self.file_path.get_path() self.entry_yjk_path.delete(0, tk.END) self.entry_yjk_path.insert(tk.END, self.file_path.yjk_path) self.yjk_update() def get_file_name(self): """ get filename of etabs result in excel format by FilePath class. update etabs widgets. :return: """ self.file_path.get_path(openfile=True) self.entry_etabs_name.delete(0, tk.END) self.entry_etabs_name.insert(tk.END, self.file_path.etabs_name) if self.file_path.etabs_name is not None and self.file_path.etabs_name != '': self.etabs_update() def get_type(self): self.yjk_update() self.etabs_update() def yjk_update(self): if self.file_path.yjk_path is not None and os.path.exists( self.file_path.yjk_path) is True: self.cur_result_type = self.result_type.get() if self.cur_result_type == 'drift': with open(self.file_path.full_name('Wdisp.out'), 'r') as f: self.cur_yjk_class = Wdisp.Wdisp(f.read()) self.cmb_yjk_lc['values'] = self.cur_yjk_class.cqc self.cmb_yjk_lc.current(0) self.cmb_yjk_lc.configure( height=len(self.cur_yjk_class.cqc)) elif self.cur_result_type == 'dsp_r' or self.result_type.get( ) == 'dft_r': with open(self.file_path.full_name('Wdisp.out'), 'r') as f: self.cur_yjk_class = Wdisp.Wdisp(f.read()) self.cmb_yjk_lc['values'] = self.cur_yjk_class.spc_lateral self.cmb_yjk_lc.current(0) self.cmb_yjk_lc.configure( height=len(self.cur_yjk_class.spc_lateral)) elif self.cur_result_type == 'mass': with open(self.file_path.full_name('wmass.out'), 'r') as f: self.cur_yjk_class = Wmass.Wmass(f.read()) self.cmb_yjk_lc['values'] = ['mass_info'] self.cmb_yjk_lc.current(0) elif self.cur_result_type == 'mass_density': with open(self.file_path.full_name('wmass.out'), 'r') as f: self.cur_yjk_class = Wmass.Wmass(f.read()) self.cmb_yjk_lc['values'] = ['mass_density_detail'] self.cmb_yjk_lc.current(0) elif self.cur_result_type == 'stf': with open(self.file_path.full_name('wmass.out'), 'r') as f: self.cur_yjk_class = Wmass.Wmass(f.read()) self.cmb_yjk_lc['values'] = ['stiffness_detail'] self.cmb_yjk_lc.current(0) elif self.cur_result_type == 's_c': with open(self.file_path.full_name('wmass.out'), 'r') as f: self.cur_yjk_class = Wmass.Wmass(f.read()) self.cmb_yjk_lc['values'] = ['storey_shear_capacity_info'] self.cmb_yjk_lc.current(0) elif self.cur_result_type == 'period': with open(self.file_path.full_name('wzq.out'), 'r') as f: self.cur_yjk_class = Wzq.Wzq(f.read()) self.cmb_yjk_lc['values'] = ['period'] self.cmb_yjk_lc.current(0) elif self.cur_result_type == 's_a_f': with open(self.file_path.full_name('wzq.out'), 'r') as f: self.cur_yjk_class = Wzq.Wzq(f.read()) self.cmb_yjk_lc['values'] = ['shear_adjust_factor'] self.cmb_yjk_lc.current(0) elif self.cur_result_type == 's_s_c': with open(self.file_path.full_name('wzq.out'), 'r') as f: self.cur_yjk_class = Wzq.Wzq(f.read()) self.cmb_yjk_lc['values'] = self.cur_yjk_class.s_s_c self.cmb_yjk_lc.current(0) self.read_yjk_df() self.update_yjk_cmb(None) def read_yjk_df(self): if len(self.cmb_yjk_lc['values']) != 0: self.cur_yjk_df = getattr(self.cur_yjk_class, self.cur_yjk_lc.get()) def update_yjk_cmb(self, event): self.read_yjk_df() _columns = self.cur_yjk_df.columns.tolist() if self.result_type.get() == 'drift': self.position = (1, 0, len(_columns) - 3) elif self.result_type.get() == 'dsp_r': self.position = (1, 0, len(_columns) - 1) elif self.result_type.get() == 'dft_r': self.position = (1, 0, 5) elif self.result_type.get() == 'mass': self.position = (1, 0, 5) elif self.result_type.get() == 'mass_density': self.position = (1, 0, 3) elif self.result_type.get() == 'stf': self.position = (1, 0, len(_columns) - 3) elif self.result_type.get() == 's_c': self.position = (1, 0, len(_columns) - 2) elif self.cur_result_type == 'period': self.position = (0, 1, 3) elif self.cur_result_type == 's_a_f': self.position = (1, 0, 2) elif self.cur_result_type == 's_s_c': self.position = (1, 0, 4) self.cmb_yjk_tower['values'] = _columns self.cmb_yjk_tower.configure(height=len(_columns)) self.cmb_yjk_tower.current(self.position[0]) self.cmb_yjk_floor['values'] = _columns self.cmb_yjk_floor.configure(height=len(_columns)) self.cmb_yjk_floor.current(self.position[1]) self.cmb_yjk_content['values'] = _columns self.cmb_yjk_content.configure(height=len(_columns)) self.cmb_yjk_content.current(self.position[2]) self.update_yjk_tree(None) def update_yjk_tree(self, event): if self.cur_yjk_tow_num_name.get() is not None and\ self.cur_yjk_flr_num_name.get() is not None and \ self.cmb_yjk_content.get() is not None: self.df_yjk_export = self.cur_yjk_df[[ self.cur_yjk_tow_num_name.get(), self.cur_yjk_flr_num_name.get(), self.cur_yjk_content_name.get() ]] _content = self.yjk_tree.get_children() for _ in _content: self.yjk_tree.delete(_) for _ in range(len(self.df_yjk_export) - 1, -1, -1): self.yjk_tree.insert( '', 0, values=self.df_yjk_export.iloc[_].tolist()) self.lbl_yjk_tree_content.set( '%d row, %d columns' % (self.df_yjk_export.shape[0], self.df_yjk_export.shape[1])) def etabs_update(self): pass def update_etabs_cmb(self, event): pass def update_etabs_tree(self, event): pass def import_yjk(self): self.data_table.model.df = self.df_yjk_export number_groups = self.df_yjk_export.groupby( self.df_yjk_export.columns.tolist()[0]).ngroups self.tower_scale.config(to=number_groups) self.cur_tower_number.set(0) self.data_table.redraw() self.plot() def import_etabs(self): self.plot() pass def merge_import(self): self.plot() pass def tower_select(self, tower_number): group_by_tower = self.df_yjk_export.groupby( self.df_yjk_export.columns.tolist()[0]) tower_number = int(tower_number) if tower_number == 0: self.data_table.model.df = self.df_yjk_export else: self.data_table.model.df = group_by_tower.get_group(tower_number) self.data_table.redraw() self.plot() def plot(self): self.ax.clear() tower_number = int(self.cur_tower_number.get()) if tower_number == 0 and tower_number > 1: pass elif tower_number > 0: if self.result_type.get() == 'drift': self.ax.plot(1 / self.data_table.model.df.iloc[:, 2] * 1000, self.data_table.model.df.iloc[:, 1], marker='o') self.ax.grid(alpha=0.5, linestyle="-.", linewidth=0.3) self.ax.set_xlabel('位移角(1/1000)', fontdict=self.font) self.ax.set_ylabel('楼层', fontdict=self.font) self.fig.tight_layout(pad=0.3) elif self.result_type.get() == 'dsp_r': self.ax.plot(self.data_table.model.df.iloc[:, 2], self.data_table.model.df.iloc[:, 1], marker='o') self.ax.grid(alpha=0.5, linestyle="-.", linewidth=0.3) self.ax.set_xlabel('位移比', fontdict=self.font) self.ax.set_ylabel('楼层', fontdict=self.font) self.fig.tight_layout(pad=0.3) elif self.result_type.get() == 'dft_r': self.ax.plot(self.data_table.model.df.iloc[:, 2], self.data_table.model.df.iloc[:, 1], marker='o') self.ax.grid(alpha=0.5, linestyle="-.", linewidth=0.3) self.ax.set_xlabel('层间位移比', fontdict=self.font) self.ax.set_ylabel('楼层', fontdict=self.font) self.fig.tight_layout(pad=0.3) elif self.result_type.get() == 'mass': self.ax.plot(self.data_table.model.df.iloc[:, 2], self.data_table.model.df.iloc[:, 1], marker='o') self.ax.grid(alpha=0.5, linestyle="-.", linewidth=0.3) self.ax.set_xlabel('楼层质量', fontdict=self.font) self.ax.set_ylabel('楼层', fontdict=self.font) self.fig.tight_layout(pad=0.3) elif self.result_type.get() == 'mass_density': self.ax.plot(self.data_table.model.df.iloc[:, 2], self.data_table.model.df.iloc[:, 1], marker='o') self.ax.grid(alpha=0.5, linestyle="-.", linewidth=0.3) self.ax.set_xlabel('单位面积楼层质量', fontdict=self.font) self.ax.set_ylabel('楼层', fontdict=self.font) self.fig.tight_layout(pad=0.3) elif self.result_type.get() == 'stf': self.ax.plot(self.data_table.model.df.iloc[:, 2], self.data_table.model.df.iloc[:, 1], marker='o') self.ax.grid(alpha=0.5, linestyle="-.", linewidth=0.3) self.ax.set_xlabel('抗侧刚度(v/d)', fontdict=self.font) self.ax.set_ylabel('楼层', fontdict=self.font) self.fig.tight_layout(pad=0.3) elif self.result_type.get() == 's_c': self.ax.plot(self.data_table.model.df.iloc[:, 2], self.data_table.model.df.iloc[:, 1], marker='o') self.ax.grid(alpha=0.5, linestyle="-.", linewidth=0.3) self.ax.set_xlabel('楼层受剪承载力', fontdict=self.font) self.ax.set_ylabel('楼层', fontdict=self.font) self.fig.tight_layout(pad=0.3) elif self.cur_result_type == 'period': pass elif self.cur_result_type == 's_a_f': self.ax.plot(self.data_table.model.df.iloc[:, 2], self.data_table.model.df.iloc[:, 1], marker='o') self.ax.grid(alpha=0.5, linestyle="-.", linewidth=0.3) self.ax.set_xlabel('地震剪力放大系数', fontdict=self.font) self.ax.set_ylabel('楼层', fontdict=self.font) self.fig.tight_layout(pad=0.3) elif self.cur_result_type == 's_s_c': self.ax.plot(self.data_table.model.df.iloc[:, 2], self.data_table.model.df.iloc[:, 1], marker='o') self.ax.grid(alpha=0.5, linestyle="-.", linewidth=0.3) self.ax.set_xlabel('剪重比(%)', fontdict=self.font) self.ax.set_ylabel('楼层', fontdict=self.font) self.fig.tight_layout(pad=0.3) self.canvas.draw() pass def figure_save(self, fig_name=None): if fig_name is None: fig_name = '%s.png' % self.cur_yjk_lc.get() self.fig.savefig(self.file_path.result_name(fig_name), transparent=True, dpi=300, format='png') def batch_plot(self): result_type_dict = self.result_type_dict + self.result_type_dict_right + self.result_type_dict_added if self.yjk_dir.get() != '' or self.etabs_name.get() != '': for name, result_type in result_type_dict: self.result_type.set(result_type) self.yjk_update() for ct, lc_name in enumerate(self.cmb_yjk_lc['values']): self.cur_yjk_lc.set(lc_name) self.update_yjk_cmb(None) self.import_yjk() self.cur_tower_number.set(1) self.tower_select(1) f_name = '%s-%d-%s.png' % (name, ct, lc_name) self.figure_save(f_name)
class Sierpinski: def __init__(self, master): self.master = master # Window dimensions and fields self.depth = 1 self.index = 0 self.colors = ["white", "#444444"] self.width = 600 self.height = int(round(self.width * math.sqrt(3) / 2)) self.margin = 10 # Setup Tk Tk.config(master, bg=self.colors[self.index]) master.title("Sierpinski") master.iconbitmap('tri.ico') master.bind("<space>", self.invert) master.bind("<Key-Up>", self.updatedepth) master.bind("<Key-Down>", self.updatedepth) master.bind("<Key-Right>", self.updatedepth) master.bind("<Key-Left>", self.updatedepth) # setup window self.canvas = Canvas(master, width=self.width + (2 * self.margin), height=self.height + (2 * self.margin)) self.canvas.config(bg=self.colors[self.index], highlightthickness=0) self.canvas.bind("<Button-1>", self.invert) self.canvas.pack() # slider to set triangle depth self.level = Scale(master, from_=1, to=8, orient=HORIZONTAL, command=self.updatedepth) self.level.config(bg=self.colors[self.index], highlightthickness=0) self.level.pack() self.draw(self.level.get()) def updatedepth(self, event): try: if event.keysym in ("Up", "Right"): self.depth = 8 if self.depth == 8 else self.depth + 1 elif event.keysym in ("Down", "Left"): self.depth = 1 if self.depth == 1 else self.depth - 1 self.level.set(self.depth) except AttributeError: self.depth = self.level.get() self.draw(self.depth) def invert(self, event): self.index = 1 if self.index == 0 else 0 self.draw(self.depth) def draw(self, level): # clear canvas self.canvas.delete("all") x1 = self.margin y1 = self.margin + self.height x2 = self.margin + self.width / 2 y2 = self.margin x3 = self.margin + self.width y3 = self.margin + self.height self.canvas.create_polygon(x1, y1, x2, y2, x3, y3, fill=self.colors[self.index], outline=self.colors[self.index - 1]) self.recursion(int(level), x1, y1, x2, y2, x3, y3) def recursion(self, level, x1, y1, x2, y2, x3, y3): color = self.colors[self.index - 1] if level <= 1: self.canvas.create_polygon(x1, y1, x2, y2, x3, y3, fill=color) else: left_x = (x1 + x2) / 2 left_y = (y1 + y2) / 2 right_x = (x2 + x3) / 2 right_y = (y2 + y3) / 2 bottom_x = (x3 + x1) / 2 bottom_y = (y3 + y1) / 2 self.recursion(level - 1, x1, y1, left_x, left_y, bottom_x, bottom_y) self.recursion(level - 1, left_x, left_y, x2, y2, right_x, right_y) self.recursion(level - 1, bottom_x, bottom_y, right_x, right_y, x3, y3)
class EulerQuatGUI: def __init__(self, window): self.window = window self.window.title('Final Project') scalelength = 180 scalewidth = 18 scalefontsize = 16 # slider self.rollslider = Scale(window, from_=-90, to=90, orient=HORIZONTAL, resolution=5, label='Roll', command=self.updateRoll) self.rollslider.grid(column=0, row=1) self.rollslider.config(length=scalelength, width=scalewidth, font=("Helvetica", scalefontsize)) self.rollslider.set(0) # set the default position # slider self.pitchSlider = Scale(window, from_=-90, to=90, orient=HORIZONTAL, resolution=5, label='Pitch', command=self.updatePitch) self.pitchSlider.grid(column=0, row=2) self.pitchSlider.config(length=scalelength, width=scalewidth, font=("Helvetica", scalefontsize)) self.pitchSlider.set(0) # set the default angle # slider self.yawSlider = Scale(window, from_=-180, to=180, orient=HORIZONTAL, resolution=5, label='Yaw', command=self.updateYaw) self.yawSlider.grid(column=0, row=3) self.yawSlider.config(length=scalelength, width=scalewidth, font=("Helvetica", scalefontsize)) self.yawSlider.set(0) # set the default wind power # show me button self.buttonEuler = Button(window, text='Show me Euler', command=self.showMeEuler) self.buttonEuler.grid(column=0, row=4) self.buttonEuler.config(width=scalewidth, font=("Helvetica", scalefontsize)) # show me button self.buttonQuat = Button(window, text='Show me Quaternion', command=self.showMeQuat) self.buttonQuat.grid(column=0, row=5) self.buttonQuat.config(width=scalewidth, font=("Helvetica", scalefontsize)) self.e0box = Text(window, width=16, height=5) self.e0box.config(width=scalewidth - 4, font=("Helvetica", scalefontsize)) self.e0box.grid(column=2, row=1) self.ThetaBox = Text(window, width=16, height=2) self.ThetaBox.config(width=scalewidth - 4, font=("Helvetica", scalefontsize)) self.ThetaBox.grid(column=2, row=2) self.VectorBox = Text(window, width=16, height=4) self.VectorBox.config(width=scalewidth - 4, font=("Helvetica", scalefontsize)) self.VectorBox.grid(column=2, row=3) self.v0, self.v1, self.v2, self.meshColors = self.getAircraftPoints() self.allpoints = np.hstack((self.v0, self.v1, self.v2)) self.initial = 0 self.initial2 = 0 self.phi = 0.0 self.theta = 0.0 self.psi = 0.0 self.poly3d = None self.poly3d_num2 = None self.convert() #end of init function def updateRoll(self, value): self.phi = np.radians(float(value)) self.convert() def updatePitch(self, value): self.theta = np.radians(float(value)) self.convert() def updateYaw(self, value): self.psi = np.radians(float(value)) self.convert() def showMeEuler(self): dt = [0.04] * 3 if self.psi < 0: dt[0] *= -1 if self.theta < 0: dt[1] *= -1 if self.phi < 0: dt[2] *= -1 for i in np.arange(0.0, self.psi, dt[0]): self.plot2(0.0, 0.0, i) # roll, pitch, yaw for i in np.arange(0.0, self.theta, dt[1]): self.plot2(0.0, i, self.psi) # roll, pitch, yaw for i in np.arange(0.0, self.phi, dt[2]): self.plot2(i, self.theta, self.psi) # roll, pitch, yaw self.plot2(self.phi, self.theta, self.psi) def showMeQuat(self): beginquat = np.array([[1, 0, 0, 0]]).T endquat = Euler2Quaternion(self.phi, self.theta, self.psi) dist = np.linalg.norm(beginquat - endquat) howmany = int(dist * 100 / 1.2) e0 = np.linspace(beginquat.item(0), endquat.item(0), num=howmany) e1 = np.linspace(beginquat.item(1), endquat.item(1), num=howmany) e2 = np.linspace(beginquat.item(2), endquat.item(2), num=howmany) e3 = np.linspace(beginquat.item(3), endquat.item(3), num=howmany) for i in range(howmany): quat = np.array([[e0[i], e1[i], e2[i], e3[i]]]).T phi, theta, psi = Quaternion2Euler(quat) self.plot2(phi, theta, psi) self.plot2(self.phi, self.theta, self.psi) def convert(self): quat = Euler2Quaternion(self.phi, self.theta, self.psi) e0 = quat.item(0) e1 = quat.item(1) e2 = quat.item(2) e3 = quat.item(3) # put the quaternion on the GUI self.e0box.delete(0.0, 20.20) self.e0box.insert(0.0, 'quaternion = \n' + str(quat)) self.THETA = np.arccos(e0) * 2.0 # put the number on the GUI self.ThetaBox.delete(0.0, 20.20) self.ThetaBox.insert( 0.0, 'theta = \n' + str(round(np.degrees(self.THETA), 5)) + ' deg') self.vector_v = quat[1:, :] * np.sin(self.THETA / 2) # put the number on the GUI self.VectorBox.delete(0.0, 20.20) showv = np.copy(self.vector_v) showv[2] = -showv[2] self.VectorBox.insert(0.0, 'v = \n' + str(showv / np.linalg.norm(showv))) # make the vector_v big for plotting purposes test = 8 * self.vector_v / np.linalg.norm(self.vector_v) if not np.isnan(self.vector_v.item(0)): R = np.array([[0, 1, 0], [1, 0, 0], [0, 0, -1]]) self.vector_v = R @ test self.plot() def rotatePoints(self, points, R): "Rotate points by the rotation matrix R" rotated_points = R @ points return rotated_points def getAircraftPoints(self): """" Define the points on the aircraft following diagram in Figure C.3 """ # m = mesh.Mesh.from_file('basic plane.stl') m = mesh.Mesh.from_file('./betterPlane.stl') v0 = m.v0.T v1 = m.v1.T v2 = m.v2.T N = v0.shape[1] # print('number of mesh nodes is: ',N) # define the colors for each face of triangular mesh red = np.array([153., 0., 51., 255.]) / 255 green = np.array([34., 204., 0., 255.]) / 255 blue = np.array([77., 136., 255., 255.]) / 255 yellow = np.array([230., 230., 0., 255.]) / 255 white = np.array([255., 255., 255., 255.]) / 255 meshColors = np.empty((N, 3, 4), dtype=np.float32) for i in range(N): # meshColors[i] = random.choice([red, green, blue, yellow]) meshColors[i] = red if m.areas.item(i) > 0.2: # print(i) meshColors[i] = yellow # return points, meshColors return v0, v1, v2, meshColors def pointsToMesh(self, points): """" Converts points to triangular mesh Each mesh face is defined by three 3D points (a rectangle requires two triangular mesh faces) """ N = int(points.shape[1] / 3) translatedv0 = points[:, 0:N].T translatedv1 = points[:, N:2 * N].T translatedv2 = points[:, 2 * N:3 * N].T my_mesh = np.zeros((N, 3, 3)) for i in range(N): my_mesh[i] = np.array( [translatedv0[i], translatedv1[i], translatedv2[i]]) return my_mesh def plot(self): if self.initial == 0: fig = Figure(figsize=(9, 12)) self.ax0 = fig.add_subplot(211, projection='3d') self.ax1 = fig.add_subplot(212, projection='3d') self.canvas = FigureCanvasTkAgg(fig, master=self.window) self.canvas.get_tk_widget().grid(column=1, row=0, rowspan=14) self.ax0.set_ylim(-8, 8) self.ax0.set_xlim(-8, 8) self.ax0.set_zlim(-8, 8) self.ax0.set_xlabel('j') self.ax0.set_ylabel('i') self.ax0.set_zlabel('k') self.ax1.set_ylim(-8, 8) self.ax1.set_xlim(-8, 8) self.ax1.set_zlim(-8, 8) self.ax1.set_xlabel('j') self.ax1.set_ylabel('i') self.ax1.set_zlabel('k') self.ax0.grid(True) self.ax0.set_title('') self.ax1.grid(True) self.ax1.set_title('') self.initial = 1 self.ax0.mouse_init() self.ax1.mouse_init() v = self.allpoints # convert North-East Down to East-North-Up for rendering R = np.array([[0, 1, 0], [1, 0, 0], [0, 0, -1]]) v = R @ v verts = self.pointsToMesh(v) # plot sides self.poly3d = Poly3DCollection(verts, alpha=.35) self.poly3d.set_facecolor(['blue', 'red']) self.ax0.add_collection3d(self.poly3d) X = [0, self.vector_v.item(0)] Y = [0, self.vector_v.item(1)] Z = [0, self.vector_v.item(2)] self.ELine, = self.ax0.plot(X, Y, Z, lw=4) self.canvas.draw() else: R = Euler2Rotation(self.phi, self.theta, self.psi) v = R @ self.allpoints # convert North-East Down to East-North-Up for rendering R = np.array([[0, 1, 0], [1, 0, 0], [0, 0, -1]]) v = R @ v verts = self.pointsToMesh(v) # plot sides self.poly3d.set_verts(verts) X = [0, self.vector_v.item(0)] Y = [0, self.vector_v.item(1)] Z = [0, self.vector_v.item(2)] self.ELine._verts3d = (X, Y, Z) self.canvas.draw() def plot2(self, phi, theta, psi): if self.initial2 == 0: # self.ax1.cla() self.initial2 = 1 v = self.allpoints # self.ax0.scatter3D(v[:, 0], v[:, 1], v[:, 2]) # convert North-East Down to East-North-Up for rendering R = np.array([[0, 1, 0], [1, 0, 0], [0, 0, -1]]) v = R @ v # generate list of sides' polygons of our pyramid verts = self.pointsToMesh(v) # plot sides self.poly3d_num2 = Poly3DCollection(verts, alpha=.35) self.poly3d_num2.set_facecolor('blue') self.ax1.add_collection3d(self.poly3d_num2) self.canvas.draw() else: R = Euler2Rotation(phi, theta, psi) v = R @ self.allpoints # convert North-East Down to East-North-Up for rendering R = np.array([[0, 1, 0], [1, 0, 0], [0, 0, -1]]) v = R @ v # self.ax0.scatter3D(v[:, 0], v[:, 1], v[:, 2]) # generate list of sides' polygons of our pyramid verts = self.pointsToMesh(v) # plot sides self.poly3d_num2.set_verts(verts) self.canvas.draw()
class Interface: """ GUI class """ def __init__(self, master): # colour swatches self.gray1 = "#f6f6f6" self.gray2 = "#eaeaea" self.gray3 = "#d9d9d9" # key parameters self.running = False self.sect_width = 360 self.rb_choice = StringVar() self.dob1_val = StringVar() self.dob2_val = StringVar() self.df_records = StringVar() master.geometry("800x600+200+200") master.title("Red Snapper") master.resizable(False, False) master.configure(background=self.gray1) # LEFT SECTION LAYER # -------------------------------------------------------------------- self.sect_left = Frame(master) self.sect_left.place(x=15, y=15, width=self.sect_width, height=570) self.sect_left.config(relief=RIDGE) self.sect_left.config(background=self.gray2) # RIGHT SECTION LAYER # -------------------------------------------------------------------- self.sect_right = Frame(master) self.sect_right.place(x=-15, y=200, width=self.sect_width, height=385) self.sect_right.place(relx=1.0, anchor="ne") self.sect_right.config(relief=RIDGE) self.sect_right.config(background=self.gray2) # Sliders layer self.layer_sliders = Frame(self.sect_left) self.layer_sliders.place(y=0, width=self.sect_width, height=320) self.layer_sliders.config(**self.layer_props(self.gray2)) self.lab_sliders = Label(self.layer_sliders) self.lab_sliders.config(**self.title_props("Parameters")) self.lab_sliders.pack() # DOB layer self.layer_dob = Frame(self.sect_left) self.layer_dob.place(y=320, width=self.sect_width, height=80) self.layer_dob.config(**self.layer_props(self.gray2)) self.lab_dob = Label(self.layer_dob) self.lab_dob.config(**self.title_props("Birthdays range")) self.lab_dob.pack() # Export layer self.layer_export = Frame(self.sect_left) self.layer_export.place(y=400, width=self.sect_width, height=80) self.layer_export.config(**self.layer_props(self.gray2)) self.lab_export = Label(self.layer_export) self.lab_export.config(**self.title_props("Export format")) self.lab_export.pack() # Run layer self.layer_run = Frame(self.sect_left) self.layer_run.place(y=480, width=self.sect_width, height=100) self.layer_run.config(**self.layer_props(self.gray2)) self.lab_run = Label(self.layer_run) self.lab_run.config(**self.title_props("Run")) self.lab_run.pack() # About layer self.layer_about = Frame(self.sect_right) self.layer_about.place(width=self.sect_width, height=385) self.layer_about.config(**self.layer_props(self.gray2)) self.lab_about = Label(self.layer_about) self.lab_about.config(**self.title_props("About Red Snapper")) self.lab_about.pack() # sliders self.sli_wom = Scale(self.layer_sliders, from_=0, to=100) self.sli_wom.config(**self.sli_props()) self.sli_wom.config(label="Percentage of women in dataset.") self.sli_wom.pack(padx=20, pady=10) self.sli_wom.set(50) self.sli_nam = Scale(self.layer_sliders, from_=0, to=100) self.sli_nam.config(**self.sli_props()) self.sli_nam.config(label="Percentage of people with double name") self.sli_nam.pack(padx=20, pady=0) self.sli_nam.set(25) self.sli_sur = Scale(self.layer_sliders, from_=0, to=100) self.sli_sur.config(**self.sli_props()) self.sli_sur.config(label="Percentage of people with double surname") self.sli_sur.pack(padx=20, pady=10) self.sli_sur.set(15) # DOB Layer - From Date self.dob1_val.set("1945") self.lab_dob1 = Label(self.layer_dob, text="From date") self.lab_dob1.config(**self.label_props()) self.lab_dob1.pack(side=LEFT, padx=5) self.box_dob1 = Spinbox(self.layer_dob) self.box_dob1.config(from_=1945, to=1996, textvariable=self.dob1_val) self.box_dob1.config(**self.date_props()) self.box_dob1.pack(side=LEFT) # DOB Layer - To Date self.dob2_val.set("1997") self.lab_dob2 = Label(self.layer_dob, text="To date") self.lab_dob2.config(**self.label_props()) self.lab_dob2.pack(side=LEFT, padx=17) self.box_dob2 = Spinbox(self.layer_dob) self.box_dob2.config(from_=1946, to=1997, textvariable=self.dob2_val) self.box_dob2.config(**self.date_props()) self.box_dob2.pack(side=LEFT) # Export layer - JSON / CSV radio buttons self.rb_choice.set("CSV") self.rb1 = Radiobutton(self.layer_export, text="Save as CSV", variable=self.rb_choice, value="CSV") self.rb1.config(**self.radio_props()) self.rb1.place(y=35, x=50) self.rb2 = Radiobutton(self.layer_export, text="Save as JSON", variable=self.rb_choice, value="JSON") self.rb2.config(**self.radio_props()) self.rb2.place(y=35, x=200) # Run layer - no of records spinbox self.df_records.set("100") self.box_gen = Spinbox(self.layer_run) self.box_gen.config(from_=1, to=999999, textvariable=self.df_records) self.box_gen.config(increment=1000, width=19) self.box_gen.place(x=70, y=53) self.lab_gen = Label(self.layer_run, text="Number of records") self.lab_gen.config(**self.label_props()) self.lab_gen.place(x=70, y=30) # Run layer - generate button self.btn_run = ttk.Button(self.layer_run) self.btn_run.place(x=225, y=35, height=40) self.btn_run_reset() # header & logo section self.sect_logo = Frame(master) self.sect_logo.place(x=-15, y=30, width=350, height=120) self.sect_logo.place(relx=1.0, anchor="ne") self.logo = PhotoImage(file="./redsnapper/interface/logo.png") self.lab_logo = Label(self.sect_logo, image=self.logo) self.lab_logo.config(background=self.gray1) self.lab_logo.pack() # About box_about = Text(self.layer_about) box_about.config(**self.text_props()) box_about.pack(pady=10, padx=10) txt = """This program allows generating thousands of rows filled with pseudo-random data. """ \ + """\nThe generated records (like name, """ \ + """ surname, date of birth, e-mail address) can be used to provide sample data to: - test query performance of your database - practice data operations with BI tools.""" \ + """ \nThe application uses 4 processes to generate data simultaneously. """ \ + """ It takes about 25 seconds to create 1 million rows of data.\n""" box_about.insert(END, txt) # styling wrapped into functions for reusability def sli_props(self): """ bundle popular attributes of TK control so they can be reused :return: dict of bundled props """ return { "length": 300, "orient": HORIZONTAL, "sliderrelief": FLAT, "showvalue": 1, "resolution": 1, "sliderlength": 25, "tickinterval": 100, "font": ("Arial", 8), "activebackground": "#333333", "background": "#666666", "troughcolor": "#d0d4d2", "foreground": "#eeeeee", "highlightthickness": 8, "highlightcolor": "#ffffff", "highlightbackground": self.gray3, "borderwidth": 1 } @staticmethod def layer_props(bgcolor): """ bundle popular attributes of TK control so they can be reused :return: dict of bundled props """ return {"relief": RIDGE, "background": bgcolor} def title_props(self, title): """ bundle popular attributes of TK control so they can be reused :return: dict of bundled props """ return { "text": title, "background": self.gray3, "width": self.sect_width, "borderwidth": 1, "relief": RIDGE } def radio_props(self): """ bundle popular attributes of TK control so they can be reused :return: dict of bundled props """ return { "background": self.gray2, "activebackground": self.gray2, } def date_props(self): """ bundle popular attributes of TK control so they can be reused :return: dict of bundled props """ return { "width": 8, "increment": 1, "font": ("Arial", 8), "background": "#666666", "buttonbackground": "#666666", "foreground": "#eeeeee", "highlightthickness": 8, "highlightcolor": "#ffffff", "highlightbackground": self.gray2, "borderwidth": 1 } def label_props(self): """ bundle popular attributes of TK control so they can be reused :return: dict of bundled props """ return { "background": self.gray2, "highlightcolor": "#ffffff", "highlightbackground": self.gray2, "borderwidth": 1 } def text_props(self): """ bundle popular attributes of TK control so they can be reused :return: dict of bundled props """ return { "font": ("Arial", 11), "background": self.gray1, "foreground": "#212121", "highlightthickness": 8, "highlightbackground": self.gray1, "highlightcolor": self.gray1, "borderwidth": 0, "wrap": "word", "spacing1": 11, "spacing2": 7, } def produce_props(self): """ produce dict of key GUI parameters selected by user :return: no return parameters """ rows = int(self.box_gen.get()) props = { "pgender": self.sli_wom.get(), "pdname": self.sli_nam.get(), "pdsurname": self.sli_sur.get(), "dob1": self.box_dob1.get(), "dob2": self.box_dob2.get(), } dataset = Dataset().run_workload(rows, **props) exp_format = self.rb_choice.get() if exp_format == "CSV": Export().to_csv(dataset) else: Export().to_json(dataset) self.btn_run_reset() return def btn_run_reset(self): """ abort button (when generating) :return: no return parameters """ self.running = False self.btn_run.config(text="Generate", command=self.btn_run_start) return def btn_run_start(self): """ handle the run button :return: no return parameters """ self.running = True newthread = threading.Thread(target=self.produce_props) newthread.start() self.btn_run.config(text="Abort", command=self.btn_run_reset) return