class TimerDemo: """Controller""" def __init__(self): self.root = Tk() self.root.title('Timer') left = (self.root.winfo_screenwidth() - WIDTH) // 2 top = (self.root.winfo_screenheight() - HEIGHT) // 2 self.root.geometry(f'{WIDTH}x{HEIGHT}+{left}+{top}') self.root.resizable(False, False) # variables self.count_start = IntVar() self.count_start.trace_add('write', self.count_start_changed) self.count = IntVar() self.count.trace_add('write', self.count_changed) self.time = StringVar() self.timer_id = None self.ui = TimerUi(self) self.count_start.set(DEFAULT_COUNT) def run(self): self.root.mainloop() def count_changed(self, *args): self.draw_time() def count_start_changed(self, *args): # Countdown中には呼ばれないようにすること self.count.set(self.count_start.get()) def draw_time(self): count = self.count.get() m, s = count // 60, count % 60 self.time.set(f'{m:0>2}:{s:0>2}') def start_timer(self): if self.count.get() == 0: self.count_start_changed() self.ui.state_started() self.timer_id = self.root.after(1000, self.countdown) def stop_timer(self): if self.timer_id: self.root.after_cancel(self.timer_id) self.ui.state_stopped() def countdown(self): self.count.set(self.count.get() - 1) if self.count.get() > 0: self.timer_id = self.root.after(1000, self.countdown) # recursive else: self.root.bell() self.ui.state_stopped()
def init_reformat_display(self): orientation_var = IntVar(0) orientation_var.trace_add('write', self.__reformat_callback) count = 0 for text, value in self._ORIENTATIONS: b = Radiobutton(self, text=text, variable=orientation_var, value=count) b.pack(side=tk.TOP, anchor='w') count += 1 self._orientation = orientation_var
class CheckBox(Widget): def __init__(self, master, **kwargs): self._value = IntVar() super().__init__(tk=ttk.Checkbutton(master=master, variable=self._value), **kwargs) self._setter = self.connect_to_prop("value", self.on_changed_value) self._trace = self._value.trace_add( "write", lambda *_: self._setter(self._value.get())) def on_changed_value(self, value): self._value.set(value) def on_disposed(self): self._value.trace_remove("write", self._trace) self._value = None self._setter = None
class Scale(Widget): def __init__(self, master, **kwargs): self._valuev = IntVar() super().__init__(tk=TkTickScale(master=master, orient=kwargs.get("node").get_attr( "orient", "horizontal"), variable=self._valuev), **kwargs) self._setter = self.connect_to_prop("value", self.on_changed_value) self._trace = self._valuev.trace_add( "write", lambda *_: self._setter(self._valuev.get())) def on_changed_value(self, value): self._valuev.set(int(value)) def on_disposed(self): self._valuev.trace_remove("write", self._trace) self._valuev = None self._setter = None
class UBX_CFGVAL_Frame(Frame): """ UBX CFG-VAL configuration command panel. """ def __init__(self, app, container, *args, **kwargs): """ Constructor. :param Frame app: reference to main tkinter application :param Frame container: reference to container frame (config-dialog) :param args: optional args to pass to Frame parent class :param kwargs: optional kwargs to pass to Frame parent class """ self.__app = app # Reference to main application class self.__master = self.__app.get_master() # Reference to root class (Tk) self.__container = container Frame.__init__(self, self.__container.container, *args, **kwargs) self._img_send = ImageTk.PhotoImage(Image.open(ICON_SEND)) self._img_pending = ImageTk.PhotoImage(Image.open(ICON_PENDING)) self._img_confirmed = ImageTk.PhotoImage(Image.open(ICON_CONFIRMED)) self._img_warn = ImageTk.PhotoImage(Image.open(ICON_WARNING)) self._cfgval_cat = None self._cfgval_keyname = None self._cfgval_keyid = None self._cfgmode = IntVar() self._cfgatt = StringVar() self._cfgkeyid = StringVar() self._cfgval = StringVar() self._cfglayer = StringVar() self._body() self._do_layout() self._attach_events() self.reset() def _body(self): """ Set up frame and widgets. """ self._lbl_configdb = Label( self, text="CFG-VALSET/DEL/GET Configuration Interface", anchor="w") self._lbl_cat = Label(self, text="Category", anchor="w") self._lbx_cat = Listbox( self, border=2, relief="sunken", bg=ENTCOL, height=5, justify=LEFT, exportselection=False, ) self._scr_catv = Scrollbar(self, orient=VERTICAL) self._scr_cath = Scrollbar(self, orient=HORIZONTAL) self._lbx_cat.config(yscrollcommand=self._scr_catv.set) self._lbx_cat.config(xscrollcommand=self._scr_cath.set) self._scr_catv.config(command=self._lbx_cat.yview) self._scr_cath.config(command=self._lbx_cat.xview) self._lbl_parm = Label(self, text="Keyname", anchor="w") self._lbx_parm = Listbox( self, border=2, relief="sunken", bg=ENTCOL, height=5, justify=LEFT, exportselection=False, ) self._scr_parmv = Scrollbar(self, orient=VERTICAL) self._scr_parmh = Scrollbar(self, orient=HORIZONTAL) self._lbx_parm.config(yscrollcommand=self._scr_parmv.set) self._lbx_parm.config(xscrollcommand=self._scr_parmh.set) self._scr_parmv.config(command=self._lbx_parm.yview) self._scr_parmh.config(command=self._lbx_parm.xview) self._rad_cfgset = Radiobutton(self, text="CFG-VALSET", variable=self._cfgmode, value=0) self._rad_cfgdel = Radiobutton(self, text="CFG-VALDEL", variable=self._cfgmode, value=1) self._rad_cfgget = Radiobutton(self, text="CFG-VALGET", variable=self._cfgmode, value=2) self._lbl_key = Label(self, text="KeyID") self._lbl_keyid = Label( self, textvariable=self._cfgkeyid, bg=ENTCOL, width=10, fg=INFCOL, border=1, relief="sunken", ) self._lbl_type = Label(self, text="Type") self._lbl_att = Label( self, textvariable=self._cfgatt, bg=ENTCOL, width=5, fg=INFCOL, border=1, relief="sunken", ) self._lbl_layer = Label(self, text="Layer") self._spn_layer = Spinbox( self, textvariable=self._cfglayer, values=("RAM", "BBR", "FLASH", "DEFAULT"), bg=ENTCOL, wrap=True, width=8, state=READONLY, readonlybackground=ENTCOL, ) self._lbl_val = Label(self, text="Value") self._ent_val = Entry( self, textvariable=self._cfgval, readonlybackground=ENTCOL, fg=INFCOL, state=READONLY, relief="sunken", ) self._lbl_send_command = Label(self) self._btn_send_command = Button( self, image=self._img_send, width=50, command=self._on_send_config, ) def _do_layout(self): """ Layout widgets. """ self._lbl_configdb.grid(column=0, row=0, columnspan=6, padx=3, sticky=(W, E)) self._lbl_cat.grid(column=0, row=1, padx=3, sticky=(W, E)) self._lbx_cat.grid(column=0, row=2, rowspan=5, padx=3, pady=3, sticky=(W, E)) self._scr_catv.grid(column=0, row=2, rowspan=5, sticky=(N, S, E)) self._scr_cath.grid(column=0, row=7, sticky=(W, E)) self._lbl_parm.grid(column=1, row=1, columnspan=4, padx=3, sticky=(W, E)) self._lbx_parm.grid(column=1, row=2, columnspan=4, rowspan=5, padx=3, pady=3, sticky=(W, E)) self._scr_parmv.grid(column=4, row=2, rowspan=5, sticky=(N, S, E)) self._scr_parmh.grid(column=1, row=7, columnspan=4, sticky=(W, E)) self._rad_cfgget.grid(column=0, row=8, padx=3, pady=0, sticky=(W)) self._rad_cfgset.grid(column=0, row=9, padx=3, pady=0, sticky=(W)) self._rad_cfgdel.grid(column=0, row=10, padx=3, pady=0, sticky=(W)) self._lbl_key.grid(column=1, row=8, padx=3, pady=0, sticky=(E)) self._lbl_keyid.grid(column=2, row=8, padx=3, pady=0, sticky=(W)) self._lbl_type.grid(column=3, row=8, padx=3, pady=0, sticky=(E)) self._lbl_att.grid(column=4, row=8, padx=3, pady=0, sticky=(W)) self._lbl_layer.grid(column=1, row=9, padx=3, pady=0, sticky=(E)) self._spn_layer.grid(column=2, row=9, padx=3, pady=0, sticky=(W)) self._lbl_val.grid(column=1, row=10, padx=3, pady=0, sticky=(E)) self._ent_val.grid(column=2, row=10, columnspan=3, padx=3, pady=0, sticky=(W, E)) self._btn_send_command.grid(column=3, row=12, rowspan=2, ipadx=3, ipady=3, sticky=(E)) self._lbl_send_command.grid(column=4, row=13, rowspan=2, ipadx=3, ipady=3, sticky=(E)) (cols, rows) = self.grid_size() for i in range(cols): self.grid_columnconfigure(i, weight=1) for i in range(rows): self.grid_rowconfigure(i, weight=1) self.option_add("*Font", self.__app.font_sm) def _attach_events(self): """ Bind events to widgets. """ self._lbx_cat.bind("<<ListboxSelect>>", self._on_select_cat) self._lbx_parm.bind("<<ListboxSelect>>", self._on_select_parm) self._cfgmode.trace_add("write", self._on_select_mode) def reset(self): """ Reset panel. """ for i, cat in enumerate(UBX_CONFIG_CATEGORIES): self._lbx_cat.insert(i, cat) self._cfgmode.set(2) def _on_select_mode(self, *args, **kwargs): # pylint: disable=unused-argument """ Mode has been selected. """ if self._cfgmode.get() == VALSET: self._ent_val.config(state=NORMAL, bg=ENTCOL, fg="Black") else: self._ent_val.config(state=READONLY, readonlybackground=ENTCOL, fg=INFCOL) def _on_select_cat(self, *args, **kwargs): # pylint: disable=unused-argument """ Configuration category has been selected. """ idx = self._lbx_cat.curselection() self._cfgval_cat = self._lbx_cat.get(idx) self._lbx_parm.delete(0, "end") self._cfgkeyid.set("") self._cfgatt.set("") idx = 0 for keyname, (_, _) in UBX_CONFIG_DATABASE.items(): if self._cfgval_cat in keyname: self._lbx_parm.insert(idx, keyname) idx += 1 self._cfgval.set("") def _on_select_parm(self, *args, **kwargs): # pylint: disable=unused-argument """ Configuration parameter (keyname) has been selected. """ idx = self._lbx_parm.curselection() self._cfgval_keyname = self._lbx_parm.get(idx) (keyid, att) = UBXMessage.cfgname2key(self._cfgval_keyname) self._cfgkeyid.set(hex(keyid)) self._cfgatt.set(att) self._cfgval.set("") def _on_send_config(self, *args, **kwargs): # pylint: disable=unused-argument """ Config interface send button has been clicked. """ if self._cfgval_keyname is not None: if self._cfgmode.get() == VALSET: self._do_valset() elif self._cfgmode.get() == VALDEL: self._do_valdel() else: self._do_valget() def _do_valset(self): """ Send a CFG-VALSET message. :return: valid entry flag :rtype: bool """ valid_entry = True att = atttyp(self._cfgatt.get()) atts = attsiz(self._cfgatt.get()) val = self._cfgval.get() layers = self._cfglayer.get() if layers == "BBR": layers = 2 elif layers == "FLASH": layers = 4 else: layers = 1 try: if att in ("C", "X"): # byte or char if len(val) == atts * 2: # 2 hex chars per byte val = bytearray.fromhex(val) else: valid_entry = False elif att in ("E", "U"): # unsigned integer val = int(val) if val < 0: valid_entry = False elif att == "L": # bool val = int(val) if val not in (0, 1): valid_entry = False elif att == "I": # signed integer val = int(val) elif att == "R": # floating point val = float(val) transaction = 0 cfgData = [ (self._cfgval_keyname, val), ] except ValueError: valid_entry = False if valid_entry: msg = UBXMessage.config_set(layers, transaction, cfgData) self.__app.serial_handler.serial_write(msg.serialize()) self._ent_val.configure(bg=ENTCOL) self._lbl_send_command.config(image=self._img_pending) self.__container.set_status("CFG-VALSET SET message sent", "blue") self.__container.set_pending(UBX_CFGVAL, ("ACK-ACK", "ACK-NAK")) else: self._ent_val.configure(bg=ERRCOL) self._lbl_send_command.config(image=self._img_warn) typ = ATTDICT[att] self.__container.set_status( ("INVALID ENTRY - must conform to parameter " f"type {att} ({typ}) and size {atts} bytes"), "red", ) return valid_entry def _do_valdel(self): """ Send a CFG-VALDEL message. """ layers = self._cfglayer.get() if layers == "BBR": layers = 2 elif layers == "FLASH": layers = 4 else: layers = 1 transaction = 0 key = [ self._cfgval_keyname, ] msg = UBXMessage.config_del(layers, transaction, key) self.__app.serial_handler.serial_write(msg.serialize()) self._ent_val.configure(bg=ENTCOL) self._lbl_send_command.config(image=self._img_pending) self.__container.set_status("CFG-VALDEL SET message sent", "blue") self.__container.set_pending(UBX_CFGVAL, ("ACK-ACK", "ACK-NAK")) def _do_valget(self): """ Send a CFG-VALGET message. """ layers = self._cfglayer.get() if layers == "BBR": layers = 1 elif layers == "FLASH": layers = 2 elif layers == "DEFAULT": layers = 7 else: layers = 0 transaction = 0 keys = [ self._cfgval_keyname, ] msg = UBXMessage.config_poll(layers, transaction, keys) self.__app.serial_handler.serial_write(msg.serialize()) self._ent_val.configure(bg=ENTCOL) self._lbl_send_command.config(image=self._img_pending) self.__container.set_status("CFG-VALGET POLL message sent", "blue") self.__container.set_pending(UBX_CFGVAL, ("CFG-VALGET", "ACK-ACK", "ACK-NAK")) def update_status(self, cfgtype, **kwargs): # pylint: disable=unused-argument """ Update pending confirmation status. :param str cfgtype: identity of UBX message containing config info :param kwargs: status keywords and values from UBX config message """ if cfgtype == "CFG-VALGET": self._lbl_send_command.config(image=self._img_confirmed) data = kwargs.get("data", None) if data is not None: val = getattr(data, self._cfgval_keyname, None) if val is not None: if isinstance(val, bytes): val = val.hex() self._cfgval.set(val) self.__container.set_status(f"{cfgtype} GET message received", "green") elif cfgtype == "ACK-ACK": self._lbl_send_command.config(image=self._img_confirmed) self.__container.set_status(f"{cfgtype} GET message received", "green") elif cfgtype == "ACK-NAK": self._lbl_send_command.config(image=self._img_warn) self.__container.set_status("CFG-VAL message rejected", "red")
class Meter(Frame): """A radial meter that can be used to show progress of long running operations or the amount of work completed; can also be used as a `Dial` when set to ``interactive=True``. This widget is very flexible. There are two primary meter types which can be set with the ``metertype`` parameter: 'full' and 'semi', which show the arc of the meter in a full or semi-circle. You can also customize the arc of the circle with the ``arcrange`` and ``arcoffset`` parameters. The progress bar indicator can be displayed as a solid color or with stripes using the ``stripethickness`` parameter. By default, the ``stripethickness`` is 0, which results in a solid progress bar. A higher ``stripethickness`` results in larger wedges around the arc of the meter. Various text and label options exist. The center text can be formatted with the ``meterstyle`` parameter and uses the `TLabel` styles. This also colors the progress bar arch. You can prepend or append text to the center text using the ``textappend`` and ``textprepend`` parameters. This is most commonly used for '$', '%', or other such symbols. Variable are generated automatically for this widget and can be linked to other widgets by referencing them via the ``amountusedvariable`` and ``amounttotalvariable`` attributes. The variable properties allow you to easily get and set the value of these variables. For example: ``Meter.amountused`` or ``Meter.amountused = 55`` will get or set the amount used on the widget without having to call the ``get`` or ``set`` methods of the tkinter variable. """ def __init__(self, master=None, arcrange=None, arcoffset=None, amounttotal=100, amountused=0, interactive=False, labelfont='Helvetica 10 bold', labelstyle='secondary.TLabel', labeltext=None, metersize=200, meterstyle='primary.TLabel', metertype='full', meterthickness=10, showvalue=True, stripethickness=0, textappend=None, textfont='Helvetica 25 bold', textprepend=None, wedgesize=0, **kw): """ Args: master (Widget): Parent widget arcoffset (int): The amount to offset the arc's starting position in degrees; 0 is at 3 o'clock. arcrange (int): The range of the arc in degrees from start to end. amounttotal (int): The maximum value of the meter. amountused (int): The current value of the meter; displayed if ``showvalue=True``. interactive (bool): Enables the meter to be adjusted with mouse interaction. labelfont(Font or str): The font of the supplemental label. labelstyle (str): The ttk style used to render the supplemental label. labeltext (str): Supplemental label text that appears `below` the center text. metersize (int): The size of the meter; represented by one side length of a square. meterstyle (str): The ttk style used to render the meter and center text. metertype (str): One of **full** or **semi**; displays a full-circle or semi-circle. meterthickness (int): The thickness of the meter's progress bar. showvalue (bool): Show the meter's value in the center text; default = True. stripethickness (int): The meter's progress bar can be displayed in solid or striped form. If the value is greater than 0, the meter's progress bar changes from a solid to striped, where the value is the thickness of the stripes. textappend (str): A short string appended to the center text. textfont (Font or str): The font of the center text. textprepend (str): A short string prepended to the center text. wedgesize (int): If greater than zero, the width of the wedge on either side of the current meter value. """ super().__init__(master=master, **kw) self.box = ttk.Frame(self, width=metersize, height=metersize) if metertype == 'semi': self.arcoffset = arcoffset if arcoffset is not None else 135 self.arcrange = arcrange if arcrange is not None else 270 else: # aka 'full' self.arcoffset = arcoffset if arcoffset is not None else -90 self.arcrange = arcrange if arcrange is not None else 360 # widget variables self.amountusedvariable = IntVar(value=amountused) self.amounttotalvariable = IntVar(value=amounttotal) self.labelvariable = StringVar(value=labeltext) self.amountusedvariable.trace_add('write', self.draw_meter) # misc widget settings self.towardsmaximum = True self.metersize = metersize self.meterthickness = meterthickness self.stripethickness = stripethickness self.showvalue = showvalue self.wedgesize = wedgesize # translate system colors if a ttkbootstrap style is not used if 'system' in self.lookup(meterstyle, 'foreground').lower(): self.meterforeground = self.convert_system_color( self.lookup(meterstyle, 'foreground')) else: self.meterforeground = self.lookup(meterstyle, 'foreground') if 'system' in self.lookup(meterstyle, 'background').lower(): self.meterbackground = Colors.update_hsv(self.convert_system_color( self.lookup(meterstyle, 'background')), vd=-0.1) else: self.meterbackground = Colors.update_hsv(self.lookup( meterstyle, 'background'), vd=-0.1) # meter image self.meter = ttk.Label(self.box) self.draw_base_image() self.draw_meter() # text & label widgets self.textcontainer = ttk.Frame(self.box) self.textprepend = ttk.Label(self.textcontainer, text=textprepend, font=labelfont, style=labelstyle) self.textprepend.configure(anchor='s', padding=(0, 5)) self.text = ttk.Label(self.textcontainer, textvariable=self.amountusedvariable, style=meterstyle, font=textfont) self.textappend = ttk.Label(self.textcontainer, text=textappend, font=labelfont, style=labelstyle) self.textappend.configure(anchor='s', padding=(0, 5)) self.label = ttk.Label(self.box, text=labeltext, style=labelstyle, font=labelfont) # set interactive mode if interactive: self.meter.bind('<B1-Motion>', self.on_dial_interact) self.meter.bind('<Button-1>', self.on_dial_interact) # geometry management self.meter.place(x=0, y=0) self.box.pack() if labeltext: self.textcontainer.place(relx=0.5, rely=0.45, anchor='center') else: self.textcontainer.place(relx=0.5, rely=0.5, anchor='center') if textprepend: self.textprepend.pack(side='left', fill='y') if showvalue: self.text.pack(side='left', fill='y') if textappend: self.textappend.pack(side='left', fill='y') self.label.place(relx=0.5, rely=0.6, anchor='center') @property def amountused(self): return self.amountusedvariable.get() @amountused.setter def amountused(self, value): self.amountusedvariable.set(value) @property def amounttotal(self): return self.amounttotalvariable.get() @amounttotal.setter def amounttotal(self, value): self.amounttotalvariable.set(value) def convert_system_color(self, systemcolorname): """Convert a system color name to a hexadecimal value Args: systemcolorname (str): a system color name, such as `SystemButtonFace` """ r, g, b = [x >> 8 for x in self.winfo_rgb(systemcolorname)] return f'#{r:02x}{g:02x}{b:02x}' def draw_base_image(self): """Draw the base image to be used for subsequent updates""" self.base_image = Image.new('RGBA', (self.metersize * 5, self.metersize * 5)) draw = ImageDraw.Draw(self.base_image) # striped meter if self.stripethickness > 0: for x in range( self.arcoffset, self.arcrange + self.arcoffset, 2 if self.stripethickness == 1 else self.stripethickness): draw.arc( (0, 0, self.metersize * 5 - 20, self.metersize * 5 - 20), x, x + self.stripethickness - 1, self.meterbackground, self.meterthickness * 5) # solid meter else: draw.arc((0, 0, self.metersize * 5 - 20, self.metersize * 5 - 20), self.arcoffset, self.arcrange + self.arcoffset, self.meterbackground, self.meterthickness * 5) def draw_meter(self, *args): """Draw a meter Args: *args: if triggered by a trace, will be `variable`, `index`, `mode`. """ im = self.base_image.copy() draw = ImageDraw.Draw(im) if self.stripethickness > 0: self.draw_striped_meter(draw) else: self.draw_solid_meter(draw) self.meterimage = ImageTk.PhotoImage( im.resize((self.metersize, self.metersize), Image.CUBIC)) self.meter.configure(image=self.meterimage) def draw_solid_meter(self, draw): """Draw a solid meter Args: draw (ImageDraw.Draw): an object used to draw an arc on the meter """ if self.wedgesize > 0: meter_value = self.meter_value() draw.arc((0, 0, self.metersize * 5 - 20, self.metersize * 5 - 20), meter_value - self.wedgesize, meter_value + self.wedgesize, self.meterforeground, self.meterthickness * 5) else: draw.arc((0, 0, self.metersize * 5 - 20, self.metersize * 5 - 20), self.arcoffset, self.meter_value(), self.meterforeground, self.meterthickness * 5) def draw_striped_meter(self, draw): """Draw a striped meter Args: draw (ImageDraw.Draw): an object used to draw an arc on the meter """ if self.wedgesize > 0: meter_value = self.meter_value() draw.arc((0, 0, self.metersize * 5 - 20, self.metersize * 5 - 20), meter_value - self.wedgesize, meter_value + self.wedgesize, self.meterforeground, self.meterthickness * 5) else: for x in range(self.arcoffset, self.meter_value() - 1, self.stripethickness): draw.arc( (0, 0, self.metersize * 5 - 20, self.metersize * 5 - 20), x, x + self.stripethickness - 1, self.meterforeground, self.meterthickness * 5) def lookup(self, style, option): """Wrapper around the tcl style lookup command Args: style (str): the name of the style used for rendering the widget. option (str): the option to lookup from the style option database. Returns: any: the value of the option looked up. """ return self.tk.call("ttk::style", "lookup", style, '-%s' % option, None, None) def meter_value(self): """Calculate the meter value Returns: int: the value to be used to draw the arc length of the progress meter """ return int((self.amountused / self.amounttotal) * self.arcrange + self.arcoffset) def on_dial_interact(self, e): """Callback for mouse drag motion on indicator Args: e (Event): event callback for drag motion. """ dx = e.x - self.metersize // 2 dy = e.y - self.metersize // 2 rads = math.atan2(dy, dx) degs = math.degrees(rads) if degs > self.arcoffset: factor = degs - self.arcoffset else: factor = 360 + degs - self.arcoffset # clamp value between 0 and ``amounttotal`` amountused = int(self.amounttotal / self.arcrange * factor) if amountused < 0: self.amountused = 0 elif amountused > self.amounttotal: self.amountused = self.amounttotal else: self.amountused = amountused def step(self, delta=1): """Increase the indicator value by ``delta``. The default increment is 1. The indicator will reverse direction and count down once it reaches the maximum value. Keyword Args: delta (int): the amount to change the indicator. """ if self.amountused >= self.amounttotal: self.towardsmaximum = True self.amountused = self.amountused - delta elif self.amountused <= 0: self.towardsmaximum = False self.amountused = self.amountused + delta elif self.towardsmaximum: self.amountused = self.amountused - delta else: self.amountused = self.amountused + delta
from tkinter import Frame, Tk, Scale, IntVar def change_color(variable, index, mode): frame['bg'] = '#%0x%0x%0x' % (var.get() * 16, var.get() * 16, var.get() * 16) if __name__ == '__main__': root = Tk() frame = Frame(root, width=100, height=100) var = IntVar() scale = Scale(root, orient='horizontal', variable=var, length=300, from_=0, to=99, tickinterval=10, resolution=10) var.trace_add('write', change_color) frame.pack() scale.pack() root.mainloop()
class AuxMode(Frame): def __init__(self, root, parent): Frame.__init__(self, root, borderwidth=2, relief='groove', height=10) settings = parent.settings('aux_mode') self.parent = parent lbl_power = Label(self, text='Power:') lbl_power.grid(row=0, column=0, pady=2) self.cmb_power = ttk.Combobox(self, value=['Off', 'On']) self.cmb_power.grid(row=0, column=1, pady=2) self.cmb_power.set(settings['power']) self.cmb_power['state'] = 'readonly' self.cmb_power.bind('<<ComboboxSelected>>', self.set_power) lbl_aux_pin = Label(self, text='AUX pin:') lbl_aux_pin.grid(row=1, column=0, pady=2) self.cmb_aux_pin = ttk.Combobox(self, values=['AUX', 'CS']) self.cmb_aux_pin.grid(row=1, column=1, pady=2) self.cmb_aux_pin['state'] = 'readonly' self.cmb_aux_pin.set(settings['aux_pin']) self.cmb_aux_pin.bind('<<ComboboxSelected>>', self.select_aux_pin) lbl_aux = Label(self, text='AUX mode:') lbl_aux.grid(row=2, column=0, pady=2) self.cmb_aux_mode = ttk.Combobox( self, values=['Input', 'Output', 'Servo', 'PWM']) self.cmb_aux_mode.grid(row=2, column=1, pady=2) self.cmb_aux_mode['state'] = 'readonly' self.cmb_aux_mode.set(settings['mode']) self.cmb_aux_mode.bind('<<ComboboxSelected>>', self.select_aux_mode) self.aux_servo = Scale(self, from_=0, to=180, orient='horizontal') self.aux_servo.set(settings['servo']) self.aux_servo.bind('<ButtonRelease-1>', self.select_aux_mode) self.aux_pin_state = IntVar() self.aux_pin_state.set(settings['aux_state']) self.aux_pin_state.trace_add('write', self.select_aux_mode) self.aux_high = Radiobutton(self, text='HIGH', value=1, variable=self.aux_pin_state) self.aux_low = Radiobutton(self, text='LOW', value=0, variable=self.aux_pin_state) self.aux_pwm_lbl = Label(self, text='Frequency (KHz):') self.aux_pwm_freq = Entry(self) self.aux_pwm_freq.insert(0, settings['pwm_freq']) self.aux_pwm_freq.bind('<KeyRelease>', self.select_aux_mode) self.aux_pwm_duty_lbl = Label(self, text='Duty cycle (%):') self.aux_pwm_duty = Scale(self, from_=0, to=99, orient='horizontal') self.aux_pwm_duty.set(settings['pwm_duty']) self.aux_pwm_duty.bind('<ButtonRelease-1>', self.select_aux_mode) def __call__(self): return { 'power': self.cmb_power.get(), 'mode': self.cmb_aux_mode.get(), 'aux_state': self.aux_pin_state.get(), 'aux_pin': self.cmb_aux_pin.get(), 'servo': self.aux_servo.get(), 'pwm_freq': self.aux_pwm_freq.get(), 'pwm_duty': self.aux_pwm_duty.get() } def select_aux_mode(self, e=None, a=None, b=None): for i, widget in enumerate(self.winfo_children()): if i > 5: widget.grid_forget() mode = self.cmb_aux_mode.get() # if self.parent.console.mode == 'HiZ': return if mode == 'Input': self.parent.console.aux('Input') if mode == 'Output': self.aux_high.grid(row=3, column=1) self.aux_low.grid(row=4, column=1) self.parent.console.aux('Output', self.aux_pin_state.get()) if mode == 'Servo': self.aux_servo.grid(row=3, column=1) self.parent.console.servo(self.aux_servo.get()) if mode == 'PWM': self.aux_pwm_lbl.grid(row=3, column=0) self.aux_pwm_freq.grid(row=3, column=1) self.aux_pwm_duty_lbl.grid(row=4, column=0) self.aux_pwm_duty.grid(row=4, column=1) self.parent.console.pwm(self.aux_pwm_freq.get(), self.aux_pwm_duty.get()) if mode == 'CS': self.parent.console.cs() self.parent.settings('aux_mode', self()) def select_aux_pin(self, e=None): self.parent.console.aux_pin(self.cmb_aux_pin.get()) def set_power(self, e=None): self.parent.console.power(self.cmb_power.get()) self.parent.settings('aux_mode', self()) def disable(self): self.cmb_power['state'] = 'disabled' self.cmb_aux_mode['state'] = 'disabled' self.aux_high['state'] = 'disabled' self.aux_low['state'] = 'disabled' self.aux_servo['state'] = 'disabled' self.aux_pwm_freq['state'] = 'disabled' self.aux_pwm_duty['state'] = 'disabled' def enable(self): self.cmb_power['state'] = 'readonly' self.cmb_aux_mode['state'] = 'readonly' self.aux_high['state'] = 'normal' self.aux_low['state'] = 'normal' self.aux_servo['state'] = 'normal' self.aux_pwm_freq['state'] = 'normal' self.aux_pwm_duty['state'] = 'normal'
class App(): """ Start of the applications """ def __init__(self): self.__version__ = '0.2.1' # Larch self.mylarch = larch.Interpreter() self.root = Tk(className='EXAFS Neo GUI') self.root.wm_title("Graphical User Interface for EXAFS Analysis (Beta)") # Standard default geometry self.root.geometry("750x500") self.padx = 5 self.pady = 3 self.os = get_platform() base_folder = os.path.dirname(os.path.join(os.getcwd(),__file__)) icon_loc = os.path.join(base_folder,'media/icon.png') img = PhotoImage(file=icon_loc) self.root.tk.call('wm','iconphoto',self.root._w,img) # Set default font self.entryFont = Font(family="TkFixedFont", size=10) self.menuFont = Font(family="TkMenuFont",size=10) self.labelFont = Font(family="TkTextFont", size=11) # Generate the mainframe self.mainframe = ttk.Notebook(self.root,height=40,padding="5") self.mainframe.grid(column=0, row=0, sticky=(N, W, E, S)) # Initalize variable self.initialize_var() self.initialize_tab() self.Build_tabs() def initialize_var(self): """ Initalize all possible variables in the gui. Future additions starts here. """ # Inputs self.data_file = StringVar(self.root,'Please choose a file') self.temp_data_file = StringVar(self.root,'Please choose a file') self.output_file = StringVar(self.root,'Please choose a file') self.ncomp = IntVar(self.root,'0') # Number of compounds self.feff_file = StringVar(self.root,'Please choose a directory') self.series = BooleanVar(self.root,'False') # Populations self.populations = IntVar(self.root,1000) self.num_gen = IntVar(self.root,100) self.best_sample = IntVar(self.root,20) self.lucky_few = IntVar(self.root,20) # Mutations self.chance_of_mutation = IntVar(self.root,20) self.orginal_chance_of_mutation = IntVar(self.root,20) self.chance_of_mutation_e0 = IntVar(self.root,20) self.mutation_options = IntVar(self.root,0) # Paths self.individual_path = BooleanVar(self.root,True) self.path_range = IntVar(self.root,20) temp_list = ",".join(np.char.mod('%i', np.arange(1,self.path_range.get()+1))) self.path_list = StringVar(self.root,temp_list) self.path_optimize = BooleanVar(self.root,False) self.path_optimize_pert = DoubleVar(self.root,0.01) self.path_optimize_only = BooleanVar(self.root,False) # Larch Paths self.kmin = DoubleVar(self.root,2.5) self.kmax = DoubleVar(self.root,15) self.k_weight = DoubleVar(self.root,2) self.delta_k = DoubleVar(self.root,0.05) self.r_bkg = DoubleVar(self.root,1.0) self.bkg_kw = DoubleVar(self.root, 1.0) self.bkg_kmax = DoubleVar(self.root, 15) #Outputs self.print_graph = BooleanVar(self.root, False) self.num_output_paths = BooleanVar(self.root, True) self.steady_state_exit = BooleanVar(self.root, True) # Pertubutuions self.n_ini = IntVar(self.root,100) def initialize_tab(self): """ Initialize tab for the main frame. more tabs """ s = ttk.Style() s.configure('TNotebook.Tab', font=('TkHeadingFont','11') ) height=1 self.tab_Inputs = tk.Frame(self.mainframe,height=height) self.tab_Populations = tk.Frame(self.mainframe,height=height) self.tab_Paths = tk.Frame(self.mainframe,height=height) self.tab_Mutation = tk.Frame(self.mainframe,height=height) self.tab_Larch = tk.Frame(self.mainframe,height=height) self.tab_Bkg = tk.Frame(self.mainframe,height=height) self.tab_Output = tk.Frame(self.mainframe,height=height) self.tab_Analysis = tk.Frame(self.mainframe,height=height) self.tab_Expert = tk.Frame(self.mainframe,height=height) # Add all the secondary tabs on the top window self.mainframe.add(self.tab_Inputs, text="Inputs") self.mainframe.add(self.tab_Populations, text="Populations") self.mainframe.add(self.tab_Paths, text="Paths") self.mainframe.add(self.tab_Mutation, text="Mutations") self.mainframe.add(self.tab_Larch, text="Larch") self.mainframe.add(self.tab_Bkg, text="Background Plots") self.mainframe.add(self.tab_Output, text="Outputs") self.mainframe.add(self.tab_Analysis, text='Analysis') self.mainframe.add(self.tab_Expert, text='Expert') self.mainframe.grid(row=0,column=0,columnspan=4,padx=self.padx,pady=self.pady,sticky=E+W+N+S) def description_tabs(self,arr,tabs,sticky=(W,E),row=None,return_description=False): # Rows = index of rows description_list = [] if row is not None: assert len(row) == len(arr); for i,inputs in enumerate(arr): entry = ttk.Label(tabs,text=inputs,font=self.labelFont) if row is not None: k = row[i] else: k = i entry.grid_configure(column=0,row=k,sticky=sticky,padx=self.padx,pady=self.pady) description_list.append(entry) if description_list: return description_list def Build_tabs(self): """ Build tabs """ # Building all arrays self.Build_global() self.Build_inputs_tab() self.Build_population_tab() self.Build_path_tab() self.Build_mutations_tab() self.Build_larch_tab() self.Build_background_tab() self.Build_output_tab() self.Build_analysis_tab() self.Build_expert_tab() self.mainframe.grid_rowconfigure(0,weight=1) self.mainframe.grid_columnconfigure(0,weight=1) self.mainframe.grid_columnconfigure(1,weight=1) def Write_ini(self,filename): """ Write the ini for the specific file """ inputs = ("[Inputs] \nnum_compounds = {compound} \ncsv_file = {data} \noutput_file = {out} \nfeff_file = {feff}\ncsv_series = {series}" .format(compound=str(self.ncomp.get()), data=str(self.data_file.get()), out=str(self.output_file.get()), feff=str(self.feff_file.get()), series=str(self.series.get()))) populations = ("\n\n[Populations] \npopulation = {pop} \nnum_gen = {numgen} \nbest_sample = {best} \nlucky_few = {luck}" .format(pop=str(self.populations.get()), numgen=str(self.num_gen.get()), best=str(self.best_sample.get()), luck=str(self.lucky_few.get()))) mutations = ("\n\n[Mutations] \nchance_of_mutation = {chance} \noriginal_chance_of_mutation = {original} \nchance_of_mutation_e0 = {e0} \nmutated_options = {opt}" .format(chance=str(self.chance_of_mutation.get()), original=str(self.orginal_chance_of_mutation.get()), e0=str(self.chance_of_mutation_e0.get()), opt=str(self.mutation_options.get()))) paths = ("\n\n[Paths] \nindividual_path = {tf} \npath_range = {range} \npath_list = {list} \npath_optimize = {optimize} \noptimize_percent = {optimize_pert} \noptimize_only = {optimize_only}" .format(tf=str(self.individual_path.get()), range=str(self.path_range.get()), list=str(self.path_list.get().replace(" ","")), optimize=str(self.path_optimize.get()), optimize_pert = str(self.path_optimize_pert.get()), optimize_only = str(self.path_optimize_only.get()) )) larch_paths = ("\n\n[Larch_Paths] \nkmin = {min} \nkmax = {max} \nkweight = {weight} \ndeltak = {delk} \nrbkg = {rb} \nbkgkw = {bk} \nbkgkmax = {bmax}" .format(min=self.kmin.get(), max=self.kmax.get(), weight=self.k_weight.get(), delk=self.delta_k.get(), rb=self.r_bkg.get(), bk=self.bkg_kw.get(), bmax=self.bkg_kmax.get())) outputs = ("\n\n[Outputs] \nprint_graph = {pg} \nnum_output_paths = {outpath}\nsteady_state_exit = {steady}" .format(pg=self.print_graph.get(), outpath=self.num_output_paths.get(), steady=self.steady_state_exit.get())) with open(filename,'w') as writer: writer.write(str(inputs)) writer.write(str(populations)) writer.write(str(mutations)) writer.write(str(paths)) writer.write(str(larch_paths)) writer.write(str(outputs)) def Generate_ini(self): os.chdir("..") #change the working directory from gui to EXAFS # while proceed == False: ini_file= filedialog.asksaveasfilename(initialdir = os.getcwd(), title = "Choose output ini file", filetypes = [("ini files","*.ini")]) if ini_file is None: return if isinstance(ini_file,tuple) == False: if len(ini_file) != 0: self.Write_ini(ini_file) messagebox.showinfo('','Ini file written to {fileloc}'.format(fileloc=ini_file)) os.chdir("gui") def stop_term(self): if hasattr(self,'proc'): # print("Stopped EXAFS") self.proc.kill() def run_term(self,file = 'test_temp.i'): """ if hasattr(self,'terminal'): # Need to close previous threads self.terminal.destroy() # command = ['exafs','-i','test.ini'] self.Write_ini('test_temp.i') command = 'exafs -i test_temp.i'.split(' ') # print(command) self.terminal = Console(self.txtbox,command) """ self.Write_ini('test_temp.i') self.stop_term() # command = 'exafs -i test_temp.i' command = ['exafs','-i',file] if self.os == 'Windows': print(' '.join(command)) self.proc = subprocess.Popen(' '.join(command),shell=True) else: self.proc = subprocess.Popen("exec " + ' '.join(command),shell=True) def run_ini(self,file = 'test_temp.i'): command = ['exafs','-i',file] self.proc = subprocess.Popen("exec " + ' '.join(command),shell=True) self.proc.wait() def Build_global(self): ''' Create global generate ini ''' def about_citation(): """ Create about popup """ popup = tk.Toplevel() popup.wm_title("About: Ver: " + str(self.__version__)) popup.resizable(False,False) cite = tk.Label(popup,text='Citation:',font='TkTextFont') cite.grid(column=0,row=0,sticky=N,padx=self.padx,pady=self.pady) citation = scrolledtext.ScrolledText(popup, width = 75, height = 10, font="TkTextFont") citation.grid(column=0,row=1,sticky=N,padx=self.padx,pady=self.pady) citation.configure(state ='disabled') with open('media/Citation') as f: citation.insert(tk.END,f.read()) License_Label = tk.Label(popup,text='License:',font='TkTextFont') License_Label.grid(column=0,row=2,sticky=N,padx=self.padx,pady=self.pady) license = scrolledtext.ScrolledText(popup, width = 75, font = "TkTextFont") license.grid(column=0,row=3,sticky=N,padx=self.padx,pady=self.pady) license.configure(state ='disabled') with open('../LICENSE') as f: license.insert(tk.END,f.read()) B1 = ttk.Button(popup, text="Okay", command = popup.destroy) B1.grid(column=0,row=4,padx=self.padx,pady=self.pady) # popup.grid_columnconfigure((1,3),weight=1) # popup.grid_rowconfigure((1,3),weight=1) popup.grid_columnconfigure((0,2),weight=1) popup.grid_rowconfigure((0,2),weight=1) popup.protocol('WM_DELETE_WINDOW', popup.destroy) self.generate_button = tk.Button(self.root, text="Generate Input", command=self.Generate_ini) self.generate_button.grid(column=3,row=2,sticky=E,padx=self.padx,pady=self.pady) self.run_button = tk.Button(self.root,text='Run',command=self.run_term) self.run_button.grid(column=1,row=2,columnspan=1,sticky=E,padx=self.padx,pady=self.pady) self.stop_button = tk.Button(self.root,text='Stop',command=self.stop_term) self.stop_button.grid(column=2,row=2,columnspan=1,sticky=W,padx=self.padx,pady=self.pady) if self.os == 'Windows': self.stop_button.config(state='disabled') self.about_button = tk.Button(self.root,text='About',command=about_citation) self.about_button.grid(column=0,row=2,columnspan=1,sticky=W,padx=self.padx,pady=self.pady) self.root.grid_columnconfigure(1, weight=1) self.root.grid_columnconfigure(2, weight=1) self.root.grid_rowconfigure(0,weight=1) # Create a empty frame self.label_frame = LabelFrame(self.root, text="Terminal", padx=5, pady=5) self.label_frame.grid(column=0,row=1, columnspan=4, padx=self.padx, pady=self.pady, sticky=E+W+N+S) # Create the textbox self.label_frame.rowconfigure(0,weight=1) self.label_frame.columnconfigure(0,weight=1) self.txtbox = scrolledtext.ScrolledText(self.label_frame, width=40, height=10) self.txtbox.grid(row=0, column=0, sticky=E+W+N+S) def Build_inputs_tab(self): arr_input = ["Data file", "Output File","Number of FEFF folder"] #FEFF Folder self.description_tabs(arr_input,self.tab_Inputs,row = [0,1,3]) self.tab_Inputs.grid_columnconfigure(1,weight=1) entry_data_file = ttk.Combobox(self.tab_Inputs, textvariable=self.temp_data_file, font=self.entryFont) entry_data_file.grid(column=1, row=0, sticky=(W, E),padx=self.padx,pady=self.pady) entry_output_file = tk.Entry(self.tab_Inputs,textvariable=self.output_file,font=self.entryFont) entry_output_file.grid(column=1,row=1,sticky=(W,E),padx=self.padx,pady=self.pady) separator = ttk.Separator(self.tab_Inputs, orient='horizontal') separator.grid(column=0, row=2,columnspan=4,sticky=W+E,padx=self.padx) comp_list = list(range(1,6)) entry_ncomp = ttk.Combobox(self.tab_Inputs, width=7, values=comp_list,textvariable=self.ncomp, font=self.entryFont) entry_ncomp.grid(column=1, row=3, sticky=(W, E),padx=self.padx) def select_data_file(): os.chdir("..") #change the working directory from gui to EXAFS file_name = filedialog.askopenfilenames(initialdir = os.getcwd(), title = "Choose xmu/csv", filetypes = (("xmu files", "*.xmu"),("csv files","*.csv"),("all files","*.*"))) if not file_name: self.data_file.set('Please choose file/s') self.temp_data_file.set('Please choose file/s') else: if isinstance(file_name,tuple): if len(file_name)==1: self.data_file.set(str(file_name[0])) self.temp_data_file.set(str(file_name[0])) else: file_list = list(self.root.splitlist(file_name)) separator = ' ' self.data_file.set(separator.join(file_list)) entry_data_file['value'] = file_list self.series.set(True) os.chdir("gui") def select_output_file(): os.chdir("..") #change the working directory from gui to EXAFS file_name = filedialog.asksaveasfilename(initialdir = os.getcwd(), title = "Choose data_file", filetypes = (("csv files","*.csv"),("all files","*.*"))) if not file_name: self.output_file.set('Please choose a file') else: self.output_file.set(file_name) os.chdir("gui") def feff_trace(var,indx,mode): # Todo: Need to add assertion to make sure every folder is not normal raw_str = [] for i in range(len(self.feff_file_list)): raw_str.append(self.feff_file_list[i].get()) raw_feff = ','.join(raw_str) self.feff_file.set(raw_feff) # print(raw_feff) def gen_feff_folder(var,indx,mode): ncomp = self.ncomp.get() self.feff_file_toggle = [True for i in range(self.ncomp.get())] k = 4 try: self.arr_feff # self.input_list except AttributeError: pass else: for i in range(len(self.feff_input_list)): self.feff_input_list[i].destroy() self.feff_button_list[i].destroy() self.feff_description_list[i].destroy() self.feff_file_list = [] self.arr_feff= [] arr_row = [] # parameter sets self.feff_input_list = [] self.feff_button_list = [] self.feff_description_list = [] # Need to remove previous one for i in range(ncomp): self.arr_feff.append('FEFF Folder (' + str(i+1) +")") arr_row.append(i+k) # Generate a list of variables temp_var = StringVar(self.root,'Please choose a directory') self.feff_file_list.append(temp_var) # Add trace back temp_var.trace_add('write',feff_trace) # Setup each entry temp_entry = tk.Entry(self.tab_Inputs,textvariable=temp_var, font=self.entryFont) temp_entry.grid(column=1,row=k+i,sticky=(W,E), padx=self.padx,pady=self.pady) self.feff_input_list.append(temp_entry) # setup each button temp_button = ttk.Button(self.tab_Inputs,text="Choose", command=lambda x=i:select_feff_folder(self.feff_file_list[x]), style='my.TButton') temp_button.grid(column=3, row=k+i, sticky=W,padx=self.padx,pady=self.pady) self.feff_button_list.append(temp_button) self.feff_description_list = self.description_tabs(self.arr_feff,self.tab_Inputs,row=arr_row,return_description=True) def check_feff_folder(pathlib_Path): feff_inp_exists = False num_feff_file = 0 folder_Path = pathlib.Path(pathlib_Path) for i in folder_Path.iterdir(): file = pathlib.Path(i).name if fnmatch.fnmatch(file,'feff????.dat'): num_feff_file +=1 if num_feff_file == 0: # Check no scattering paths for i in folder_Path.iterdir(): file = pathlib.Path(i).name if fnmatch.fnmatch(file,'feff.inp'): feff_inp_exists = True feff_inp_loc = file abs_feff_inp_loc = folder_Path.joinpath('feff.inp') create_feff_folder(abs_feff_inp_loc) else: # self.txtbox.insert('No feff.inp in folder') self.txtbox.insert(tk.END,"No feff.inp in folder\n") # return False def select_feff_folder(var): os.chdir("..") #change the working directory from gui to EXAFS # select folder name folder_name = filedialog.askdirectory(initialdir = os.getcwd(), title = "Select folder") if not folder_name: var.set('Please choose a directory') else: check_feff_folder(folder_name) folder_name = os.path.join(folder_name,'feff') var.set(folder_name) os.chdir("gui") # add trace back self.ncomp.trace_add('write',gen_feff_folder) self.ncomp.set(1) button_data_file = ttk.Button(self.tab_Inputs, text="Choose", command=select_data_file, style='my.TButton') button_data_file.grid(column=3, row=0, sticky=W,padx=self.padx,pady=self.pady) button_output_file = ttk.Button(self.tab_Inputs,text="Choose", command=select_output_file, style='my.TButton') button_output_file.grid(column=3, row=1, sticky=W,padx=self.padx,pady=self.pady) def Build_population_tab(self): arr_pop = ["Population", "Number of Generations", "Best Individuals(%)", "Lucky Survivor(%)"] self.description_tabs(arr_pop,self.tab_Populations) self.tab_Populations.grid_columnconfigure(1,weight=1) entry_population = tk.Entry(self.tab_Populations, textvariable=self.populations, font=self.entryFont) entry_population.grid(column=1, row=0, sticky=(W, E),padx=self.padx) entry_num_gen = tk.Entry(self.tab_Populations, textvariable=self.num_gen, font=self.entryFont) entry_num_gen.grid(column=1, row=1, sticky=(W, E),padx=self.padx) entry_best_sample = tk.Entry(self.tab_Populations, textvariable=self.best_sample, font=self.entryFont) entry_best_sample.grid(column=1, row=2, sticky=(W, E),padx=self.padx) entry_lucky_few = tk.Entry(self.tab_Populations, textvariable=self.lucky_few, font=self.entryFont) entry_lucky_few.grid(column=1, row=3, sticky=(W, E),padx=self.padx) def Build_path_tab(self): """ Build path tabs """ arr_paths = ['Path Optimize','Multi-Path Optimize','Path Optimize Percentage'] self.description_tabs(arr_paths,self.tab_Paths,row = [0,1,2]) def checkbox_individual_paths(): if self.individual_path.get() == True: entry_path_range.config(state='disabled') entry_path_list.config(state='normal') else: entry_path_range.config(state='normal') entry_path_list.config(state='disabled') # def path_list_cb(var, indx, mode): # """ # Path_list call-back for the number of lists # """ # if self.individual_path.get()== True: # self.path_list.set(self.path_list_call.get()) # counts = 0 # test_list = self.path_list.get().split(",") # for i in range(len(test_list)): # if test_list[i] != '': # counts += 1 # self.path_range_call.set(counts) # entry_individual_paths = ttk.Checkbutton(self.tab_Paths, # variable = self.individual_path,command = checkbox_individual_paths) # entry_individual_paths.grid(column=1,row=0,sticky=(W),padx=self.padx) # self.path_range_call = StringVar(self.tab_Paths,str(self.path_range.get())) # self.path_range_call.trace_add("write",path_range_cb) # entry_path_range = ttk.Entry(self.tab_Paths, width=7, # textvariable=self.path_range_call, font=self.entryFont) # entry_path_range.grid(column=1, row=1, sticky=(W),padx=self.padx) # self.path_list_call = StringVar(self.tab_Paths,self.path_list.get()) # self.path_list_call.trace_add('write',path_list_cb) # entry_path_list = ttk.Entry(self.tab_Paths, # textvariable=self.path_list_call, font=self.entryFont) # entry_path_list.grid(column=1, row=2, sticky=(W, E),padx=self.padx) # entry_path_list.config(state='disabled') # def path_range_cb(var, indx, mode): # if self.individual_path.get()==False: # if self.path_range_call.get() == '': # int_test = 0 # else: # int_test = int(float(self.path_range_call.get())) # self.path_range_call.set(int_test) # self.path_range.set(int_test) # # custom_path_list = ",".join(np.char.mod('%i', np.arange(1,self.path_range.get()+1))) # self.path_list_call.set(custom_path_list) # self.path_list.set(custom_path_list) # def test_str(var): def output_var_trace(var,indx,mode): """ Updates the variables whenever it gets changes """ raw_str = [] # compounds if self.ncomp.get() > 1: for i in range(len(self.path_file_list)): # print(self.path_file_list[i].get()) # create the internal brackets # base_str = list(self.path_file_list[i].get()) var = self.path_file_list[i].get() if var == str(0) or var == "": base_str = '' # del the corresponding one from the final output self.feff_file_toggle[i] = False else: base_str = "[" + var + "]" self.feff_file_toggle[i] = True raw_str.append(base_str) # call back for path and feff temp_path_list = [] temp_feff_list = [] for i in range(len(self.path_file_list)): if self.feff_file_toggle[i] == True: temp_path_list.append(raw_str[i]) temp_feff_list.append(self.feff_file_list[i].get()) raw_path = ','.join(temp_path_list) raw_feff = ','.join(temp_feff_list) self.path_list.set(raw_path) self.feff_file.set(raw_feff) self.individual_path.set(True) # else calculate compounds else: self.path_list.set(self.path_file_list[0].get()) # print("-----------------------") # print(self.feff_file_toggle) # print(self.feff_file.get()) # print(self.path_list.get()) def ncomp_trace_cb(var, indx, mode): """ Trace the number of components, if changes, recomputed """ ncomp = self.ncomp.get() k = 5 # Starting components # Check if arr_pathlist exists try: self.arr_pathlist except AttributeError: pass else: # Destory all in the current list for i in range(len(self.path_input_list)): self.path_description_list[i].destroy() self.path_input_list[i].destroy() # Create the list self.arr_pathlist = [] self.path_input_list = [] self.path_file_list = [] arr_row = [] for i in range(ncomp): self.arr_pathlist.append('Pathlist Folder ('+ str(i+1) + ")") arr_row.append(i+k) # Create the entry variable temp_path_var = StringVar(self.root,'Path_list') temp_path_var.trace_add('write',output_var_trace) # temp_path_var.trace_add('write',) self.path_file_list.append(temp_path_var) # Create the entry temp_entry = tk.Entry(self.tab_Paths,textvariable=self.path_file_list[i], font=self.entryFont) # lock to the grid temp_entry.grid(column=1,columnspan=2,row=k+i,sticky=(W,E), padx=self.padx,pady=self.pady) self.path_input_list.append(temp_entry) # create the descriptions for it. self.path_description_list = self.description_tabs(self.arr_pathlist,self.tab_Paths,row=arr_row,return_description=True) entry_path_optimize = ttk.Checkbutton(self.tab_Paths, variable = self.path_optimize) entry_path_optimize.grid(column=1,row=0,sticky=(W,E),padx=self.padx) entry_path_optimize_only = ttk.Checkbutton(self.tab_Paths, variable = self.path_optimize_only) entry_path_optimize_only.grid(column=1,row=1,sticky=(W,E),padx=self.padx) entry_optimize_perc= ttk.Entry(self.tab_Paths, textvariable=self.path_optimize_pert, font=self.entryFont) entry_optimize_perc.grid(column=1, row=2, sticky=(W, E),padx=self.padx) separator = ttk.Separator(self.tab_Paths, orient='horizontal') separator.grid(column=0, row=3,columnspan=4,sticky=(W,E),padx=self.padx) self.tab_Paths.columnconfigure(4,weight=1) # add the call back for it to modify if it get changes self.ncomp.trace_add('write',ncomp_trace_cb) # inital default value self.ncomp.set(1) def Build_mutations_tab(self): arr_muts = ["Mutation Chance (%)", "Original chance of mutation (%)", "E0 Mutation Chance (%)", "Mutation Options"] self.description_tabs(arr_muts,self.tab_Mutation) mut_list = list(range(101)) # self.tab_Inputs.grid_columnconfigure(1,weight=1) entry_chance_of_mutation = ttk.Combobox(self.tab_Mutation, width=7, values=mut_list,textvariable=self.chance_of_mutation, font=self.entryFont) entry_chance_of_mutation.grid(column=1, row=0, sticky=(W, E),padx=self.padx) entry_chance_of_mutation = ttk.Combobox(self.tab_Mutation, width=7, values=mut_list,textvariable=self.orginal_chance_of_mutation, font=self.entryFont) entry_chance_of_mutation.grid(column=1, row=1, sticky=(W, E),padx=self.padx) entry_chance_of_mutation = ttk.Combobox(self.tab_Mutation, width=7, values=mut_list,textvariable=self.chance_of_mutation_e0, font=self.entryFont) entry_chance_of_mutation.grid(column=1, row=2, sticky=(W, E),padx=self.padx) mut_list = list(range(3)) entry_chance_of_mutation = ttk.Combobox(self.tab_Mutation, width=7, values=mut_list,textvariable=self.mutation_options, font=self.entryFont) entry_chance_of_mutation.grid(column=1, row=3, sticky=(W, E),padx=self.padx) def Build_larch_tab(self): arr_larch = ["Kmin", "Kmax", "Kweight", "Delta k", "R Bkg", "Bkg Kw", "Bkg Kmax"] self.description_tabs(arr_larch,self.tab_Larch) entry_kmin = ttk.Entry(self.tab_Larch, textvariable=self.kmin, font=self.entryFont) entry_kmin.grid(column=1, row=0, sticky=(W, E),padx=self.padx) entry_kmax = ttk.Entry(self.tab_Larch, textvariable=self.kmax, font=self.entryFont) entry_kmax.grid(column=1, row=1, sticky=(W, E),padx=self.padx) entry_k_weight = ttk.Entry(self.tab_Larch, textvariable=self.k_weight, font=self.entryFont) entry_k_weight.grid(column=1, row=2, sticky=(W, E),padx=self.padx) entry_delta_k = ttk.Entry(self.tab_Larch, textvariable=self.delta_k, font=self.entryFont) entry_delta_k.grid(column=1, row=3, sticky=(W, E),padx=self.padx) entry_r_bkg = ttk.Entry(self.tab_Larch, textvariable=self.r_bkg, font=self.entryFont) entry_r_bkg.grid(column=1, row=4, sticky=(W, E),padx=self.padx) entry_bkg_kw = ttk.Entry(self.tab_Larch, textvariable=self.bkg_kw, font=self.entryFont) entry_bkg_kw.grid(column=1, row=5, sticky=(W, E),padx=self.padx) entry_bkg_kmax = ttk.Entry(self.tab_Larch, textvariable=self.bkg_kmax, font=self.entryFont) entry_bkg_kmax.grid(column=1, row=6, sticky=(W, E),padx=self.padx) def Build_background_tab(self): def button_bkg_draw(): # TODO: # Makes it so bkg_plot.inital_parameters(self.temp_data_file,self.r_bkg,self.bkg_kw,self.bkg_kmax,self.kmin,self.kmax,self.delta_k,self.k_weight) bkg_plot.draw_background() arr_bkg = ["R Bkg", "K min","K max","Bkg Kw", "Bkg Kmax"] self.description_tabs(arr_bkg,self.tab_Bkg,sticky=(W,E,N)) # self.tab_Bkg.grid_rowconfigure(3,weight=1) # self.tab_Bkg.grid_rowconfigure(4,weight=1) # self.tab_Bkg.grid_rowconfigure(5,weight=1) # self.tab_Bkg.grid_rowconfigure(6,weight=1) self.tab_Bkg.rowconfigure(8,weight=1) self.tab_Bkg.columnconfigure(2,weight=1) entry_r_bkg = ttk.Entry(self.tab_Bkg, textvariable=self.r_bkg, font=self.entryFont) entry_r_bkg.grid(column=1, row=0, sticky=(W,E,N),padx=self.padx,pady=self.pady) entry_k_min = ttk.Entry(self.tab_Bkg, textvariable=self.kmin, font=self.entryFont) entry_k_min.grid(column=1, row=1, sticky=(W,E,N),padx=self.padx,pady=self.pady) entry_k_max = ttk.Entry(self.tab_Bkg, textvariable=self.kmax, font=self.entryFont) entry_k_max.grid(column=1, row=2, sticky=(W,E,N),padx=self.padx,pady=self.pady) entry_bkg_kw = ttk.Entry(self.tab_Bkg, textvariable=self.bkg_kw, font=self.entryFont) entry_bkg_kw.grid(column=1, row=3, sticky=(W,E,N),padx=self.padx,pady=self.pady) entry_bkg_kmax = ttk.Entry(self.tab_Bkg, textvariable=self.bkg_kmax, font=self.entryFont) entry_bkg_kmax.grid(column=1, row=4, sticky=(W,E,N),padx=self.padx,pady=self.pady) bkg_plot = BKG_plot(self.tab_Bkg,self.mylarch) button_bkg_mu = ttk.Button(self.tab_Bkg, text="Plot Background and Mu", command=button_bkg_draw) button_bkg_mu.grid(column=0, row=5,columnspan=2,sticky=W+E,padx=self.padx,pady=self.pady) button_kspace = ttk.Button(self.tab_Bkg, text="K space", command=bkg_plot.draw_kspace) button_kspace.grid(column=0, row=6,columnspan=2,sticky=W+E,padx=self.padx,pady=self.pady) button_rspace = ttk.Button(self.tab_Bkg, text="R space", command=bkg_plot.draw_rspace) button_rspace.grid(column=0, row=7,columnspan=2,sticky=W+E,padx=self.padx,pady=self.pady) def Build_output_tab(self): pop_min = IntVar(self.tab_Output,100) pop_max = IntVar(self.tab_Output,5001) gen_min = IntVar(self.tab_Output,20) gen_max = IntVar(self.tab_Output,501) mut_min = IntVar(self.tab_Output,20) mut_max = IntVar(self.tab_Output,51) pertub_check = IntVar(self.tab_Output,0) def generate_multi_ini(): # os.chdir("..") pop_range = np.arange(pop_min.get(),pop_max.get(),100) gen_range = np.arange(gen_min.get(),gen_max.get(),5) mut_range = np.arange(mut_min.get(),mut_max.get(),10) # exit() multi_folder = filedialog.askdirectory(initialdir = os.getcwd(),title = 'Select folder') if not multi_folder: return else: og_pop = self.populations.get() og_gen = self.num_gen.get() og_mut = self.chance_of_mutation.get() og_out_file = self.output_file.get() for i in range(self.n_ini.get()): pop_select = np.random.choice(pop_range) gen_select = np.random.choice(gen_range) mut_select = np.random.choice(mut_range) base_file = os.path.splitext(og_out_file)[0] self.output_file.set(os.path.join(base_file +"_"+str(i).zfill(3) +".ini")) self.populations.set(pop_select) self.num_gen.set(gen_select) self.chance_of_mutation.set(mut_select) self.Write_ini(multi_folder + "/file_" + str(i).zfill(3)+'.i') # set back og ini self.populations.set(og_pop) self.num_gen.set(og_gen) self.chance_of_mutation.set(og_mut) self.output_file.set(og_out_file) # os.chdir("gui") return multi_folder def run_multi_ini(): """ Run multiple ini file """ folder_loc = generate_multi_ini() full_file_list = [] file_list = glob.glob(folder_loc + "/*.i") for i in file_list: full_file_list.append(os.path.join(folder_loc,i)) full_file_list.sort(key=natural_keys) for i in full_file_list: print(bcolors.BOLD + str(i) + bcolors.ENDC) self.run_ini(i) def checkbox_multi(): widget_lists=[ entry_n_ini, entry_pertub_pop_min, entry_pertub_pop_max, entry_pertub_gen_min, entry_pertub_gen_max, entry_pertub_mut_min, entry_pertub_mut_max, button_gen_nini, button_run_nini] if pertub_check.get() == 0: for i in widget_lists: i.config(state='disabled') elif pertub_check.get() == 1: for i in widget_lists: i.config(state='normal') arr_out = ["Print graph", "Steady state exit"] self.description_tabs(arr_out,self.tab_Output) checkbutton_print_graph = ttk.Checkbutton(self.tab_Output, var=self.print_graph) checkbutton_print_graph.grid(column=1, row=0,sticky=W+E,padx=self.padx) checkbutton_steady_state= ttk.Checkbutton(self.tab_Output, var=self.steady_state_exit) checkbutton_steady_state.grid(column=1, row=1,sticky=W+E,padx=self.padx) # Create separators separator = ttk.Separator(self.tab_Output, orient='horizontal') separator.grid(column=0, row=2,columnspan=4,sticky=W+E,padx=self.padx) self.tab_Output.columnconfigure(3,weight=1) arr_out = ["Create Multiple Input Files","Number of Ini Files","Pertubutions-Population(min,max)", "Pertubutions-Generation(min,max)","Pertubutions-Mutation(min,max)"] self.description_tabs(arr_out,self.tab_Output,row=[3,5,6,7,8]) # Create New pertubutuions checkbutton_pertub= ttk.Checkbutton(self.tab_Output, var=pertub_check,command=checkbox_multi) checkbutton_pertub.grid(column=1, row=3,sticky=W+E,padx=self.padx) pertub_list = list(range(1,101)) text ='Each entry allows user to control perturbation percentage of the desire variables.' entry = ttk.Label(self.tab_Output,text=text,font=self.labelFont) entry.grid_configure(column=0,row=4,columnspan=3,sticky=W+E,padx=self.padx,pady=self.pady) entry_n_ini = tk.Entry(self.tab_Output,textvariable=self.n_ini,font=self.entryFont) entry_n_ini.grid(column=1, row=5,columnspan=2,sticky=(W, E),padx=self.padx) entry_n_ini.config(state='disabled') width = 5 # -------------- entry_pertub_pop_min= ttk.Entry(self.tab_Output, width=width,textvariable=pop_min, font=self.entryFont) entry_pertub_pop_min.grid(column=1, row=6, sticky=(W, E),padx=self.padx) entry_pertub_pop_max= ttk.Entry(self.tab_Output, width=width,textvariable=pop_max, font=self.entryFont) entry_pertub_pop_max.grid(column=2, row=6, sticky=(W, E),padx=self.padx) entry_pertub_pop_min.config(state='disabled') entry_pertub_pop_max.config(state='disabled') # -------------- entry_pertub_gen_min= ttk.Entry(self.tab_Output, width=width,textvariable=gen_min, font=self.entryFont) entry_pertub_gen_min.grid(column=1, row=7, sticky=(W, E),padx=self.padx) entry_pertub_gen_max= ttk.Entry(self.tab_Output, width=width,textvariable=gen_max, font=self.entryFont) entry_pertub_gen_max.grid(column=2, row=7, sticky=(W, E),padx=self.padx) entry_pertub_gen_min.config(state='disabled') entry_pertub_gen_max.config(state='disabled') # -------------- entry_pertub_mut_min= ttk.Entry(self.tab_Output, width=width,textvariable=mut_min, font=self.entryFont) entry_pertub_mut_min.grid(column=1, row=8, sticky=(W, E),padx=self.padx) entry_pertub_mut_max= ttk.Entry(self.tab_Output, width=width,textvariable=mut_max, font=self.entryFont) entry_pertub_mut_max.grid(column=2, row=8, sticky=(W, E),padx=self.padx) entry_pertub_mut_min.config(state='disabled') entry_pertub_mut_max.config(state='disabled') # -------------- button_gen_nini = tk.Button(self.tab_Output,text="Generate Input Files",command=generate_multi_ini) button_gen_nini.grid(column=0, row=9,columnspan=3,sticky=W+E,padx=self.padx,pady=self.pady) button_gen_nini.config(state='disabled') button_run_nini = tk.Button(self.tab_Output,text="Run Multiple Input Files",command=run_multi_ini) button_run_nini.grid(column=0, row=10,columnspan=3,sticky=W+E,padx=self.padx,pady=self.pady) button_run_nini.config(state='disabled') def Build_analysis_tab(self): def select_analysis_folder(): os.chdir("..") #change the working directory from gui to EXAFS folder_name = filedialog.askdirectory(initialdir = os.getcwd(), title = "Select folder") if not folder_name: analysis_folder.set('Please choose a directory') else: # folder_name = os.path.join(folder_name,'feff') analysis_folder.set(folder_name) # print(self.feff_file.get()) os.chdir("gui") def run_analysis(): params = {} # params['base'] = Path(os.getcwd()).parent params['base'] = '' params['Kmin'] = self.kmin.get() params['Kmax'] = self.kmax.get() params['kweight'] = self.k_weight.get() params['deltak'] = self.delta_k.get() params['rbkg'] = self.r_bkg.get() params['bkgkw'] = self.bkg_kw.get() params['bkgkmax'] = self.bkg_kmax.get() params['front'] = self.feff_file.get().split(',') params['CSV'] = self.data_file.get() params['optimize'] = self.path_optimize.get() params['series_index'] = series_index.get() params['series'] = self.series.get() # set up the params self.txtbox.insert(tk.END,"Running Analysis...\n") analysis_plot.setup_params(params) paths = '[' + self.path_list.get() + ']' paths_list = ast.literal_eval(paths) # analysis_plot.setup_paths(paths) print(paths) print(paths_list) print(type(paths_list)) analysis_plot.setup_paths(ast.literal_eval(paths)) analysis_plot.setup_dirs(analysis_folder.get()) arr_str,latex_str = analysis_plot.extract_and_run(analysis_folder) self.txtbox.insert(tk.END,arr_str) self.txtbox.insert(tk.END,"-------------\n") self.txtbox.insert(tk.END,latex_str) self.txtbox.insert(tk.END,"\n-------------\n") self.txtbox.insert(tk.END,"Done") def plot_occurances(): # plot the self_occurances, and then plot the others """ To do: occurances only works with one """ analysis_plot.plot_occurances(analysis_folder.get(),self.path_optimize_pert.get(),self.path_list.get()) analysis_folder = StringVar(self.tab_Analysis,'Please choose a directory') series_index = IntVar(self.tab_Analysis,0) self.tab_Analysis.columnconfigure(1,weight=1) self.tab_Analysis.columnconfigure(2,weight=1) self.tab_Analysis.rowconfigure(10,weight=1) arr_out = ["Select folder"] self.description_tabs(arr_out,self.tab_Analysis) entry_analysis_folder = tk.Entry(self.tab_Analysis,textvariable=analysis_folder,font=self.entryFont) entry_analysis_folder.grid(column=1,row=0,columnspan=2,sticky=W+E,padx=self.padx,pady=self.pady) button_analysis_folder = ttk.Button(self.tab_Analysis,text="Choose", command=select_analysis_folder, style='my.TButton') button_analysis_folder.grid(column=3, row=0, sticky=W+E,padx=self.padx,pady=self.pady) separator = ttk.Separator(self.tab_Analysis, orient='horizontal') separator.grid(column=0, row=1,columnspan=4,sticky=W+E,padx=self.padx) analysis_plot = Analysis_Plot(self.tab_Analysis,self.mylarch) # ---------------------------------------------------------------------- button_Run_Analysis = ttk.Button(self.tab_Analysis,text="Run Analysis", command=run_analysis, style='my.TButton') button_Run_Analysis.grid(column=0, row=2,columnspan=4, sticky=W+E,padx=self.padx,pady=self.pady) button_plot_kR = ttk.Button(self.tab_Analysis,text="Plot K and R Spectrum", command=analysis_plot.plot_k_r_space, style='my.TButton') button_plot_kR.grid(column=0, row=3,columnspan=1, sticky=W+E,padx=self.padx,pady=self.pady) button_plot_individual = ttk.Button(self.tab_Analysis,text='Plot K Individual', command=analysis_plot.plot_individual, style='my.TButton') button_plot_individual.grid(column=1, row=3,columnspan=1, sticky=W+E,padx=self.padx,pady=self.pady) button_plot_error = ttk.Button(self.tab_Analysis,text="Plot Error", command=analysis_plot.plot_error, style='my.TButton') button_plot_error.grid(column=2, row=3,columnspan=1, sticky=W+E,padx=self.padx,pady=self.pady) button_plot_occurances = ttk.Button(self.tab_Analysis,text='Plot Occurances', command=plot_occurances, style='my.TButton') button_plot_occurances.grid(column=3,row=3,columnspan=1,sticky=W+E,padx=self.padx,pady=self.pady) def Build_expert_tab(self): arr_muts = ["Override Num Compounds"] self.description_tabs(arr_muts,self.tab_Expert) ncomp_list = list(range(1,101)) entry_ncomp = ttk.Combobox(self.tab_Expert, width=7, values=ncomp_list,textvariable=self.ncomp, font=self.entryFont) entry_ncomp.grid(column=1, row=0, sticky=(W, E),padx=self.padx) def On_closing(self): """ on closing function """ if messagebox.askokcancel("Quit", "Do you want to quit?"): self.stop_term() if hasattr(self,'terminal'): self.root.quit() self.terminal.destroy() else: self.root.quit() def Run(self): """ Run the code """ self.root.protocol('WM_DELETE_WINDOW', self.On_closing) self.beta_popup() self.root.mainloop() def beta_popup(self): beta_popup = tk.Toplevel(self.root) beta_popup.wm_title("Warning") msg = "This Graphical User Interface is still under active development.\nPlease contact us using Github Issues." entry = ttk.Label(beta_popup,text=msg) entry.grid(column=0,row=0,padx=5,pady=3) B1 = ttk.Button(beta_popup, text="Okay", command = beta_popup.destroy) B1.grid(column=0,row=1,padx=5,pady=3) beta_popup.grid_columnconfigure((0,1),weight=1) beta_popup.grid_rowconfigure((0,1),weight=1) beta_popup.protocol('WM_DELETE_WINDOW', beta_popup.destroy) beta_popup.attributes('-topmost', 'true')
def on_value_change(event): global selected_chapter # The extra chapters have urls that contain dashes instead of dots (e. g. 64.5 becomes 64-5) selected_chapter = ((dropDown.get()).split(" ", 1)[0]).replace(".", "-") def on_radiobutton_change(a, b, c): dropDown.config(values=choices[selected_manga.get()]) dropDown_vol.config(values=choices_vol[selected_manga.get()]) dropDown.current(0) dropDown_vol.current(0) dropDown.bind("<<ComboboxSelected>>", on_value_change) selected_manga.trace_add("write", on_radiobutton_change) note_book.add(page_download_chapters, text="Chapters") note_book.add(page_download_volumes, text="Volumes") note_book.add(page_show_help, text="Info") note_book.grid(column=0, row=0, padx=10, pady=10, ipadx=10, ipady=10) progress = ttk.Progressbar(app, orient="horizontal", length=450, mode='determinate') progress.grid(column=0, row=2, ipady=5) progressLabel = ttk.Label(app, text="Ready") progressLabel.grid(column=0, row=1, pady=(5, 0))
class MakeScale: """"create slider (scale) widgets""" def __init__(self, frame, step=0.0, offset=1.0, res=1): self.row_i = 1 self.column = 2 self.rowspan = 1 self.sticky = 'NSEW' self.frame = frame self.text = "" self.from_ = 1 self.to = 11 self.step = step self.offset = offset self.res = res self.input_var = IntVar() def create_separator(self): Separator(self.frame).grid(row=self.row_i) def create_empty_space(self): Label(self.frame, anchor=W).grid(row=self.row_i) self.row_i += 1 def create_scale(self): scale = Limiter(self.frame, from_=self.from_, to=self.to, orient=HORIZONTAL, length=200, variable=self.input_var, precision=0, step=self.step, offset=self.offset, res=self.res) scale.grid(row=self.row_i, column=self.column + 1, columnspan=2, rowspan=self.rowspan, sticky=self.sticky) return scale def set_offset(self, label): var = self.input_var step = self.step res = self.res offset = self.offset def update_other_label(*args): value = var.get() multiplier = round(value * step, res) product = round(offset + multiplier - step, res) input_var_mult.set(product) self.input_var.trace_add("write", update_other_label) input_var_mult = DoubleVar() label.config(textvariable=input_var_mult) def create_text_label(self): txt = Label(self.frame, text=self.text, anchor=W) txt.grid(row=self.row_i, column=self.column, columnspan=1, rowspan=self.rowspan, sticky=self.sticky) def create_value_label(self): scale_nr = Label(self.frame, textvariable=self.input_var, anchor=E, width=5) scale_nr.grid(row=self.row_i, column=self.column + 4, columnspan=1, rowspan=self.rowspan, sticky=self.sticky) return scale_nr def make(self, text, from_=1, to=11, step=0.0, offset=1.0, res=1): self.input_var = IntVar() self.from_ = from_ self.to = to self.text = text self.step = step self.offset = offset self.res = res if (self.row_i % 3) == 0: # create empty space every 2 sliders self.create_empty_space() self.create_separator() scale = self.create_scale() self.create_text_label() scale_nr = self.create_value_label() if step != 0: self.set_offset(scale_nr) self.row_i += 1 return scale