def save(event=None): name = name_entry.get().strip() if not mailbox: # new mailbox i = self.b_add.grid_info()['row'] self.b_add.grid_configure(row=i + 1) c = Checkbutton(self.frame) c.state(('selected',)) c.grid(row=i, column=0, pady=4, padx=(4, 0)) l = Label(self.frame, text=name) l.grid(row=i, column=1, padx=4, pady=4) b_edit = Button(self.frame, image=self.im_edit, width=1, command=lambda m=name: self.mailbox_info(m)) b_edit.grid(row=i, column=2, padx=4, pady=4) b_del = Button(self.frame, image=self.im_del, width=1, command=lambda m=name: self.del_mailbox(m)) b_del.grid(row=i, column=3, padx=4, pady=4) self.mailboxes[name] = [c, l, b_edit, b_del] elif name != mailbox: # change name of mailbox os.remove(os.path.join(LOCAL_PATH, mailbox)) c, l, b_edit, b_del = self.mailboxes[mailbox] del(self.mailboxes[mailbox]) l.configure(text=name) b_edit.configure(command=lambda m=name: self.mailbox_info(m)) b_del.configure(command=lambda m=name: self.del_mailbox(m)) self.mailboxes[name] = [c, l, b_edit, b_del] encrypt(name, self.pwd, server_entry.get().strip(), login_entry.get().strip(), password_entry.get().strip(), folder_entry.get().strip()) top.destroy()
def __init__(self, master, pwd): """Create the mailbox manager dialog.""" Toplevel.__init__(self, master, class_="CheckMails") self.title(_("Mailbox Manager")) self.minsize(200, 10) self.pwd = pwd self.resizable(False, False) self.protocol("WM_DELETE_WINDOW", self.quit) self.im_add = PhotoImage(master=self, file=ADD) self.im_del = PhotoImage(master=self, file=DEL) self.im_edit = PhotoImage(master=self, file=EDIT) self.mailboxes = {} active = CONFIG.get("Mailboxes", "active").split(", ") inactive = CONFIG.get("Mailboxes", "inactive").split(", ") while "" in active: active.remove("") while "" in inactive: inactive.remove("") active.sort() inactive.sort() self.frame = Frame(self) self.columnconfigure(0, weight=1) self.frame.columnconfigure(1, weight=1) self.frame.grid(row=0, column=0, padx=10, pady=10, sticky="eswn") i = -1 for i, box in enumerate(active): c = Checkbutton(self.frame) c.state(('selected',)) c.grid(row=i, column=0, pady=4, padx=(4, 0)) l = Label(self.frame, text=box) l.grid(row=i, column=1, padx=4, pady=4) b_edit = Button(self.frame, image=self.im_edit, width=1, command=lambda m=box: self.mailbox_info(m)) b_edit.grid(row=i, column=2, padx=4, pady=4) b_del = Button(self.frame, image=self.im_del, width=1, command=lambda m=box: self.del_mailbox(m)) b_del.grid(row=i, column=3, padx=4, pady=4) self.mailboxes[box] = [c, l, b_edit, b_del] for box in inactive: i += 1 c = Checkbutton(self.frame) c.grid(row=i, column=0, pady=4, padx=(4, 0)) l = Label(self.frame, text=box) l.grid(row=i, column=1, padx=4, pady=4) b_edit = Button(self.frame, image=self.im_edit, width=1, command=lambda m=box: self.mailbox_info(m)) b_edit.grid(row=i, column=2, padx=4, pady=4) b_del = Button(self.frame, image=self.im_del, width=1, command=lambda m=box: self.del_mailbox(m)) b_del.grid(row=i, column=3, padx=4, pady=4) self.mailboxes[box] = [c, l, b_edit, b_del] self.b_add = Button(self.frame, image=self.im_add, command=self.mailbox_info, width=1) self.b_add.grid(row=i + 1, column=0, columnspan=4, pady=4, padx=4, sticky='w')
class UpdateChecker(Toplevel): version_parser = VersionParser() def __init__(self, master, notify=False): Toplevel.__init__(self, master, class_="CheckMails") logging.info('Checking for updates') self.title(_("Update")) self.withdraw() self.rowconfigure(0, weight=1) self.columnconfigure(0, weight=1) self.columnconfigure(1, weight=1) self.protocol("WM_DELETE_WINDOW", self.quit) self.notify = notify self.img = PhotoImage(file=IM_QUESTION, master=self) frame = Frame(self) frame.grid(row=0, columnspan=2, sticky="ewsn") Label(frame, image=self.img).pack(side="left", padx=(10, 4), pady=(10, 4)) Label(frame, text=_("A new version of CheckMails is available.\ \nDo you want to download it?"), font="TkDefaultFont 10 bold", wraplength=335).pack(side="left", padx=(4, 10), pady=(10, 4)) self.b1 = Button(self, text=_("Yes"), command=self.download) self.b1.grid(row=1, column=0, padx=10, pady=10, sticky="e") Button(self, text=_("No"), command=self.quit).grid(row=1, column=1, padx=10, pady=10, sticky="w") self.ch = Checkbutton(self, text=_("Check for updates on startup.")) if CONFIG.getboolean("General", "check_update"): self.ch.state(("selected", )) self.ch.grid(row=2, columnspan=2, sticky='w') self.update = None self.thread = Thread(target=self.update_available, daemon=True) self.thread.start() self.after(1000, self.check_update) def check_update(self): if self.update is None: self.after(1000, self.check_update) elif self.update: self.deiconify() self.grab_set() self.lift() self.b1.focus_set() else: if self.notify: run([ "notify-send", "-i", IMAGE2, _("Update"), _("CheckMails is up-to-date.") ]) logging.info("CheckMails is up-to-date") self.destroy() def quit(self): CONFIG.set("General", "check_update", str("selected" in self.ch.state())) save_config() self.destroy() def download(self): webOpen("https://sourceforge.net/projects/checkmails/files") self.quit() def update_available(self): """ Check for updates online, return True if an update is available, False otherwise (and if there is no Internet connection). """ try: with request.urlopen( 'https://sourceforge.net/projects/checkmails') as page: latest_version = self.version_parser.feed(page.read().decode()) self.update = latest_version > __version__ except error.URLError as e: if e.reason.errno == -2: # no Internet connection self.update = False elif e.reason.errno == 104: # connection timed out self.update_available() else: raise e
class StringEntry(LabelFrame): # changed """String class for entry rationalised with integer and float classes super Parameters ---------- parent : str parent handle lf_text : str text on LabelFrame def_inp : str default text colour : str frame colour mod : boolean enable or disable state switch Returns ------- string """ def __init__(self, parent, lf_text, def_inp="", colour='brown', mod=False): self.lf_text = lf_text super().__init__(parent, text=lf_text) # added self.mod = mod self.ent0 = None # for entry self.cb_opt = None # for check option self.out_var = StringVar() self.out_var.set(def_inp) self.construct(colour) def construct(self, colour): """construct of colour style Parameters ---------- colour : str frame colour Returns ------- None """ self.farbe = farbe = { 'blue': 'light blue', 'brown': 'brown1', 'green': 'light green', 'pink': '#EAAFBF' } colour = colour if colour in farbe else 'brown' self.colour = colour st1 = Style() st1.theme_use('default') st1.configure(colour + '.TLabelframe', background='#C9B99B') st1.configure(colour + '.TLabelframe.Label', background=farbe[colour]) st1.configure(colour + '.TCheckbutton', background=farbe[colour]) st1.configure('brown.TLabel', background='#EDEF77') st1.configure('lowr.TLabel', background='lightblue') st1.configure('upr.TLabel', background='red') # self.lf1 = Labelframe(self.fr, text=self.lf_text, # style=self.colour+'.TLabelframe') # self.lf1.grid(column=0,row=0,padx=10, pady=10) self['style'] = self.colour + '.TLabelframe' self.messlbl = Label(self, style='brown.TLabel') # self.lf1 self.messlbl.grid(row=2, column=0, pady=10, padx=10) self.make_entry() def make_entry(self): """construct of Entry Parameters ---------- None Returns ------- None """ vcmd = self.register(self.is_okay) self.ent0 = ent0 = Entry( self, validate='key', validatecommand=(vcmd, '%P', '%S', '%i'), # self.lf1 textvariable=self.out_var) ent0.bind("<Return>", self.end_input) ent0.grid(row=1, column=0, padx=10) ent0.focus() if self.mod in (True, False): self.modify() def modify(self): """construct of state switch Parameters ---------- None Returns ------- None """ # entry disabled until checkbox is ticked self.cb_opt = Checkbutton( self, command=self.toggle_opt, # self.lf1 style=self.colour + '.TCheckbutton') self['labelwidget'] = self.cb_opt # self.lf1[ if self.mod: self.ent0.state(['!disabled']) self.cb_opt.state(['!selected']) self.cb_opt['text'] = self.lf_text self.ent0.focus() else: self.ent0.state(['disabled']) self.cb_opt.state(['selected']) self.cb_opt['text'] = self.lf_text def toggle_opt(self): """state switch logic Parameters ---------- None Returns ------- None """ # state of entry controlled # by the state of the check button in Option frame label widget if self.cb_opt.instate(['selected']): self.ent0.state(['disabled']) self.cb_opt['text'] = self.lf_text else: self.ent0.state(['!disabled']) self.cb_opt['text'] = self.lf_text self.ent0.focus() def end_input(self, _evt): """limit on string Parameters ---------- evt : str bind handle Returns ------- None """ if len(self.out_var.get()) > 5: self.messlbl['text'] = "That's OK" else: self.messlbl['text'] = "Need at least 6 characters" def is_okay(self, text, inp, ind): """ validation function Parameters ---------- text : str text if allowed inp : str current input Returns ------- boolean """ ind = int(ind) if (inp.isalnum() or inp in (",", ".", "'", " ")) and ind > 0: return True else: return bool((text.isupper() or text == "") and ind == 0)
class Config(Toplevel): def __init__(self, master): Toplevel.__init__(self, master, class_=APP_NAME) self.title(_("Settings")) self.grab_set() self.columnconfigure(0, weight=1) self.columnconfigure(1, weight=1) self.rowconfigure(0, weight=1) self.resizable(True, True) self.minsize(470, 574) style = Style(self) self._bg = style.lookup('TFrame', 'background') self.notebook = Notebook(self) self._validate = self.register(self._validate_entry_nb) self.img_color = PhotoImage(master=self, file=IM_COLOR) self.lang = StringVar(self, LANGUAGES[CONFIG.get("General", "language")]) self.gui = StringVar(self, CONFIG.get("General", "trayicon").capitalize()) self._init_general() self._init_widget() self.notebook.grid(sticky='ewsn', row=0, column=0, columnspan=2) Button(self, text=_('Ok'), command=self.ok).grid(row=1, column=0, sticky='e', padx=4, pady=10) Button(self, text=_('Cancel'), command=self.destroy).grid(row=1, column=1, sticky='w', padx=4, pady=10) def _init_general(self): frame_general = Frame(self) self.notebook.add(frame_general, text=_("General")) # --- Language Label(frame_general, text=_("Language")).grid(row=0, column=0, padx=8, pady=4, sticky="e") menu_lang = Menu(frame_general, tearoff=False, background=self._bg) mb = Menubutton(frame_general, menu=menu_lang, textvariable=self.lang) mb.grid(row=0, column=1, padx=8, pady=4, sticky="w") for lang in LANGUAGES: language = LANGUAGES[lang] menu_lang.add_radiobutton(label=language, value=language, variable=self.lang, command=self.translate) # --- gui toolkit Label(frame_general, text=_("GUI Toolkit for the system tray icon")).grid(row=2, column=0, padx=8, pady=4, sticky="e") menu_gui = Menu(frame_general, tearoff=False, background=self._bg) Menubutton(frame_general, menu=menu_gui, width=9, textvariable=self.gui).grid(row=2, column=1, padx=8, pady=4, sticky="w") for toolkit, b in TOOLKITS.items(): if b: menu_gui.add_radiobutton(label=toolkit.capitalize(), value=toolkit.capitalize(), variable=self.gui, command=self.change_gui) # --- Update delay Label(frame_general, text=_("Feed update delay (min)")).grid(row=4, column=0, padx=8, pady=4, sticky="e") self.entry_delay = Entry(frame_general, width=10, justify='center', validate='key', validatecommand=(self._validate, '%P')) self.entry_delay.grid(row=4, column=1, padx=8, pady=4, sticky='w') self.entry_delay.insert( 0, CONFIG.getint('General', 'update_delay') // 60000) # --- image loading timeout Label(frame_general, text=_("Image loading timeout (s)")).grid(row=5, column=0, padx=8, pady=4, sticky="e") self.entry_timeout = Entry(frame_general, width=10, justify='center', validate='key', validatecommand=(self._validate, '%P')) self.entry_timeout.grid(row=5, column=1, padx=8, pady=4, sticky='w') self.entry_timeout.insert( 0, CONFIG.getint('General', 'img_timeout', fallback=10)) # --- Notifications self.notifications = Checkbutton(frame_general, text=_("Activate notifications")) self.notifications.grid(row=6, column=0, padx=8, pady=4, columnspan=2, sticky='w') if CONFIG.getboolean('General', 'notifications', fallback=True): self.notifications.state(('selected', '!alternate')) else: self.notifications.state(('!selected', '!alternate')) # --- Confirm remove feed self.confirm_feed_rem = Checkbutton( frame_general, text=_("Show confirmation dialog before removing feed")) self.confirm_feed_rem.grid(row=7, column=0, padx=8, pady=4, columnspan=2, sticky='w') if CONFIG.getboolean('General', 'confirm_feed_remove', fallback=True): self.confirm_feed_rem.state(('selected', '!alternate')) else: self.confirm_feed_rem.state(('!selected', '!alternate')) # --- Confirm remove cat self.confirm_cat_rem = Checkbutton( frame_general, text=_("Show confirmation dialog before removing category")) self.confirm_cat_rem.grid(row=8, column=0, padx=8, pady=4, columnspan=2, sticky='w') if CONFIG.getboolean('General', 'confirm_cat_remove', fallback=True): self.confirm_cat_rem.state(('selected', '!alternate')) else: self.confirm_cat_rem.state(('!selected', '!alternate')) # --- Confirm update self.confirm_update = Checkbutton( frame_general, text=_("Check for updates on start-up")) self.confirm_update.grid(row=9, column=0, padx=8, pady=4, columnspan=2, sticky='w') if CONFIG.getboolean('General', 'check_update', fallback=True): self.confirm_update.state(('selected', '!alternate')) else: self.confirm_update.state(('!selected', '!alternate')) # --- Splash supported self.splash_support = Checkbutton( frame_general, text=_("Check this box if the widgets disappear when you click")) self.splash_support.grid(row=10, column=0, padx=8, pady=4, columnspan=2, sticky='w') if not CONFIG.getboolean('General', 'splash_supported', fallback=True): self.splash_support.state(('selected', '!alternate')) else: self.splash_support.state(('!selected', '!alternate')) def _init_widget(self): frame_widget = Frame(self) self.notebook.add(frame_widget, text=_('Widget')) # --- font frame_font = Frame(frame_widget) self.title_font = FontFrame(frame_font, CONFIG.get("Widget", "font_title"), True) self.text_font = FontFrame(frame_font, CONFIG.get("Widget", "font")) frame_font.columnconfigure(1, weight=1) Label(frame_font, text=_('Title')).grid(row=0, column=0, sticky='nw', padx=4, pady=4) self.title_font.grid(row=0, column=1) Separator(frame_font, orient='horizontal').grid(row=1, columnspan=2, sticky='ew', padx=4, pady=4) Label(frame_font, text=_('Text')).grid(row=2, column=0, sticky='nw', padx=4, pady=4) self.text_font.grid(row=2, column=1) # --- opacity self.opacity_frame = OpacityFrame(frame_widget, CONFIG.get("Widget", "alpha")) # --- colors frame_color = Frame(frame_widget) frame_color.columnconfigure(1, weight=1) frame_color.columnconfigure(3, weight=1) self.color_bg = ColorFrame(frame_color, CONFIG.get("Widget", "background"), _('Background color')) self.color_fg = ColorFrame(frame_color, CONFIG.get("Widget", "foreground"), _('Foreground color')) self.color_feed_bg = ColorFrame( frame_color, CONFIG.get("Widget", "feed_background"), _('Background color')) self.color_feed_fg = ColorFrame( frame_color, CONFIG.get("Widget", "feed_foreground"), _('Foreground color')) self.color_link = ColorFrame(frame_color, CONFIG.get("Widget", "link_color"), _('Link color')) Label(frame_color, text=_('General')).grid(row=0, column=0, sticky='w', padx=4, pady=2) self.color_bg.grid(row=0, column=1, sticky='e', padx=4, pady=2) self.color_fg.grid(row=1, column=1, sticky='e', padx=4, pady=2) Separator(frame_color, orient='horizontal').grid(row=2, columnspan=4, sticky='ew', padx=4, pady=4) Label(frame_color, text=_('Feed entry')).grid(row=3, column=0, sticky='w', padx=4, pady=2) self.color_feed_bg.grid(row=3, column=1, sticky='e', padx=4, pady=2) self.color_feed_fg.grid(row=4, column=1, sticky='e', padx=4, pady=2) self.color_link.grid(row=5, column=1, sticky='e', padx=4, pady=2) # --- pack Label(frame_widget, text=_('Font'), font='TkDefaultFont 9 bold', anchor='w').pack(padx=4, fill='x') frame_font.pack(fill='x', padx=14) Separator(frame_widget, orient='horizontal').pack(fill='x', pady=6) self.opacity_frame.pack(padx=(4, 10), fill='x') Separator(frame_widget, orient='horizontal').pack(fill='x', pady=6) Label(frame_widget, text=_('Colors'), font='TkDefaultFont 9 bold', anchor='w').pack(padx=4, fill='x') frame_color.pack(fill='x', padx=14) def display_label(self, value): self.opacity_label.configure(text=" {val} %".format( val=int(float(value)))) def translate(self): showinfo( "Information", _("The language setting will take effect after restarting the application" ), parent=self) @staticmethod def _config_size(variable, font): size = variable.get() if size: font.configure(size=size) @staticmethod def _validate_entry_nb(P): """ Allow only to enter numbers""" parts = P.split(".") b = len(parts) < 3 and P != "." for p in parts: b = b and (p == "" or p.isdigit()) return b def change_gui(self): showinfo( "Information", _("The GUI Toolkit setting will take effect after restarting the application" ), parent=self) def ok(self): # --- general CONFIG.set("General", "language", REV_LANGUAGES[self.lang.get()]) CONFIG.set("General", "trayicon", self.gui.get().lower()) CONFIG.set("General", "update_delay", "%i" % (int(self.entry_delay.get()) * 60000)) CONFIG.set("General", "img_timeout", "%i" % (int(self.entry_timeout.get()))) CONFIG.set('General', 'confirm_feed_remove', str(self.confirm_feed_rem.instate(('selected', )))) CONFIG.set('General', 'confirm_cat_remove', str(self.confirm_cat_rem.instate(('selected', )))) CONFIG.set('General', 'check_update', str(self.confirm_update.instate(('selected', )))) CONFIG.set('General', 'splash_supported', str(not self.splash_support.instate(('selected', )))) CONFIG.set('General', 'notifications', str(self.notifications.instate(('selected', )))) # --- widget CONFIG.set("Widget", "alpha", "%i" % self.opacity_frame.get_opacity()) font_title_dic = self.title_font.get_font() font_title_dic[ 'underline'] = 'underline' if font_title_dic['underline'] else '' font_title_dic['family'] = font_title_dic['family'].replace(' ', '\ ') CONFIG.set( "Widget", "font_title", "{family} {size} {weight} {slant} {underline}".format( **font_title_dic)) font_text_dic = self.text_font.get_font() font_text_dic['family'] = font_text_dic['family'].replace(' ', '\ ') CONFIG.set("Widget", "font", "{family} {size}".format(**font_text_dic)) CONFIG.set("Widget", "foreground", self.color_fg.get_color()) CONFIG.set("Widget", "background", self.color_bg.get_color()) CONFIG.set("Widget", "feed_foreground", self.color_feed_fg.get_color()) CONFIG.set("Widget", "feed_background", self.color_feed_bg.get_color()) CONFIG.set("Widget", "link_color", self.color_link.get_color()) self.destroy()
class StringEntry: """String class for entry added colour, change state Parameters ---------- parent : str parent handle lf_text : str text on LabelFrame mess_text : str message def_text : str default text colour : str frame colour mod : str enable or disable state switch Returns ------- string """ def __init__(self, parent, lf_text, mess_text, def_text="", colour='brown', mod=False): self.parent = parent self.lf_text = lf_text self.mess_text = mess_text self.mod = mod self.out_var = StringVar() self.out_var.set(def_text) self.farbe = farbe = { 'blue': 'light blue', 'brown': '#EDEF77', 'green': 'light green', 'pink': '#EAAFBF' } colour = colour if colour in farbe else 'brown' self.colour = colour st1 = Style() st1.theme_use('default') st1.configure(colour + '.TLabelframe', background='#C9B99B') st1.configure(colour + '.TLabelframe.Label', background=farbe[colour]) st1.configure(colour + '.TCheckbutton', background=farbe[colour]) st1.configure('brown.TLabel', background='#EDEF77') self.construct() def construct(self): """construct of LabelFrame and message Parameters ---------- None Returns ------- None """ self.lf1 = Labelframe(self.parent, text=self.lf_text, style=self.colour + '.TLabelframe') self.lf1.grid(column=0, row=0, padx=10, pady=10) self.messlbl = Label(self.lf1, text=self.mess_text, style='brown.TLabel') self.messlbl.grid(row=2, column=0, pady=10, padx=10) self.make_entry() def make_entry(self): """construct of Entry Parameters ---------- None Returns ------- None """ vcmd = self.lf1.register(self.is_okay) self.ent1 = ent1 = Entry(self.lf1, validate='key', validatecommand=(vcmd, '%P', '%S', '%i'), textvariable=self.out_var) ent1.bind("<Return>", self.end_input) ent1.grid(row=1, column=0, padx=10) ent1.focus() if self.mod in (True, False): self.modify() def modify(self): """construct of state switch Parameters ---------- None Returns ------- None """ lf_text = self.lf_text # entry disabled until checkbox is ticked self.cb_opt = Checkbutton(self.lf1, command=self.toggle_opt, style=self.colour + '.TCheckbutton') self.lf1['labelwidget'] = self.cb_opt if self.mod: self.ent1.state(['!disabled']) self.cb_opt.state(['!selected']) self.cb_opt['text'] = lf_text + ' Check to prevent editing ' self.ent1.focus() else: self.ent1.state(['disabled']) self.cb_opt.state(['selected']) self.cb_opt['text'] = lf_text + ' Check to modify ' def toggle_opt(self): """state switch logic Parameters ---------- None Returns ------- None """ lf_text = self.lf_text # state of entry controlled # by the state of the check button in Option frame label widget if self.cb_opt.instate(['selected']): print('selected state') self.ent1.state(['disabled']) self.cb_opt['text'] = lf_text + ' Check to modify ' else: print('unselected state') self.ent1.state(['!disabled']) # enable option self.cb_opt['text'] = lf_text + ' Check to prevent editing ' self.ent1.focus() def end_input(self, _evt): """limit on string Parameters ---------- evt : str bind handle Returns ------- None """ if len(self.out_var.get()) > 5: self.messlbl['text'] = "That's OK" else: self.messlbl['text'] = "Should be at least 6 characters long" def is_okay(self, text, inp, ind): """ validation function Parameters ---------- text : str text if allowed inp : str current input Returns ------- boolean """ ind = int(ind) print(ind) if (inp.isalnum() or inp in (",", ".", "'", " ")) and ind > 0: return True else: return bool((text.isupper() or text == "") and ind == 0)
class Root(Frame): ''' The root window ''' def __init__(self,parent,csvpath="",rosterpath=""): ''' Initilization of the window, assigning height centering the window, and starting the interface. ''' self.queue = Queue() self.parent = parent self.interface = GuiInterface() self.loadWindow = None self.remember = False self.initialized = False self.csvpathh = csvpath self.rosterpathh = rosterpath self.outpathh = "" self.teamsizeh = "" self.startMainUI() def centerWindow(self,notself=None): ''' This centers the window into place if notself is set, then it centers the notself window @param: notself - TKobject ''' if notself != None: #notself is primarly for progressbar sw = self.parent.winfo_screenwidth() sh = self.parent.winfo_screenheight() x = (sw - self.w/2) / 2 y = (sh - self.h/2) / 2 notself.geometry('%dx%d+%d+%d' % (self.w/1.8,self.h/1.8, x,y)) else: sw = self.parent.winfo_screenwidth() sh = self.parent.winfo_screenheight() x = (sw - self.w) / 2 y = (sh - self.h) / 2 self.parent.geometry('%dx%d+%d+%d' % (self.w,self.h, x ,y)) def startWindow(self): ''' This method starts/creates the window for the UI ''' Frame.__init__(self, self.parent, background="white") self.style = Style() self.style.theme_use("default") self.pack(fill=BOTH, expand=1) if(not self.initialized): self.centerWindow() else: self.parent.geometry('%dx%d' % (self.w,self.h)) self.initialized = True def resetWindow(self): ''' Resets the window ''' if(self.initialized): self.destroy() if(self.loadWindow != None): self.loadWindow.destroy() self.startWindow() def startMainUI(self): ''' Starting the main UI takes some work, this creates the buttons labels and entrys. Also puts them into place, and adds function calls to the buttons ''' #RESETING WINDOW self.h = 290 self.w = 600 self.resetWindow() self.parent.title("Input") #CREATING CSV FRAME csvFrame = Frame(self) csvFrame.pack(fill=X, side=TOP) csvLabel = Label(csvFrame, text="Path to csv:", background="white") csvLabel.pack(side=LEFT, padx=15, pady=10) self.csvEntry = Entry(csvFrame, width=30) self.csvEntry.insert(0,self.csvpathh) self.csvEntry.pack(side=LEFT, padx=35, pady=10) csvButton = Button(csvFrame, command=self.csvstartfilebrowser, text="Browse...") csvButton.pack(side=LEFT, padx=10, pady=10) #DONE CSV FRAME #CREATING ROSTER FRAME rosterFrame = Frame(self) rosterFrame.pack(fill=X, side=TOP) rosterLabel = Label(rosterFrame, text="Path to roster:", background="white") rosterLabel.pack(side=LEFT, padx=17, pady=10) self.rosterEntry = Entry(rosterFrame, width=30) self.rosterEntry.insert(0,self.rosterpathh) self.rosterEntry.pack(side=LEFT, padx=15, pady=10) rosterButton = Button(rosterFrame, command=self.rosterstartfilebrowser, text="Browse...") rosterButton.pack(side=LEFT, padx=28, pady=10) #DONE ROSTER FRAME #CREATING OUTPUT FRAME outputFrame = Frame(self) outputFrame.pack(fill=X, side=TOP) outputLabel = Label(outputFrame, text="Path to output:", background="white") outputLabel.pack(side=LEFT, padx=15, pady=10) self.outputEntry = Entry(outputFrame, width=30) self.outputEntry.insert(0,self.outpathh) self.outputEntry.pack(side=LEFT, padx=15, pady=10) outputButton = Button(outputFrame, command=self.outputstartfilebrowser, text="Browse...") outputButton.pack(side=LEFT, padx=28, pady=10) #DONE OUTPUT FRAME #CREATING TEAMSIZE FRAME teamsizeFrame= Frame(self) teamsizeFrame.pack(fill=X, side=TOP) teamsizeLabel = Label(teamsizeFrame, text="Team size:", background="white") teamsizeLabel.pack(side=LEFT, padx=15, pady=10) self.teamsizeEntry = Entry(teamsizeFrame, width=5) self.teamsizeEntry.insert(0,self.teamsizeh) self.teamsizeEntry.pack(side=LEFT, padx=43, pady=10) #DONE TEAMSIZE FRAME #CREATING BOTTOM BUTTONS frame = Frame(self, borderwidth=1) frame.pack(fill=BOTH, expand=True) self.pack(fill=BOTH, expand=True) exitButton = Button(self,text="Exit",command=self.parent.destroy) exitButton.pack(side=RIGHT, padx=5, pady=5) self.submitButton = Button(self,text="Submit",command=self.submitFiles) self.submitButton.pack(side=RIGHT) #DONE BOTTOM BUTTONS def optionUI(self): ''' This creates the option window which presents the user with the generated teams and their options ''' #RESETING WINDOW self.h = 400 self.w = 800 self.resetWindow() self.parent.title("Options") #CREATING SCROLL AREA scrollFrame = Frame(self) scrollFrame.pack(fill=X, side=TOP) self.teamlisting = Listbox(scrollFrame, width=self.w, height=18, \ selectmode=MULTIPLE) count = 1 for team in self.interface.teams: teamstring = "Team: " + str(count) teamstring += " score: " + "%.4f " % team.rating teamstring += " members: " for student in team.members: teamstring += student.name + " | " count += 1 self.teamlisting.insert(END, teamstring) self.teamlisting.pack(padx=5, pady=5) #DONE SCROLL AREA #This will enable double-clicking self.teamlisting.bind('<Double-1>', lambda x: self.inspectTeamUI(self.teamlisting.curselection())) #CREATING BOTTOM BUTTONS frame = Frame(self, borderwidth=1) frame.pack(fill=BOTH, expand=True) self.pack(fill=BOTH, expand=True) backButton = Button(self,text="Back",command=self.startMainUI) backButton.pack(side=LEFT, padx=5, pady=5) exitButton = Button(self,text="Exit",command=lambda: self.parent.destroy() and exit()) exitButton.pack(side=RIGHT, padx=5, pady=5) saveButton = Button(self,text="Save",command=self.interface.writeFile) saveButton.pack(side=RIGHT) rerunButton = Button(self,text="Rerun",command=self.reRun) rerunButton.pack(side=RIGHT, padx=5, pady=5) shuffleTeamsButton = Button(self,text="Shuffle Selected",command=self.shuffleSelected) shuffleTeamsButton.pack(side=RIGHT) swappingMembersButton = Button(self,text="Swap Members",command=self.memberSwap) swappingMembersButton.pack(side=RIGHT,padx=5, pady=5) emailscreenButton = Button(self,text="Email Team(s)",command=self.emailScreen) emailscreenButton.pack(side=RIGHT) #DONE BOTTOM BUTTONS def inspectTeamUI(self,selection): ''' This page will allow the user to see info on the team that was double clicked. ''' #RESETING WINDOW self.h = 400 self.w = 800 self.resetWindow() self.parent.title("About This Team") #CREATING SCROLL AREA scrollFrame = Frame(self) scrollFrame.pack(fill=X, side=TOP) self.inspectedTeamStudentListing = Listbox(scrollFrame, width=self.w, height=9) self.inspectedTeamScheduleListing = Listbox(scrollFrame, width=self.w, height=9) if selection: inspectedTeamIndex = selection[0] inspectedTeam = self.interface.teams[inspectedTeamIndex] for student in inspectedTeam.members: studentstring = "Name: " + student.name studentstring += " |Languages: " + str(student.filters.get("Languages")[0]).strip('[]') studentstring += " |Pref. Teammates: " + str(student.filters.get("Teammates")[0]).strip('[]') self.inspectedTeamStudentListing.insert(END, studentstring) studentstring = "Schedule for " + student.name + " = " for time_and_day in student.filters.get("Schedule")[0]: studentstring += str(time_and_day.name) + " " + str(time_and_day.times) + " | " self.inspectedTeamScheduleListing.insert(END, studentstring) else: self.inspectedTeamStudentListing.insert(END, "Please try again") self.inspectedTeamStudentListing.pack(padx=5, pady=5) self.inspectedTeamScheduleListing.pack(padx=5, pady=5) #DONE SCROLL AREA #CREATING BOTTOM BUTTONS frame = Frame(self, borderwidth=1) frame.pack(fill=BOTH, expand=True) self.pack(fill=BOTH, expand=True) closeButton = Button(self,text="Close",command=self.optionUI) closeButton.pack(side=RIGHT,padx=5, pady=5) #DONE BOTTOM BUTTONS def memberSwapUI(self,indexes): ''' This creates the window which allows the user to swap individual members and reweigh teams. @param: indexes = int[] ''' #RESETING WINDOW self.h = 400 self.w = 800 self.resetWindow() self.parent.title("Swapping Members") #CREATING SCROLL AREA scrollFrame = Frame(self) scrollFrame.pack(fill=X, side=TOP) self.teamlisting1 = Listbox(scrollFrame, width=self.w, height=9) self.teamlisting2 = Listbox(scrollFrame, width=self.w, height=9) count = 1 team = self.interface.teams[indexes[0]] for student in team.members: teamstring = "" teamstring += student.name self.teamlisting1.insert(END, teamstring) count += 1 team = self.interface.teams[indexes[1]] for student in team.members: teamstring = "" teamstring += student.name self.teamlisting2.insert(END, teamstring) count += 1 self.teamlisting1.pack(padx=5, pady=5) self.teamlisting2.pack(padx=5, pady=5) #DONE SCROLL AREA #CREATING BOTTOM BUTTONS frame = Frame(self, borderwidth=1) frame.pack(fill=BOTH, expand=True) self.pack(fill=BOTH, expand=True) backButton = Button(self,text="Back",command=lambda: self.swapSizeCheck(indexes)) backButton.pack(side=LEFT, padx=5, pady=5) exitButton = Button(self,text="Exit",command=self.parent.destroy) exitButton.pack(side=RIGHT, padx=5, pady=5) swapButton = Button(self,text="Swap Team",command= lambda: self.switchTeams(indexes)) swapButton.pack(side=RIGHT,padx=5, pady=5) #DONE BOTTOM BUTTONS def emailScreen(self): ''' This starts the email login screen ''' if(len(self.teamlisting.curselection()) < 1): messagebox.showinfo("Error","Please select one or more teams") return if(self.remember): self.emailTeams() return self.emailWindow = Toplevel(self.parent) self.centerWindow(self.emailWindow) #CREATING EMAIL FRAME emailFrame = Frame(self.emailWindow) emailFrame.pack(fill=X, side=TOP) emailLabel = Label(emailFrame, text="Email address:", background="white") emailLabel.pack(side=LEFT, padx=15, pady=10) self.emailEntry = Entry(emailFrame, width=20) self.emailEntry.insert(0,"") self.emailEntry.pack(side=LEFT, padx=43, pady=10) #EMAIL FRAME DONE #CREATING PASSWORD FRAME passwordFrame = Frame(self.emailWindow) passwordFrame.pack(fill=X, side=TOP) passwordLabel = Label(passwordFrame, text="Password:"******"white") passwordLabel.pack(side=LEFT, padx=17, pady=10) self.passwordEntry = Entry(passwordFrame, width=20, show="*") self.passwordEntry.insert(0,"") self.passwordEntry.pack(side=LEFT, padx=65, pady=10) #PASSWORD FRAME DONE #CREATING REMEMBER FRAME rememberFrame = Frame(self.emailWindow) rememberFrame.pack(fill=X, side=TOP) rememberLabel = Label(rememberFrame, text="Remember Username/Password", background="white") rememberLabel.pack(side=LEFT, padx=15, pady=10) self.rememberCheck = Checkbutton(rememberFrame) self.rememberCheck.pack(side=LEFT, padx=15, pady=10) #REMEMBER FRAME DONE #CREATING BOTTOM BUTTONS frame = Frame(self.emailWindow, borderwidth=1) frame.pack(fill=BOTH, expand=True) exitButton = Button(self.emailWindow,text="Cancel",command=self.emailWindow.destroy) exitButton.pack(side=RIGHT, padx=5, pady=5) submitButton = Button(self.emailWindow,text="Submit",command=self.emailTeams) submitButton.pack(side=RIGHT,padx=5, pady=5) #DONE BOTTOM BUTTONS def emailTeams(self): ''' This invokes emailing the selected teams ''' success = True if(not self.remember): selection = self.teamlisting.curselection() email = self.emailEntry.get() password = self.passwordEntry.get() if(email == "" or password == ""): messagebox.showinfo("Error","Cannot leave fields empty") return if(len(self.rememberCheck.state()) != 0 and self.rememberCheck.state()[0] == "selected"): self.remember = True success = self.interface.sendEmail(selection,email,password,True) else: success = self.interface.sendEmail(selection,email,password) else: success = self.interface.sendEmail(self.teamlisting.curselection()) if not success: self.remember = False messagebox.showinfo("Error","Sending the email was unsuccessful, check your email and password") return if success: messagebox.showinfo("Success","Email was sent successfully") self.emailWindow.destroy() return def loadingScreen(self): ''' This starts the loading screen and disables all buttons ''' for i in self.winfo_children(): if Button == type(i): i.configure(state=DISABLED) self.loadWindow = Toplevel(self.parent) loadingstring = "Please wait while we run the algorithm" loadinglabel = Label(self.loadWindow, text=loadingstring, background="white") progressbar = Progressbar(self.loadWindow, orient= "horizontal", \ length=300, mode="indeterminate") progressbar.pack(pady=self.h/10) loadinglabel.pack() self.centerWindow(self.loadWindow) self.loadWindow.title("Wait") progressbar.start() def memberSwap(self): ''' This will setup the call for memberSwapUI and check for improper/missing selections ''' indexes = [] selection = self.teamlisting.curselection() for i in selection: indexes.append(i) if len(indexes) == 2: self.memberSwapUI(indexes); else: messagebox.showinfo("Error","Please select 2 teams") def switchTeams(self, indexes): ''' Puts selected members into the other team in the memberSwapUI @param: indexes = int[] ''' student1 = self.teamlisting1.curselection() student2 = self.teamlisting2.curselection() if student1: if len(self.interface.teams[indexes[1]].members) < self.interface.teams[indexes[1]].maxsize: student = self.interface.teams[indexes[0]].members[int(student1[0])] newTeam = self.interface.teams[indexes[1]] oldTeam = self.interface.teams[indexes[0]] newTeam.insertStudent(student) oldTeam.remStudent(student) self.memberSwapUI(indexes) else: messagebox.showinfo("Max Capacity", "Group is at maximum capacity") if student2: if len(self.interface.teams[indexes[0]].members) < self.interface.teams[indexes[0]].maxsize: student = self.interface.teams[indexes[1]].members[int(student2[0])] newTeam = self.interface.teams[indexes[0]] oldTeam = self.interface.teams[indexes[1]] newTeam.insertStudent(student) oldTeam.remStudent(student) self.memberSwapUI(indexes) else: messagebox.showinfo("Max Capacity", "Group is at maximum capacity") def swapSizeCheck(self,indexes): ''' This is a check to make sure before you back up from the memberSwapUI that the sizes are still correct @param: indexes = int[] ''' if len(self.interface.teams[indexes[0]].members) < self.interface.teams[indexes[0]].maxsize \ or len(self.interface.teams[indexes[1]].members) < self.interface.teams[indexes[1]].maxsize: if messagebox.askokcancel("WARNING", "Warning: A group is shorthanded. You sure you want to proceed?"): for index in indexes: self.interface.algorithm.weightCalc(self.interface.teams[index]) self.optionUI(); else: for index in indexes: self.interface.algorithm.weightCalc(self.interface.teams[index]) self.optionUI(); def shuffleSelected(self): ''' This is a wrapper function that shuffles the selected teams ''' #Gets selected values indexes = [] selection = self.teamlisting.curselection() for i in selection: indexes.append(i) self.interface.reShuffleSelectedTeams(indexes) self.optionUI() def reRun(self): ''' A wrapper function to rerun the algorithm ''' thread = ThreadedTask(self.queue,\ self.interface.reShuffleAll) thread.start() ThreadedTask(self.queue,self.loadingScreen).start() self.checkThread(thread,self.optionUI) def submitFiles(self): ''' Checks the validity of the entry feilds for After checks it runs our python script. ''' csvtext = self.csvEntry.get() teamsize = self.teamsizeEntry.get() rostertext = self.rosterEntry.get() outputtext = self.outputEntry.get() #Checking existance of paths and extensions if(not os.path.exists(csvtext) and csvtext[-4:] != ".csv"): messagebox.showinfo("Error","Not a CSV or the file does not exist") return #Checking existance of paths and extensions if(not os.path.exists(rostertext) and rostertext[-4:] != ".txt"): messagebox.showinfo("Error","Not a roster or the file does not exist") return #Checking existance of path if(not os.path.exists(outputtext)): messagebox.showinfo("Error","Directory dosen't exists for output") return #Checking if the string is an int and in range if(not self.testNumber(teamsize)): messagebox.showinfo("Error","Please enter a positive integer for teamsize(2,5)") return self.csvpathh = csvtext self.rosterpathh = rostertext self.outpathh = outputtext self.teamsizeh = teamsize self.interface.setOutputPath(outputtext) self.submitButton.configure(state=DISABLED) runalgorithm = lambda: self.interface.runGeneral(\ rostertext,csvtext,int(teamsize)) thread1 = ThreadedTask(self.queue,runalgorithm) thread2 = ThreadedTask(self.queue,self.loadingScreen) thread2.start() thread1.start() self.checkThread(thread1,self.optionUI) def checkThread(self,thread,function): ''' This function checks to see if the given thread is dead, if it is not, it recalls a new checkThread. After the thread is dead, it calls the given function @param: thread - ThreadedTask functoin - a function ''' if thread.is_alive(): self.parent.after(1000, lambda: self.checkThread(thread,function)) else: function() def testNumber(self,i,minimum=0,maximum=5): ''' Checks if i is an integer and between a certain range @param: i (string) minimum (optional int) maximum (optional int) ''' try: i = int(i) return (i >= minimum) and (i <= maximum) except: return False def csvstartfilebrowser(self): ''' Starts the filebrowser for the csv file ''' currdir = os.getcwd() fileopt = [('csv files', '*.csv')] directo = filedialog.askopenfilename(parent=self, filetypes=fileopt, \ initialdir=currdir, title="Select file") #clearning and setting csventry self.csvEntry.delete(0,'end') self.csvEntry.insert(0,str(directo)) def rosterstartfilebrowser(self): ''' Starts the filebrowser for the text file ''' currdir = os.getcwd() fileopt = [('text files', '*.txt')] directo = filedialog.askopenfilename(parent=self, filetypes=fileopt, \ initialdir=currdir, title="Select file") #clearning and setting rosterentry self.rosterEntry.delete(0,'end') self.rosterEntry.insert(0,str(directo)) def outputstartfilebrowser(self): ''' Starts the filebrowser for the output ''' currdir = os.getcwd() directo = filedialog.askdirectory(parent=self,\ initialdir=currdir, title="Select file") #clearning and setting outputentry self.outputEntry.delete(0,'end') self.outputEntry.insert(0,str(directo))
class Config(Toplevel): """Config dialog.""" def __init__(self, master): """Create Config dialog.""" Toplevel.__init__(self, master, class_='MyNotes') self.title(_("Preferences")) self.grab_set() self.protocol("WM_DELETE_WINDOW", self.quit) self.changes = {}, {}, False, False self.minsize(width=430, height=450) # --- style style = Style(self) style.theme_use("clam") style.configure("TScale", sliderlength=20) style.map("TCombobox", fieldbackground=[('readonly', 'white')], selectbackground=[('readonly', 'white')], selectforeground=[('readonly', 'black')]) style.configure("prev.TLabel", background="white") style.map("prev.TLabel", background=[("active", "white")]) color = CONFIG.get("Categories", CONFIG.get("General", "default_category")) style.configure("titlebar.TFrame", background=color) style.configure("titlebar.TLabel", background=color) style.configure("text.TFrame", background="white") # --- body self.notebook = Notebook(self) okcancel_frame = Frame(self) okcancel_frame.columnconfigure(0, weight=1) okcancel_frame.columnconfigure(1, weight=1) okcancel_frame.pack(fill="x", side='bottom') self.notebook.pack(expand=True, fill="both") # --- * General settings self._init_general() # --- * Font settings self._init_font() # --- * Categories self.category_settings = CategoryManager(self.notebook, master) self.notebook.add(self.category_settings, text=_("Categories"), sticky="ewsn", padding=4) # --- * Symbols size = CONFIG.get("Font", "text_size") family = CONFIG.get("Font", "text_family") symbols_settings = Frame(self.notebook, padding=4) self.notebook.add(symbols_settings, text=_("Symbols"), sticky="ewsn", padding=4) txt_frame = Frame(symbols_settings, relief="sunken", borderwidth=1, style="text.TFrame") txt_frame.rowconfigure(0, weight=1) txt_frame.columnconfigure(0, weight=1) self.symbols = Text(txt_frame, width=1, height=1, highlightthickness=0, spacing2=5, spacing1=5, relief="flat", padx=4, pady=4, font="%s %s" % (family.replace(" ", "\ "), size)) scroll_y = AutoScrollbar(txt_frame, orient='vertical', command=self.symbols.yview) self.symbols.configure(yscrollcommand=scroll_y.set) self.symbols.insert("1.0", CONFIG.get("General", "symbols")) Label(symbols_settings, text=_("Available symbols")).pack(padx=4, pady=4) txt_frame.pack(fill="both", expand=True, padx=4, pady=4) self.symbols.grid(sticky='ewns') scroll_y.grid(row=0, column=1, sticky='ns') Button(symbols_settings, text=_('Reset'), command=self.reset_symbols).pack(padx=4, pady=4) # --- * AutoCorrect self.autocorrect_settings = AutoCorrectConfig(self.notebook, master) self.notebook.add(self.autocorrect_settings, text=_("AutoCorrect"), sticky="ewsn", padding=4) # --- Ok/Cancel buttons Button(okcancel_frame, text="Ok", command=self.ok).grid(row=1, column=0, padx=4, pady=10, sticky="e") Button(okcancel_frame, text=_("Cancel"), command=self.destroy).grid(row=1, column=1, padx=4, pady=10, sticky="w") def _init_general(self): general_settings = Frame(self.notebook, padding=4) general_settings.columnconfigure(0, weight=1) self.notebook.add(general_settings, text=_("General"), sticky="ewsn", padding=4) # ---- language self.lang = StringVar(self, LANGUAGES[CONFIG.get("General", "language")]) lang_frame = Frame(general_settings) Label(lang_frame, text=_("Language")).grid(row=0, sticky="w", padx=4, pady=4) menu_lang = Menu(lang_frame, tearoff=False) Menubutton(lang_frame, menu=menu_lang, width=9, textvariable=self.lang).grid(row=0, column=1, padx=8, pady=4) for lang in LANGUAGES.values(): menu_lang.add_radiobutton(variable=self.lang, label=lang, value=lang, command=self.translate) # ---- gui toolkit self.gui = StringVar(self, CONFIG.get("General", "trayicon").capitalize()) gui_frame = Frame(general_settings) Label(gui_frame, text=_("GUI Toolkit for the system tray icon")).grid(row=0, column=0, padx=4, pady=4, sticky="w") menu_gui = Menu(gui_frame, tearoff=False) Menubutton(gui_frame, menu=menu_gui, width=9, textvariable=self.gui).grid(row=0, column=1, padx=4, pady=4, sticky="w") for toolkit, b in TOOLKITS.items(): if b: menu_gui.add_radiobutton(label=toolkit.capitalize(), value=toolkit.capitalize(), variable=self.gui, command=self.change_gui) # ---- opacity self.opacity = OpacityFrame(general_settings, CONFIG.getint("General", "opacity")) # ---- position frame_position = Frame(general_settings) self.position = StringVar(self, CONFIG.get("General", "position")) Label(frame_position, text=_("Default position of the notes")).grid(row=0, columnspan=3, sticky="w", padx=4, pady=4) Radiobutton(frame_position, text=_("Always above"), value="above", variable=self.position).grid(row=1, column=0, padx=4) Radiobutton(frame_position, text=_("Always below"), value="below", variable=self.position).grid(row=1, column=1, padx=4) Radiobutton(frame_position, text=_("Normal"), value="normal", variable=self.position).grid(row=1, column=2, padx=4) # ---- titlebar self.titlebar_disposition = StringVar( self, CONFIG.get("General", "buttons_position")) self.title_var = StringVar( self) # to add date if date_in_title is true font_title = "%s %s" % (CONFIG.get("Font", "title_family").replace( " ", "\ "), CONFIG.get("Font", "title_size")) style = CONFIG.get("Font", "title_style").split(",") if style: font_title += " " font_title += " ".join(style) frame_titlebar = Frame(general_settings) frame_titlebar.columnconfigure(1, weight=1) frame_titlebar.columnconfigure(3, weight=1) Label(frame_titlebar, text=_("Title bar disposition")).grid(row=0, columnspan=4, sticky="w", padx=4, pady=4) Radiobutton(frame_titlebar, value="right", variable=self.titlebar_disposition).grid(row=1, column=0, padx=4) right = Frame(frame_titlebar, style="titlebar.TFrame") right.grid(row=1, column=1, sticky="ew", padx=4) def select_right(event): self.titlebar_disposition.set("right") Label(right, textvariable=self.title_var, style="titlebar.TLabel", anchor="center", font=font_title).pack(side="left", fill="x", expand=True) Label(right, image="img_close", style="titlebar.TLabel").pack(side="right") Label(right, image="img_roll", style="titlebar.TLabel").pack(side="right") for ch in right.children.values(): ch.bind("<Button-1>", select_right) Radiobutton(frame_titlebar, value="left", variable=self.titlebar_disposition).grid(row=1, column=2) left = Frame(frame_titlebar, style="titlebar.TFrame") left.grid(row=1, column=3, sticky="ew") def select_left(event): self.titlebar_disposition.set("left") Label(left, image="img_close", style="titlebar.TLabel").pack(side="left") Label(left, image="img_roll", style="titlebar.TLabel").pack(side="left") Label(left, textvariable=self.title_var, style="titlebar.TLabel", anchor="center", font=font_title).pack(side="right", fill="x", expand=True) for ch in left.children.values(): ch.bind("<Button-1>", select_left) self.date_in_title = BooleanVar( self, CONFIG.getboolean('General', 'date_in_title', fallback=True)) date_in_title = Checkbutton(frame_titlebar, variable=self.date_in_title, text=_('Display creation date in title'), command=self.toggle_date) date_in_title.grid(row=2, columnspan=4, sticky='w', pady=4, padx=4) self.toggle_date() # ---- placement lang_frame.grid(sticky="w") Separator(general_settings, orient="horizontal").grid(sticky="ew", pady=10) gui_frame.grid(sticky="w") Separator(general_settings, orient="horizontal").grid(sticky="ew", pady=10) # opacity_frame.grid(sticky='w') self.opacity.grid(sticky='w', padx=4) Separator(general_settings, orient="horizontal").grid(sticky="ew", pady=10) frame_position.grid(sticky="ew") Separator(general_settings, orient="horizontal").grid(sticky="ew", pady=10) frame_titlebar.grid(sticky="ew", pady=4) # ---- clean local data Separator(general_settings, orient="horizontal").grid(sticky="ew", pady=10) Button(general_settings, text=_('Delete unused local data'), command=self.cleanup).grid(padx=4, pady=4, sticky='w') # ---- splash supported Separator(general_settings, orient="horizontal").grid(sticky="ew", pady=10) self.splash_support = Checkbutton( general_settings, text=_("Check this box if the notes disappear when you click")) self.splash_support.grid(padx=4, pady=4, sticky='w') if not CONFIG.getboolean('General', 'splash_supported', fallback=True): self.splash_support.state(('selected', '!alternate')) else: self.splash_support.state(('!selected', '!alternate')) def _init_font(self): font_settings = Frame(self.notebook, padding=4) font_settings.columnconfigure(1, weight=1) self.notebook.add(font_settings, text=_("Font"), sticky="ewsn", padding=4) # ---- title title_size = CONFIG.get("Font", "title_size") title_family = CONFIG.get("Font", "title_family").replace(" ", "\ ") font_title = '{} {}'.format(title_family, title_size) style = CONFIG.get("Font", "title_style").split(",") if style: font_title = font_title + " " + " ".join(style) self.title_font = FontFrame(font_settings, font_title, style=True) # ---- text size = CONFIG.get("Font", "text_size") family = CONFIG.get("Font", "text_family").replace(" ", "\ ") self.text_font = FontFrame(font_settings, '{} {}'.format(family, size)) # ---- mono mono_fonts = [f for f in set(font.families()) if 'Mono' in f] mono_family = CONFIG.get("Font", "mono").replace(" ", "\ ") self.mono_font = FontFrame(font_settings, '{} {}'.format(mono_family, size), size=False, font_list=mono_fonts) add_trace( self.text_font.font_size, 'write', lambda *args: self.mono_font._config_size( self.text_font.font_size, self.mono_font.font)) # ---- placement Label(font_settings, text=_("Title")).grid(row=0, column=0, padx=4, pady=4, sticky="nw") self.title_font.grid(row=0, column=1, sticky="w", padx=20) Separator(font_settings, orient="horizontal").grid(row=1, columnspan=2, sticky="ew", pady=10) Label(font_settings, text=_("Text")).grid(row=2, column=0, padx=4, pady=4, sticky="nw") self.text_font.grid(row=2, column=1, sticky="w", padx=20) Separator(font_settings, orient="horizontal").grid(row=3, columnspan=2, sticky="ew", pady=10) Label(font_settings, text=_("Mono")).grid(row=4, column=0, padx=4, pady=4, sticky="nw") self.mono_font.grid(row=4, column=1, sticky="w", padx=20) def reset_symbols(self): self.symbols.delete('1.0', 'end') self.symbols.insert('1.0', SYMBOLS) def toggle_date(self): if self.date_in_title.get(): self.title_var.set('{} - {}'.format(_('Title'), strftime('%x'))) else: self.title_var.set(_('Title')) def cleanup(self): """Remove unused local data and latex images.""" self.master.cleanup() showinfo(_('Information'), _('Unused local data have been cleaned up.')) def ok(self): """Validate configuration.""" # --- splash supported splash_supp = not self.splash_support.instate(('selected', )) splash_change = splash_supp != CONFIG.getboolean( "General", "splash_supported", fallback=True) # --- font mono_font = self.mono_font.get_font()['family'] text_font = self.text_font.get_font() title_font = self.title_font.get_font() style = "{weight},{slant}".format(**title_font) style = style + ',underline' * title_font['underline'] # --- language language = REV_LANGUAGES[self.lang.get()] # --- symbols symbols = [ l.strip() for l in self.symbols.get("1.0", "end").splitlines() ] # --- autocorrect self.autocorrect_settings.ok() autocorrect = "\t".join( ["%s %s" % (key, val) for key, val in AUTOCORRECT.items()]) # --- update CONFIG CONFIG.set("General", "default_category", self.category_settings.default_category.get().lower()) CONFIG.set("General", "language", language) CONFIG.set("General", "opacity", str(self.opacity.get())) CONFIG.set("General", "position", self.position.get()) CONFIG.set("General", "buttons_position", self.titlebar_disposition.get()) CONFIG.set("General", "date_in_title", str(self.date_in_title.get())) CONFIG.set("General", "symbols", "".join(symbols)) CONFIG.set("General", "trayicon", self.gui.get().lower()) CONFIG.set("General", "autocorrect", autocorrect) CONFIG.set('General', 'splash_supported', str(splash_supp)) CONFIG.set("Font", "text_size", str(text_font['size'])) CONFIG.set("Font", "text_family", text_font['family']) CONFIG.set("Font", "title_family", title_font['family']) CONFIG.set("Font", "title_size", str(title_font['size'])) CONFIG.set("Font", "title_style", style) CONFIG.set("Font", "mono", mono_font) # --- notes config col_changes = {} name_changes = {} new_cat = False for cat in self.category_settings.categories: new_name = self.category_settings.get_name(cat) if cat in CONFIG.options("Categories"): old_color = CONFIG.get("Categories", cat) new_color = COLORS[self.category_settings.get_color(cat)] if new_name != cat: name_changes[cat] = new_name CONFIG.remove_option("Categories", cat) CONFIG.set("Categories", new_name, new_color) if old_color != new_color: col_changes[new_name] = (old_color, new_color) CONFIG.set("Categories", new_name, new_color) else: new_cat = True CONFIG.set("Categories", new_name, COLORS[self.category_settings.get_color(cat)]) save_config() self.changes = col_changes, name_changes, new_cat, splash_change self.destroy() def get_changes(self): return self.changes def translate(self): """Show information dialog about language change.""" showinfo( _("Information"), _("The language setting will take effect after restarting the application" ), parent=self) def change_gui(self): """Show information dialog about gui toolkit change.""" showinfo( "Information", _("The GUI Toolkit setting will take effect after restarting the application" ), parent=self) def display_label(self, value): self.opacity_label.configure(text=" {val} %".format( val=int(float(value)))) def quit(self): self.destroy()
background='black', foreground='white') ram.grid(column=1, row=1) disk.grid(column=2, row=1) p = Progressbar(root, orient="vertical", variable=battery) # Add the label to the progressbar style p_label = Label(background='black', foreground='white', text='') p_label.grid(column=3, row=2) p.grid(column=3, row=1) clock_hours.grid(column=4, row=1) top = Checkbutton(root, text='Keep on top') top.grid(column=1, row=2) top.state(['!alternate']) clock_minutes.grid(column=6, row=1) clock_colon.grid(column=5, row=1) root.config(bg='black') root.resizable(False, False) while True: try: if ram_display: ram.set_value(round(bytes_to_gb(virtual_memory().used), 1)) else: ram.set_value(int(virtual_memory().percent)) if hasattr(sensors_battery(), 'percent'): battery.set(int(sensors_battery().percent)) if sensors_battery().power_plugged:
class Export(Toplevel): """Category export dialog.""" def __init__(self, master, note_data): """Create export dialog.""" Toplevel.__init__(self, master, class_='MyNotes') self.title(_("Export")) self.minsize(350, 250) self.grab_set() self.columnconfigure(0, weight=1) self.rowconfigure(3, weight=1) self.note_data = note_data self.categories = CONFIG.options("Categories") self.categories.sort() self.notes_to_export = [] self.export_type = None self.export_data = False # export type self.type = StringVar(self, _("Notes (.notes)")) type_frame = Frame(self) menu_type = Menu(self, tearoff=False) for etype in EXT_DICT: menu_type.add_radiobutton(label=etype, value=etype, variable=self.type) mb = Menubutton(type_frame, menu=menu_type, textvariable=self.type, width=max([int(len(key) * 0.8) for key in EXT_DICT])) Label(type_frame, text=_('Export to')).pack(side='left', padx=4) mb.pack(side='left', padx=4) type_frame.grid(row=0, columnspan=2, sticky='w', pady=4) Separator(self).grid(columnspan=2, sticky="ew", padx=4, pady=4) # export only visible notes checkbutton self.ch_only_visible = Checkbutton(self, text=_("Only visible notes"), command=self.select_only_visible) self.ch_only_visible.grid(columnspan=2, sticky="w", padx=4, pady=4) # note selection self.tree = CheckboxTreeview(self, show='tree') self.tree.grid(row=3, sticky="nsew", padx=4, pady=4) scroll = Scrollbar(self, orient='vertical', command=self.tree.yview) self.tree.configure(yscrollcommand=scroll.set) scroll.grid(row=3, column=1, sticky='ns') self.tree.insert('', 'end', 'root', text=_('Categories')) for cat in self.categories: self.tree.insert('root', 'end', cat, text=cat.capitalize()) for key, data in self.note_data.items(): self.tree.insert(data['category'], 'end', key, text='{} - {}'.format(data['title'], data.get('date', '??')), tags=['visible'] if data['visible'] else []) for cat in self.categories: if not self.tree.get_children(cat): self.tree.detach(cat) self.tree.bind('<<Checked>>', self.toggle_select_visible) self.tree.bind('<<Unchecked>>', self.toggle_select_visible) Separator(self).grid(sticky="ew", columnspan=2, padx=4, pady=4) self.ch_export_data = Checkbutton(self, text=_('Export data (pictures and linked files)')) self.ch_export_data.grid(sticky="w", columnspan=2, padx=4, pady=4) frame = Frame(self) frame.grid(columnspan=2) Button(frame, text="Ok", command=self.ok).grid(row=0, column=0, sticky="w", padx=4, pady=4) Button(frame, text=_("Cancel"), command=self.destroy).grid(row=0, column=1, sticky="e", padx=4, pady=4) self.tree.check_item('root') self.tree.expand_all() self.toggle_select_visible() def ok(self): """Validate choice.""" self.notes_to_export = self.tree.get_checked() self.export_type = self.type.get() self.export_data = "selected" in self.ch_export_data.state() self.destroy() def select_only_visible(self): """Select only visible notes.""" for cat in self.categories: for item in self.tree.get_children(cat): if self.tree.tag_has('visible', item): self.tree.check_item(item) else: self.tree.uncheck_item(item) def toggle_select_visible(self, event=None): """Change select all checkbutton state when another checkbutton is clicked.""" checked = list(self.tree.get_checked()) checked.sort() visible = list(self.tree.tag_has('visible')) visible.sort() self.ch_only_visible.state(['!' * (visible != checked) + 'selected']) def get_export(self): return self.export_type, self.notes_to_export, self.export_data
class Export(Toplevel): """ Category export dialog """ def __init__(self, master): Toplevel.__init__(self, master) self.title(_("Export")) self.resizable(False, False) self.grab_set() # self.columnconfigure(0, weight=1) self.categories = CONFIG.options("Categories") self.categories.sort() self.categories_to_export = [] self.only_visible = False self.ch_all = Checkbutton(self, text=_("Select all"), command=self.select_all) self.ch_only_visible = Checkbutton(self, text=_("Only visible notes")) self.ch_all.grid(sticky="w", padx=4, pady=4) self.ch_only_visible.grid(sticky="w", padx=4, pady=4) Separator(self).grid(sticky="ew", padx=4, pady=4) self.checkbuttons = [] for cat in self.categories: self.checkbuttons.append( Checkbutton(self, text=cat.capitalize(), command=self.toggle_select_all)) self.checkbuttons[-1].grid(sticky="w", padx=4, pady=4) frame = Frame(self) frame.grid() Button(frame, text="Ok", command=self.ok).grid(row=0, column=0, sticky="w", padx=4, pady=4) Button(frame, text=_("Cancel"), command=self.destroy).grid(row=0, column=1, sticky="e", padx=4, pady=4) self.ch_all.state(("selected", )) self.select_all() def ok(self): for ch, cat in zip(self.checkbuttons, self.categories): if "selected" in ch.state(): self.categories_to_export.append(cat) self.only_visible = "selected" in self.ch_only_visible.state() self.destroy() def select_all(self): if ("selected" in self.ch_all.state()): state = "selected" else: state = "!selected" for ch in self.checkbuttons: ch.state((state, )) def toggle_select_all(self): b = 0 for ch in self.checkbuttons: if "selected" in ch.state(): b += 1 if b == len(self.checkbuttons): self.ch_all.state(("selected", )) else: self.ch_all.state(("!selected", )) def get_export(self): return self.categories_to_export, self.only_visible
class Config(Toplevel): def __init__(self, master): Toplevel.__init__(self, master) self.title(_("Preferences")) self.grab_set() self.resizable(False, False) self.protocol("WM_DELETE_WINDOW", self.quit) self.changes = {}, {} # --- style style = Style(self) style.theme_use("clam") style.configure("TScale", sliderlength=20) style.map("TCombobox", fieldbackground=[('readonly', 'white')], selectbackground=[('readonly', 'white')], selectforeground=[('readonly', 'black')]) style.configure("prev.TLabel", background="white") style.map("prev.TLabel", background=[("active", "white")]) color = CONFIG.get("Categories", CONFIG.get("General", "default_category")) style.configure("titlebar.TFrame", background=color) style.configure("titlebar.TLabel", background=color) style.configure("text.TFrame", background="white") # --- body self.notebook = Notebook(self) okcancel_frame = Frame(self) okcancel_frame.columnconfigure(0, weight=1) okcancel_frame.columnconfigure(1, weight=1) self.notebook.pack(expand=True, fill="both") okcancel_frame.pack(fill="x", expand=True) # --- * General settings general_settings = Frame(self.notebook) general_settings.columnconfigure(0, weight=1) self.notebook.add(general_settings, text=_("General"), sticky="ewsn", padding=4) # --- *-- language lang = {"fr": "Français", "en": "English"} self.lang = StringVar(self, lang[CONFIG.get("General", "language")]) lang_frame = Frame(general_settings) Label(lang_frame, text=_("Language")).grid(row=0, sticky="w", padx=4, pady=4) menu_lang = Menu(lang_frame, tearoff=False) Menubutton(lang_frame, menu=menu_lang, width=9, textvariable=self.lang).grid(row=0, column=1, padx=8, pady=4) menu_lang.add_radiobutton(label="English", value="English", variable=self.lang, command=self.translate) menu_lang.add_radiobutton(label="Français", value="Français", variable=self.lang, command=self.translate) # --- *-- opacity self.opacity_scale = Scale(general_settings, orient="horizontal", length=200, from_=0, to=100, value=CONFIG.get("General", "opacity"), command=self.display_label) self.opacity_label = Label( general_settings, text="{val}%".format(val=self.opacity_scale.get())) # --- *-- position frame_position = Frame(general_settings) self.position = StringVar(self, CONFIG.get("General", "position")) Label(frame_position, text=_("Default position of the notes")).grid(row=0, columnspan=3, sticky="w", padx=4, pady=4) Radiobutton(frame_position, text=_("Always above"), value="above", variable=self.position).grid(row=1, column=0) Radiobutton(frame_position, text=_("Always below"), value="below", variable=self.position).grid(row=1, column=1) Radiobutton(frame_position, text=_("Normal"), value="normal", variable=self.position).grid(row=1, column=2) # --- *-- titlebar self.titlebar_disposition = StringVar( self, CONFIG.get("General", "buttons_position")) font_title = "%s %s" % (CONFIG.get("Font", "title_family").replace( " ", "\ "), CONFIG.get("Font", "title_size")) style = CONFIG.get("Font", "title_style").split(",") if style: font_title += " " font_title += " ".join(style) frame_titlebar = Frame(general_settings) frame_titlebar.columnconfigure(1, weight=1) frame_titlebar.columnconfigure(3, weight=1) Label(frame_titlebar, text=_("Title bar disposition")).grid(row=0, columnspan=4, sticky="w", padx=4, pady=4) Radiobutton(frame_titlebar, value="right", variable=self.titlebar_disposition).grid(row=1, column=0) right = Frame(frame_titlebar, style="titlebar.TFrame") right.grid(row=1, column=1, sticky="ew") def select_right(event): self.titlebar_disposition.set("right") Label(right, text=_("Title"), style="titlebar.TLabel", anchor="center", font=font_title).pack(side="left", fill="x", expand=True) Label(right, image="img_close", style="titlebar.TLabel").pack(side="right") Label(right, image="img_roll", style="titlebar.TLabel").pack(side="right") for ch in right.children.values(): ch.bind("<Button-1>", select_right) Radiobutton(frame_titlebar, value="left", variable=self.titlebar_disposition).grid(row=1, column=2) left = Frame(frame_titlebar, style="titlebar.TFrame") left.grid(row=1, column=3, sticky="ew") def select_left(event): self.titlebar_disposition.set("left") Label(left, image="img_close", style="titlebar.TLabel").pack(side="left") Label(left, image="img_roll", style="titlebar.TLabel").pack(side="left") Label(left, text=_("Title"), style="titlebar.TLabel", anchor="center", font=font_title).pack(side="right", fill="x", expand=True) for ch in left.children.values(): ch.bind("<Button-1>", select_left) # --- *-- placement lang_frame.grid(sticky="w") Separator(general_settings, orient="horizontal").grid(sticky="ew", pady=10) Label(general_settings, text=_("Opacity")).grid(sticky="w", padx=4, pady=4) self.opacity_scale.grid(padx=4, pady=(4, 10)) self.opacity_label.place(in_=self.opacity_scale, relx=1, rely=0.5, anchor="w", bordermode="outside") Separator(general_settings, orient="horizontal").grid(sticky="ew", pady=10) frame_position.grid(sticky="ew") Separator(general_settings, orient="horizontal").grid(sticky="ew", pady=10) frame_titlebar.grid(sticky="ew", pady=4) if LATEX: Separator(general_settings, orient="horizontal").grid(sticky="ew", pady=10) Button(general_settings, text=_('Delete unused LaTex data'), command=self.cleanup).grid(padx=4, pady=4, sticky='w') # --- * Font settings font_settings = Frame(self.notebook) font_settings.columnconfigure(0, weight=1) self.notebook.add(font_settings, text=_("Font"), sticky="ewsn", padding=4) # --- *-- title fonttitle_frame = Frame(font_settings) title_size = CONFIG.get("Font", "title_size") title_family = CONFIG.get("Font", "title_family") self.sampletitle = Label(fonttitle_frame, text=_("Sample text"), anchor="center", style="prev.TLabel", relief="groove") self.sampletitle.grid(row=2, columnspan=2, padx=4, pady=6, ipadx=4, ipady=4, sticky="eswn") self.fonts = list(set(font.families())) self.fonts.append("TkDefaultFont") self.fonts.sort() w = max([len(f) for f in self.fonts]) self.sizes = [ "%i" % i for i in (list(range(6, 17)) + list(range(18, 32, 2))) ] self.fonttitle_family = Combobox(fonttitle_frame, values=self.fonts, width=(w * 2) // 3, exportselection=False, validate="key") self._validate_title_size = self.register( lambda *args: self.validate_font_size(self.fonttitle_size, *args)) self._validate_title_family = self.register( lambda *args: self.validate_font_family(self.fonttitle_family, * args)) self.fonttitle_family.configure( validatecommand=(self._validate_title_family, "%d", "%S", "%i", "%s", "%V")) self.fonttitle_family.current(self.fonts.index(title_family)) self.fonttitle_family.grid(row=0, column=0, padx=4, pady=4) self.fonttitle_size = Combobox( fonttitle_frame, values=self.sizes, width=5, exportselection=False, validate="key", validatecommand=(self._validate_title_size, "%d", "%P", "%V")) self.fonttitle_size.current(self.sizes.index(title_size)) self.fonttitle_size.grid(row=0, column=1, padx=4, pady=4) frame_title_style = Frame(fonttitle_frame) frame_title_style.grid(row=1, columnspan=2, padx=4, pady=6) self.is_bold = Checkbutton(frame_title_style, text=_("Bold"), command=self.update_preview_title) self.is_italic = Checkbutton(frame_title_style, text=_("Italic"), command=self.update_preview_title) self.is_underlined = Checkbutton(frame_title_style, text=_("Underline"), command=self.update_preview_title) style = CONFIG.get("Font", "title_style") if "bold" in style: self.is_bold.state(("selected", )) if "italic" in style: self.is_italic.state(("selected", )) if "underline" in style: self.is_underlined.state(("selected", )) self.is_bold.pack(side="left") self.is_italic.pack(side="left") self.is_underlined.pack(side="left") self.update_preview_title() # --- *-- text size = CONFIG.get("Font", "text_size") family = CONFIG.get("Font", "text_family") font_frame = Frame(font_settings) self.sample = Label(font_frame, text=_("Sample text"), anchor="center", style="prev.TLabel", relief="groove") self.sample.grid(row=1, columnspan=2, padx=4, pady=6, ipadx=4, ipady=4, sticky="eswn") self.font_family = Combobox(font_frame, values=self.fonts, width=(w * 2) // 3, exportselection=False, validate="key") self._validate_family = self.register( lambda *args: self.validate_font_family(self.font_family, *args)) self._validate_size = self.register( lambda *args: self.validate_font_size(self.font_size, *args)) self.font_family.configure(validatecommand=(self._validate_family, "%d", "%S", "%i", "%s", "%V")) self.font_family.current(self.fonts.index(family)) self.font_family.grid(row=0, column=0, padx=4, pady=4) self.font_size = Combobox(font_frame, values=self.sizes, width=5, exportselection=False, validate="key", validatecommand=(self._validate_size, "%d", "%P", "%V")) self.font_size.current(self.sizes.index(size)) self.font_size.grid(row=0, column=1, padx=4, pady=4) self.update_preview() # --- *-- placement Label(font_settings, text=_("Title")).grid(row=0, padx=4, pady=4, sticky="w") fonttitle_frame.grid(row=1) Separator(font_settings, orient="horizontal").grid(row=2, sticky="ew", pady=10) Label(font_settings, text=_("Text")).grid(row=3, padx=4, pady=4, sticky="w") font_frame.grid(row=4) # --- * Categories self.category_settings = CategoryManager(self.notebook, master) self.notebook.add(self.category_settings, text=_("Categories"), sticky="ewsn", padding=4) # --- * Symbols symbols_settings = Frame(self.notebook) self.notebook.add(symbols_settings, text=_("Symbols"), sticky="ewsn", padding=4) txt_frame = Frame(symbols_settings, relief="sunken", borderwidth=1, style="text.TFrame") self.symbols = Text(txt_frame, width=1, height=1, highlightthickness=0, spacing2=5, spacing1=5, relief="flat", padx=4, pady=4, font="%s %s" % (family.replace(" ", "\ "), size)) self.symbols.insert("1.0", CONFIG.get("General", "symbols")) Label(symbols_settings, text=_("Available symbols")).pack(padx=4, pady=4) txt_frame.pack(fill="both", expand=True, padx=4, pady=4) self.symbols.pack(fill="both", expand=True) Button(symbols_settings, text=_('Reset'), command=self.reset_symbols).pack(padx=4, pady=4) # --- Ok/Cancel buttons Button(okcancel_frame, text="Ok", command=self.ok).grid(row=1, column=0, padx=4, pady=10, sticky="e") Button(okcancel_frame, text=_("Cancel"), command=self.destroy).grid(row=1, column=1, padx=4, pady=10, sticky="w") # --- bindings self.font_family.bind('<<ComboboxSelected>>', self.update_preview) self.font_family.bind('<Return>', self.update_preview) self.font_size.bind('<<ComboboxSelected>>', self.update_preview, add=True) self.font_size.bind('<Return>', self.update_preview, add=True) self.fonttitle_family.bind('<<ComboboxSelected>>', self.update_preview_title) self.fonttitle_size.bind('<<ComboboxSelected>>', self.update_preview_title, add=True) self.fonttitle_family.bind('<Return>', self.update_preview_title) self.fonttitle_size.bind('<Return>', self.update_preview_title, add=True) def reset_symbols(self): self.symbols.delete('1.0', 'end') self.symbols.insert('1.0', SYMBOLS) def cleanup(self): ''' Remove unused latex images ''' self.master.cleanup() def validate_font_size(self, combo, d, ch, V): ''' Validation of the size entry content ''' if d == '1': l = [i for i in self.sizes if i[:len(ch)] == ch] if l: i = self.sizes.index(l[0]) combo.current(i) index = combo.index("insert") combo.selection_range(index + 1, "end") combo.icursor(index + 1) return ch.isdigit() else: return True def validate_font_family(self, combo, action, modif, pos, prev_txt, V): """ completion of the text in the path entry with existing folder/file names """ try: sel = combo.selection_get() txt = prev_txt.replace(sel, '') except TclError: txt = prev_txt if action == "0": txt = txt[:int(pos)] + txt[int(pos) + 1:] return True else: txt = txt[:int(pos)] + modif + txt[int(pos):] l = [i for i in self.fonts if i[:len(txt)] == txt] if l: i = self.fonts.index(l[0]) combo.current(i) index = combo.index("insert") combo.delete(0, "end") combo.insert(0, l[0].replace("\ ", " ")) combo.selection_range(index + 1, "end") combo.icursor(index + 1) return True else: return False def ok(self): family = self.font_family.get() if family not in self.fonts: l = [i for i in self.fonts if i[:len(family)] == family] if l: family = l[0] else: family = 'TkDefaultFont' size = self.font_size.get() familytitle = self.fonttitle_family.get() if familytitle not in self.fonts: l = [i for i in self.fonts if i[:len(familytitle)] == familytitle] if l: familytitle = l[0] else: familytitle = 'TkDefaultFont' sizetitle = self.fonttitle_size.get() opacity = "%i" % float(self.opacity_scale.get()) language = self.lang.get().lower()[:2] style = "" if self.is_bold.instate(("selected", )): style += "bold," if self.is_italic.instate(("selected", )): style += "italic," if self.is_underlined.instate(("selected", )): style += "underline," if style: style = style[:-1] symbols = [ l.strip() for l in self.symbols.get("1.0", "end").splitlines() ] CONFIG.set("General", "default_category", self.category_settings.default_category.get().lower()) CONFIG.set("General", "language", language) CONFIG.set("General", "opacity", opacity) CONFIG.set("General", "position", self.position.get()) CONFIG.set("General", "buttons_position", self.titlebar_disposition.get()) CONFIG.set("General", "symbols", "".join(symbols)) CONFIG.set("Font", "text_size", size) CONFIG.set("Font", "text_family", family) CONFIG.set("Font", "title_family", familytitle) CONFIG.set("Font", "title_size", sizetitle) CONFIG.set("Font", "title_style", style) col_changes = {} name_changes = {} for cat in self.category_settings.categories: new_name = self.category_settings.get_name(cat) if cat in CONFIG.options("Categories"): old_color = CONFIG.get("Categories", cat) new_color = COLORS[self.category_settings.get_color(cat)] if new_name != cat: name_changes[cat] = new_name CONFIG.remove_option("Categories", cat) CONFIG.set("Categories", new_name, new_color) if old_color != new_color: col_changes[new_name] = (old_color, new_color) CONFIG.set("Categories", new_name, new_color) else: CONFIG.set("Categories", new_name, COLORS[self.category_settings.get_color(cat)]) save_config() self.changes = col_changes, name_changes self.destroy() def get_changes(self): return self.changes def translate(self): showinfo( "Information", _("The language setting will take effect after restarting the application" ), parent=self) def update_preview(self, event=None): family = self.font_family.get() size = self.font_size.get() self.sample.configure(font="%s %s" % (family.replace(" ", "\ "), size)) def update_preview_title(self, event=None): family = self.fonttitle_family.get() size = self.fonttitle_size.get() config = "%s %s" % (family.replace(" ", "\ "), size) if self.is_bold.instate(("selected", )): config += " bold" if self.is_italic.instate(("selected", )): config += " italic" if self.is_underlined.instate(("selected", )): config += " underline" self.sampletitle.configure(font=config) def display_label(self, value): self.opacity_label.configure(text=" {val} %".format( val=int(float(value)))) def quit(self): self.destroy()
class ToggledFrame(Frame): """ A frame that can be toggled to open and close """ def __init__(self, master=None, text="", **kwargs): font = kwargs.pop('font', '') Frame.__init__(self, master, **kwargs) self.style_name = self.cget('style') self.toggle_style_name = '%s.Toggle' % ('.'.join( self.style_name.split('.')[:-1])) self.columnconfigure(1, weight=1) self.rowconfigure(1, weight=1) self.style = Style(self) self.style.configure(self.toggle_style_name, background=self.style.lookup( self.style_name, 'background')) self.style.map(self.toggle_style_name, background=[]) self._checkbutton = Checkbutton(self, style=self.toggle_style_name, command=self.toggle, cursor='arrow') self.label = Label(self, text=text, font=font, style=self.style_name.replace('TFrame', 'TLabel')) self.interior = Frame(self, style=self.style_name) self.interior.grid(row=1, column=1, sticky="nswe", padx=(4, 0)) self.interior.grid_remove() self.label.bind('<Configure>', self._wrap) self.label.bind('<1>', lambda e: self._checkbutton.invoke()) self._grid_widgets() self.bind('<<ThemeChanged>>', self._theme_changed) def _theme_changed(self, event): self.style.configure(self.toggle_style_name, background=self.style.lookup( self.style_name, 'background')) def _wrap(self, event): self.label.configure(wraplength=self.label.winfo_width()) def _grid_widgets(self): self._checkbutton.grid(row=0, column=0) self.label.grid(row=0, column=1, sticky="we") def toggle(self): if 'selected' not in self._checkbutton.state(): self.interior.grid_remove() self.event_generate("<<ToggledFrameClose>>") else: self.interior.grid() self.event_generate("<<ToggledFrameOpen>>") def open(self): self._checkbutton.state(('selected', )) self.interior.grid() self.event_generate("<<ToggledFrameOpen>>") def close(self): self._checkbutton.state(('!selected', )) self.interior.grid_remove() self.event_generate("<<ToggledFrameClose>>")
class ManagerItem(Frame): def __init__(self, master, note_data, toggle_visibility_cmd): Frame.__init__(self, master, class_='ManagerItem', style='manager.TFrame') self.columnconfigure(0, weight=0, minsize=18) self.columnconfigure(1, weight=1, minsize=198) self.columnconfigure(2, weight=1, minsize=198) self.columnconfigure(3, weight=0, minsize=85) self.columnconfigure(4, weight=0, minsize=22) self.toggle_visibility_cmd = toggle_visibility_cmd title = note_data['title'] if title: title = title[:17] + (len(title) > 17) * '...' title = title.replace('\t', ' ') date = note_data.get('date', '??') txt = note_data['txt'].splitlines() if txt: txt = txt[0][:17] + (len(txt[0]) > 17 or len(txt) > 1) * '...' else: txt = '' txt = txt.replace('\t', ' ') self._data = {'title': title, 'text': txt, 'date': date} self.title = Label(self, text=title, anchor='w', style='manager.TLabel') self.text = Label(self, text=txt, anchor='w', style='manager.TLabel') self.date = Label(self, text=date, anchor='center', style='manager.TLabel') self.checkbutton = Checkbutton(self, style='manager.TCheckbutton') self.visibility = BooleanVar(self, note_data['visible']) self.toggle_visibility = Checkbutton(self, style='manager.Toggle', variable=self.visibility, command=self.toggle_visibility) self.checkbutton.grid(row=0, column=0, padx=(2, 0), pady=4, sticky='nsew') self.title.grid(row=0, column=1, padx=4, pady=4, sticky='ew') self.text.grid(row=0, column=2, padx=4, pady=4, sticky='ew') self.date.grid(row=0, column=3, padx=4, pady=4, sticky='ew') self.toggle_visibility.grid(row=0, column=4, padx=(0, 2), pady=4, sticky='wens') self.bind('<Enter>', self._on_enter) self.bind('<Leave>', self._on_leave) self.checkbutton.bind('<Enter>', self._on_enter) # override class binding self.checkbutton.bind('<Leave>', self._on_leave) # override class binding self.toggle_visibility.bind('<Enter>', self._on_enter) # override class binding self.toggle_visibility.bind('<Leave>', self._on_leave) # override class binding self.bind('<ButtonRelease-1>', self._on_click) self.text.bind('<ButtonRelease-1>', self._on_click) self.title.bind('<ButtonRelease-1>', self._on_click) self.date.bind('<ButtonRelease-1>', self._on_click) def state(self, statespec=None): return self.checkbutton.state(statespec) def toggle_visibility(self): self.toggle_visibility_cmd(self.visibility.get()) def get(self, key): if key == 'visibility': return self.visibility.get() else: return self._data[key] def get_values(self): return (self._data['title'], self._data['text'], self._data['date'], self.visibility.get()) def _on_enter(self, event): self.title.state(('active', )) self.text.state(('active', )) self.date.state(('active', )) self.checkbutton.state(('active', )) self.toggle_visibility.state(('active', )) Frame.state(self, ('active', )) return "break" def _on_leave(self, event): self.title.state(('!active', )) self.text.state(('!active', )) self.date.state(('!active', )) self.checkbutton.state(('!active', )) self.toggle_visibility.state(('!active', )) Frame.state(self, ('!active', )) return "break" def _on_click(self, event): self.checkbutton.invoke() return "break"
def build(self): self.rbs = [] self.rbs1 = [] self.lF0 = lF0 = LabelFrame(self.fr, text='Widget and Themes') lF0.grid(row=0, column=0, sticky='nw') self.fr1 = fr1 = Frame(lF0) fr1.grid(row=0, column=0, sticky='nw') # create check box to select reverse selection order self.lF12 = lF12 = LabelFrame(fr1, text='Select Widget before Theme') lF12.grid(row=0, column=0, sticky='nw') self.ord = ord = IntVar() ord.set(0) cbut3 = Checkbutton(lF12, text='Reverse selection order', variable=ord, command=self.selord) cbut3.grid(row=0, column=0, padx=5, pady=5) cbut3.state(['!selected']) # create a Combobox to choose widgets widget_sel = ['Button', 'Checkbutton', 'Combobox', 'Entry', 'Frame', 'Label', 'LabelFrame', 'Menubutton', 'Notebook', 'PanedWindow', 'Progressbar', 'Radiobutton', 'Scale', 'Scrollbar', 'Separator', 'Sizegrip', 'Treeview'] ord = self.ord self.lf6 = LabelFrame(self.fr1, text='Select Widget', style="RoundedFrame", padding=(10,1,10,10)) self.lf6.grid(row=1, column=0, sticky='nw') self.lf6.state([("focus" if self.ord.get() == 0 else "!focus")]) self.widget_value = StringVar() self.cb = Combobox(self.lf6, values=widget_sel, textvariable=self.widget_value, state= ('disabled' if self.ord.get()==1 else 'active')) self.cb.grid(row=0, column=0, padx=5, pady=5, sticky='nw') self.cb.bind('<<ComboboxSelected>>', self.enabled) # create a Radio Buttons to choose orientation fr2 = Frame(self.lF0) fr2.grid(row=0, column=1, sticky='nw') self.lF5 = lF5 = LabelFrame(fr2, style="RoundedFrame", padding=(10,1,10,10), text='Orientation of \nProgressbar \nScale \nScrollbar') lF5.grid(row=0, column=0, padx=5, pady=5, sticky='nw') self.orient = StringVar() orientT = ['Horizontal', 'Vertical'] for ix, val in enumerate(orientT): rb = Radiobutton(lF5, text=val, value=val, command=self.orient_command, variable=self.orient, state='disabled') rb.grid(row=ix, column=0, sticky='w') self.rbs.append(rb) # create Radio Buttons to choose themes themes = {"alt": "alt - standard", "clam": "clam - standard", "classic": "classic - standard", "default": "default - standard"} self.lF1 = LabelFrame(self.fr1, text='Select Theme', style="RoundedFrame", padding=(10,1,10,10)) self.lF1.grid(row=2, column=0, sticky='n') self.theme_value = StringVar() for ix, val in enumerate(themes): rb1 = Radiobutton(self.lF1, text=themes[val], value=val, state='disabled', variable=self.theme_value, command=self.theme_command) rb1.grid(row=ix, column=0, padx=10, sticky='nw') self.rbs1.append(rb1)
def __init__(self, master, key, **kwargs): """ Create a new sticky note. master: main app key: key identifying this note in master.note_data kwargs: dictionnary of the other arguments (title, txt, category, color, tags, geometry, locked, checkboxes, images, rolled) """ Toplevel.__init__(self, master) # --- window properties self.id = key self.is_locked = not (kwargs.get("locked", False)) self.images = [] self.links = {} self.latex = {} self.nb_links = 0 self.title('mynotes%s' % key) self.attributes("-type", "splash") self.attributes("-alpha", CONFIG.getint("General", "opacity") / 100) self.focus_force() # window geometry self.update_idletasks() self.geometry(kwargs.get("geometry", '220x235')) self.save_geometry = kwargs.get("geometry", '220x235') self.update() self.rowconfigure(1, weight=1) self.minsize(10, 10) self.protocol("WM_DELETE_WINDOW", self.hide) # --- style self.style = Style(self) self.style.configure(self.id + ".TCheckbutton", selectbackground="red") self.style.map('TEntry', selectbackground=[('!focus', '#c3c3c3')]) selectbg = self.style.lookup('TEntry', 'selectbackground', ('focus', )) self.style.configure("sel.TCheckbutton", background=selectbg) self.style.map("sel.TCheckbutton", background=[("active", selectbg)]) # --- note elements # title font_title = "%s %s" % (CONFIG.get("Font", "title_family").replace( " ", "\ "), CONFIG.get("Font", "title_size")) style = CONFIG.get("Font", "title_style").split(",") if style: font_title += " " font_title += " ".join(style) self.title_var = StringVar(master=self, value=kwargs.get("title", _("Title"))) self.title_label = Label(self, textvariable=self.title_var, anchor="center", style=self.id + ".TLabel", font=font_title) self.title_entry = Entry(self, textvariable=self.title_var, exportselection=False, justify="center", font=font_title) # buttons/icons self.roll = Label(self, image="img_roll", style=self.id + ".TLabel") self.close = Label(self, image="img_close", style=self.id + ".TLabel") self.im_lock = PhotoImage(master=self, file=IM_LOCK) self.cadenas = Label(self, style=self.id + ".TLabel") # corner grip self.corner = Sizegrip(self, style=self.id + ".TSizegrip") # texte font_text = "%s %s" % (CONFIG.get("Font", "text_family").replace( " ", "\ "), CONFIG.get("Font", "text_size")) self.txt = Text(self, wrap='word', undo=True, selectforeground='white', inactiveselectbackground=selectbg, selectbackground=selectbg, tabs=(10, 'right', 21, 'left'), relief="flat", borderwidth=0, highlightthickness=0, font=font_text) # tags self.txt.tag_configure("bold", font="%s bold" % font_text) self.txt.tag_configure("italic", font="%s italic" % font_text) self.txt.tag_configure("bold-italic", font="%s bold italic" % font_text) self.txt.tag_configure("underline", underline=True, selectforeground="white") self.txt.tag_configure("overstrike", overstrike=True, selectforeground="white") self.txt.tag_configure("center", justify="center") self.txt.tag_configure("left", justify="left") self.txt.tag_configure("right", justify="right") self.txt.tag_configure("link", foreground="blue", underline=True, selectforeground="white") self.txt.tag_configure("list", lmargin1=0, lmargin2=21, tabs=(10, 'right', 21, 'left')) self.txt.tag_configure("todolist", lmargin1=0, lmargin2=21, tabs=(10, 'right', 21, 'left')) margin = 2 * Font(self, font=font_text).measure("m") self.txt.tag_configure("enum", lmargin1=0, lmargin2=margin + 5, tabs=(margin, 'right', margin + 5, 'left')) for coul in TEXT_COLORS.values(): self.txt.tag_configure(coul, foreground=coul, selectforeground="white") self.txt.tag_configure(coul + "-underline", foreground=coul, selectforeground="white", underline=True) self.txt.tag_configure(coul + "-overstrike", foreground=coul, overstrike=True, selectforeground="white") # --- menus # --- * menu on title self.menu = Menu(self, tearoff=False) # note color menu_note_color = Menu(self.menu, tearoff=False) colors = list(COLORS.keys()) colors.sort() for coul in colors: menu_note_color.add_command( label=coul, command=lambda key=coul: self.change_color(key)) # category self.category = StringVar( self, kwargs.get("category", CONFIG.get("General", "default_category"))) self.menu_categories = Menu(self.menu, tearoff=False) categories = CONFIG.options("Categories") categories.sort() for cat in categories: self.menu_categories.add_radiobutton(label=cat.capitalize(), value=cat, variable=self.category, command=self.change_category) # position: normal, always above, always below self.position = StringVar( self, kwargs.get("position", CONFIG.get("General", "position"))) menu_position = Menu(self.menu, tearoff=False) menu_position.add_radiobutton(label=_("Always above"), value="above", variable=self.position, command=self.set_position_above) menu_position.add_radiobutton(label=_("Always below"), value="below", variable=self.position, command=self.set_position_below) menu_position.add_radiobutton(label=_("Normal"), value="normal", variable=self.position, command=self.set_position_normal) # mode: note, list, todo list menu_mode = Menu(self.menu, tearoff=False) self.mode = StringVar(self, kwargs.get("mode", "note")) menu_mode.add_radiobutton(label=_("Note"), value="note", variable=self.mode, command=self.set_mode_note) menu_mode.add_radiobutton(label=_("List"), value="list", variable=self.mode, command=self.set_mode_list) menu_mode.add_radiobutton(label=_("ToDo List"), value="todolist", variable=self.mode, command=self.set_mode_todolist) menu_mode.add_radiobutton(label=_("Enumeration"), value="enum", variable=self.mode, command=self.set_mode_enum) self.menu.add_command(label=_("Delete"), command=self.delete) self.menu.add_cascade(label=_("Category"), menu=self.menu_categories) self.menu.add_cascade(label=_("Color"), menu=menu_note_color) self.menu.add_command(label=_("Lock"), command=self.lock) self.menu.add_cascade(label=_("Position"), menu=menu_position) self.menu.add_cascade(label=_("Mode"), menu=menu_mode) # --- * menu on main text self.menu_txt = Menu(self.txt, tearoff=False) # style menu_style = Menu(self.menu_txt, tearoff=False) menu_style.add_command(label=_("Bold"), command=lambda: self.toggle_text_style("bold")) menu_style.add_command( label=_("Italic"), command=lambda: self.toggle_text_style("italic")) menu_style.add_command(label=_("Underline"), command=self.toggle_underline) menu_style.add_command(label=_("Overstrike"), command=self.toggle_overstrike) # text alignment menu_align = Menu(self.menu_txt, tearoff=False) menu_align.add_command(label=_("Left"), command=lambda: self.set_align("left")) menu_align.add_command(label=_("Right"), command=lambda: self.set_align("right")) menu_align.add_command(label=_("Center"), command=lambda: self.set_align("center")) # text color menu_colors = Menu(self.menu_txt, tearoff=False) colors = list(TEXT_COLORS.keys()) colors.sort() for coul in colors: menu_colors.add_command(label=coul, command=lambda key=coul: self. change_sel_color(TEXT_COLORS[key])) # insert menu_insert = Menu(self.menu_txt, tearoff=False) menu_insert.add_command(label=_("Symbols"), command=self.add_symbols) menu_insert.add_command(label=_("Checkbox"), command=self.add_checkbox) menu_insert.add_command(label=_("Image"), command=self.add_image) menu_insert.add_command(label=_("Date"), command=self.add_date) menu_insert.add_command(label=_("Link"), command=self.add_link) if LATEX: menu_insert.add_command(label="LaTex", command=self.add_latex) self.menu_txt.add_cascade(label=_("Style"), menu=menu_style) self.menu_txt.add_cascade(label=_("Alignment"), menu=menu_align) self.menu_txt.add_cascade(label=_("Color"), menu=menu_colors) self.menu_txt.add_cascade(label=_("Insert"), menu=menu_insert) # --- restore note content/appearence self.color = kwargs.get("color", CONFIG.get("Categories", self.category.get())) self.txt.insert('1.0', kwargs.get("txt", "")) self.txt.edit_reset() # clear undo stack # restore inserted objects (images and checkboxes) # we need to restore objects with increasing index to avoid placment errors indexes = list(kwargs.get("inserted_objects", {}).keys()) indexes.sort(key=sorting) for index in indexes: kind, val = kwargs["inserted_objects"][index] if kind == "checkbox": ch = Checkbutton(self.txt, takefocus=False, style=self.id + ".TCheckbutton") if val: ch.state(("selected", )) self.txt.window_create(index, window=ch) elif kind == "image": if os.path.exists(val): self.images.append(PhotoImage(master=self.txt, file=val)) self.txt.image_create(index, image=self.images[-1], name=val) # restore tags for tag, indices in kwargs.get("tags", {}).items(): if indices: self.txt.tag_add(tag, *indices) for link in kwargs.get("links", {}).values(): self.nb_links += 1 self.links[self.nb_links] = link self.txt.tag_bind("link#%i" % self.nb_links, "<Button-1>", lambda e, l=link: open_url(l)) for img, latex in kwargs.get("latex", {}).items(): self.latex[img] = latex if LATEX: self.txt.tag_bind(img, '<Double-Button-1>', lambda e, im=img: self.add_latex(im)) mode = self.mode.get() if mode != "note": self.txt.tag_add(mode, "1.0", "end") self.txt.focus_set() self.lock() if kwargs.get("rolled", False): self.rollnote() if self.position.get() == "above": self.set_position_above() elif self.position.get() == "below": self.set_position_below() # --- placement # titlebar if CONFIG.get("General", "buttons_position") == "right": # right = lock icon - title - roll - close self.columnconfigure(1, weight=1) self.roll.grid(row=0, column=2, sticky="e") self.close.grid(row=0, column=3, sticky="e", padx=(0, 2)) self.cadenas.grid(row=0, column=0, sticky="w") self.title_label.grid(row=0, column=1, sticky="ew", pady=(1, 0)) else: # left = close - roll - title - lock icon self.columnconfigure(2, weight=1) self.roll.grid(row=0, column=1, sticky="w") self.close.grid(row=0, column=0, sticky="w", padx=(2, 0)) self.cadenas.grid(row=0, column=3, sticky="e") self.title_label.grid(row=0, column=2, sticky="ew", pady=(1, 0)) # body self.txt.grid(row=1, columnspan=4, column=0, sticky="ewsn", pady=(1, 4), padx=4) self.corner.lift(self.txt) self.corner.place(relx=1.0, rely=1.0, anchor="se") # --- bindings self.bind("<FocusOut>", self.save_note) self.bind('<Configure>', self.bouge) self.bind('<Button-1>', self.change_focus, True) self.close.bind("<Button-1>", self.hide) self.close.bind("<Enter>", self.enter_close) self.close.bind("<Leave>", self.leave_close) self.roll.bind("<Button-1>", self.rollnote) self.roll.bind("<Enter>", self.enter_roll) self.roll.bind("<Leave >", self.leave_roll) self.title_label.bind("<Double-Button-1>", self.edit_title) self.title_label.bind("<ButtonPress-1>", self.start_move) self.title_label.bind("<ButtonRelease-1>", self.stop_move) self.title_label.bind("<B1-Motion>", self.move) self.title_label.bind('<Button-3>', self.show_menu) self.title_entry.bind("<Return>", lambda e: self.title_entry.place_forget()) self.title_entry.bind("<FocusOut>", lambda e: self.title_entry.place_forget()) self.title_entry.bind("<Escape>", lambda e: self.title_entry.place_forget()) self.txt.tag_bind("link", "<Enter>", lambda event: self.txt.configure(cursor="hand1")) self.txt.tag_bind("link", "<Leave>", lambda event: self.txt.configure(cursor="")) self.txt.bind("<FocusOut>", self.save_note) self.txt.bind('<Button-3>', self.show_menu_txt) # add binding to the existing class binding so that the selected text # is erased on pasting self.txt.bind("<Control-v>", self.paste) self.corner.bind('<ButtonRelease-1>', self.resize) # --- keyboard shortcuts self.txt.bind('<Control-b>', lambda e: self.toggle_text_style('bold')) self.txt.bind('<Control-i>', lambda e: self.toggle_text_style('italic')) self.txt.bind('<Control-u>', lambda e: self.toggle_underline()) self.txt.bind('<Control-r>', lambda e: self.set_align('right')) self.txt.bind('<Control-l>', lambda e: self.set_align('left'))
class UpdateChecker(Toplevel): def __init__(self, master, notify=False): Toplevel.__init__(self, master, class_=APP_NAME) logging.info('Checking for updates') self.title(_("Update")) self.withdraw() self.rowconfigure(0, weight=1) self.columnconfigure(0, weight=1) self.columnconfigure(1, weight=1) self.protocol("WM_DELETE_WINDOW", self.quit) self.notify = notify self._version = __version__ self.img = PhotoImage(file=ICONS['question'], master=self) frame = Frame(self) frame.grid(row=0, columnspan=2, sticky="ewsn") Label(frame, image=self.img).pack(side="left", padx=(10, 4), pady=(10, 4)) Label(frame, text=_("A new version of {app_name} is available.\ \nDo you want to download it?").format(app_name=APP_NAME), font="TkDefaultFont 10 bold", wraplength=335).pack(side="left", padx=(4, 10), pady=(10, 4)) self.b1 = Button(self, text=_("Yes"), command=self.download) self.b1.grid(row=1, column=0, padx=10, pady=10, sticky="e") Button(self, text=_("No"), command=self.quit).grid(row=1, column=1, padx=10, pady=10, sticky="w") self.ch = Checkbutton(self, text=_("Check for updates on startup.")) if CONFIG.getboolean("General", "check_update"): self.ch.state(("selected", "!alternate")) else: self.ch.state(("!selected", "!alternate")) self.ch.grid(row=2, columnspan=2, sticky='w') self.update = None self.thread = Process(target=self.update_available, daemon=True) self.thread.start() self.after(1000, self.check_update) def check_update(self): if self.update is None: self.after(1000, self.check_update) elif self.update: logging.info("%s %s is available", APP_NAME, self._version) self.deiconify() self.grab_set() self.lift() self.b1.focus_set() else: if self.notify: run([ "notify-send", "-i", IM_ICON_SVG, _("Update"), _("{app_name} is up-to-date.").format(app_name=APP_NAME) ]) logging.info("%s is up-to-date", APP_NAME) self.destroy() def quit(self): CONFIG.set("General", "check_update", str("selected" in self.ch.state())) save_config() self.destroy() def download(self): webOpen("https://github.com/j4321/{app_name}/releases/tag/v{version}". format(app_name=APP_NAME, version=self._version)) self.quit() def update_available(self): """ Check for updates online, return True if an update is available, False otherwise (and if there is no Internet connection). """ feed = feedparser.parse( "https://github.com/j4321/{app_name}/releases.atom".format( app_name=APP_NAME)) try: # feed['entries'][0]['id'] is of the form 'tag:github.com,...:Repository/.../vx.y.z' self._version = os.path.split(feed['entries'][0]['id'])[1][1:] self.update = self._version > __version__ except IndexError: # feed['entries'] == [] because there is no Internet connection self.update = False