def style_for_warning_entry() -> None: """ Функция создания стиля для пустого Entry :return: """ style = Style() try: style.element_create('plain.field', 'from', 'clam') except TclError as error: if str(error) == 'Duplicate element plain.field': pass style.layout('Warning.TEntry', [('Entry.plain.field', { 'children': [('Entry.background', { 'children': [('Entry.padding', { 'children': [('Entry.textarea', { 'sticky': 'nswe' })], 'sticky': 'nswe' })], 'sticky': 'nswe' })], 'border': '2', 'sticky': 'nswe' })]) style.configure('Warning.TEntry', fieldbackground='#FFA3AD')
def __init__(self): Tk.__init__(self) self.title("BSE Kafka Tool") self.set_window_center(900, 500) self.resizable(True, True) self.win_success = None # Treeview style style = Style() style.element_create("Custom.Treeheading.border", "from", "default") style.layout("Custom.Treeview.Heading", [ ("Custom.Treeheading.cell", { 'sticky': 'nswe' }), ("Custom.Treeheading.border", { 'sticky': 'nswe', 'children': [("Custom.Treeheading.padding", { 'sticky': 'nswe', 'children': [("Custom.Treeheading.text", { 'sticky': 'we' })] })] }), ]) style.configure("Custom.Treeview.Heading", background="grey", foreground="white", relief="flat") configuration_window.ConfigurationWindow()
class PomodoroNotebook(ThemedTk): def __init__(self): ThemedTk.__init__(self) self.iconbitmap('pomodoro_icon.ico') self.resizable(False, False) self.set_theme('radiance') self.style = Style() self.style.configure('.', font=('Times', 13), takfocus=0, xpad=10) self.style.configure('TFrame', background='#471d0f') self.style.configure('TButton', 'Button.focus' == {'sticky': '0'}) print(self.style.layout('TButton')) self.title('Pomodoro') self.protocol("WM_DELETE_WINDOW", self.quit_event) self.note = Notebook(self) self.page_one = Page1(self.note) self.page_two = Page2(self.note) self.page_three = Page3(self.note) def batch_grid(self): self.note.add(self.page_one, text='Task Generation') self.note.add(self.page_two, text='Current Task') self.note.add(self.page_three, text='History') self.note.pack(fill='both') self.mainloop() def quit_event(self): try: pass except Exception as error: logging.debug(error) finally: self.destroy()
class ProgBar(object): def __init__(self, top): print("this is Progress bar status") #进度条窗口 #status bar self.fm_status = tk.Frame(top) for i in range(1): tk.Label(self.fm_status, text='').grid(row=i, column=0) self.v_status = tk.StringVar() self.v_status.set('Start') self.label_status = tk.Label(self.fm_status,textvariable=self.v_status, bd=1,\ justify='left') self.label_status.grid(row=3, column=1, sticky=tk.S) #进度条 self.style_bar = Style(top) self.style_bar.layout("LabeledProgressbar", [('LabeledProgressbar.trough', { 'children': [('LabeledProgressbar.pbar', { 'side': 'left', 'sticky': 'ns' }), ("LabeledProgressbar.label", { "sticky": "" })], 'sticky': 'nswe' })]) self.p_bar = Progressbar(self.fm_status, orient=tk.HORIZONTAL,\ length=100, mode='determinate',style="LabeledProgressbar") self.style_bar.configure("LabeledProgressbar", text="0 % ") self.p_bar.grid(row=3, column=0) self.p_bar["maximum"] = 100. self.p_bar["value"] = 0 def update_log(self, s): self.v_status.set(s) def show_status(self, flag=True): if flag: self.fm_status.pack(side=tk.LEFT) else: self.fm_status.pack_forget()
def __init__(self, master, kw_vals, default_text='Any', **kwargs): super().__init__(master, **kwargs) self.default_text = default_text self.kw_vals = kw_vals self.kw_vals_inv = dict(map(reversed, kw_vals.items())) ui_style = Style() ui_style.configure('MultiCombobox.TMenubutton', relief=RAISED, padding=3, anchor=CENTER) ui_style.layout('MultiCombobox.TMenubutton', _layout) self.config(style='MultiCombobox.TMenubutton') menu = Menu(self, tearoff=False, activeforeground='SystemHighlightText', activebackground='SystemHighlight', foreground='SystemWindowText', background='SystemWindow', disabledforeground='SystemGrayText', bd=0, activeborderwidth=1) self.configure(menu=menu) self.any_var = BooleanVar(value=True) menu.add_checkbutton(label=default_text, variable=self.any_var, onvalue=True, offvalue=False, command=self.anySelected) self.choices = {} for i, choice in enumerate(kw_vals): self.choices[choice] = BooleanVar() # columnbreak = (i+1) % 4 == 0 columnbreak = False menu.add_checkbutton(label=choice, variable=self.choices[choice], onvalue=True, offvalue=False, columnbreak=columnbreak, command=self.updateValue) self.updateValue()
class Table: """Table class used to display a table with database rows.""" def __init__(self, root, lst: list, fields: list, row: int, column: int, size=False): """ Constructor. :param root: - the frame where it will be displayed the table. :param lst: - list of rows from database. :param fields: - column names. :param row: - integer, to position table on Frame grid. :param column: - integer, to position table on Frame grid. :param size: - boolean, to define height of the table. If no rows to display, height is 0 else height is 20 rows. Default 4 rows. """ col, height = tuple([i for i in range(len(fields))]), 4 if len(lst) < 1: height = 0 if size: height = 20 # Style for the treeview self.style = Style() # Modify the font of the body self.style.configure("Treeview", highlightthickness=3, bd=3, background=st.colors['table'], font=st.fonts['table']) # Modify the font of the headings self.style.configure("Treeview.Heading", font=st.fonts['heading']) # Remove the borders self.style.layout("Treeview", [('Treeview.treearea', {'sticky': 'news'})]) # Creating the Table self.table = Treeview(root, column=col, height=height, show='headings', style='Treeview') self.table.grid(row=row, column=column, sticky="news", padx=12) # Creating scroll bar for the table self.scroll = Scrollbar(root, orient='vertical', command=self.table.yview) self.scroll.grid(row=row, column=column, sticky='nes', padx=12) # Adding the scroll bar to the table self.table.configure(yscrollcommand=self.scroll.set) # First column/ID width for item in range(len(fields)): if item == 0: self.table.column(item, width=30) self.table.heading(item, text=fields[item], anchor='nw') # Populating the table with database rows for row_ in lst: self.table.insert('', 'end', values=" ".join(str(i).strip("\'") for i in row_))
def __init__(self): global root self.master = Toplevel(root) self.master.withdraw() self.master.protocol('WM_DELETE_WINDOW', root.destroy) self.master.iconbitmap(imgdir) self.master.geometry("400x150") self.master.resizable(False, False) self.master.title("Adb & Fastboot Installer - By @Pato05") estyle = Style() estyle.element_create("plain.field", "from", "clam") estyle.layout("White.TEntry", [('Entry.plain.field', {'children': [( 'Entry.background', {'children': [( 'Entry.padding', {'children': [( 'Entry.textarea', {'sticky': 'nswe'})], 'sticky': 'nswe'})], 'sticky': 'nswe'})], 'border': '4', 'sticky': 'nswe'})]) estyle.configure("White.TEntry", background="white", foreground="black", fieldbackground="white") window = Frame(self.master, relief=FLAT) window.pack(padx=10, pady=5, fill=BOTH) Label(window, text='Installation path:').pack(fill=X) self.syswide = IntVar() self.instpath = StringVar() self.e = Entry(window, state='readonly', textvariable=self.instpath, style='White.TEntry') self.e.pack(fill=X) self.toggleroot() Label(window, text='Options:').pack(pady=(10, 0), fill=X) inst = Checkbutton(window, text="Install Adb and Fastboot system-wide?", variable=self.syswide, command=self.toggleroot) inst.pack(fill=X) self.path = IntVar(window, value=1) Checkbutton(window, text="Put Adb and Fastboot in PATH?", variable=self.path).pack(fill=X) Button(window, text='Install', command=self.install).pack(anchor='se') self.master.deiconify()
class show_status(): def __init__(self): # get program-call from arguments call = sys.argv[0] # was it an .exe-call ? if call.find('.exe') >= 0: # yes, perform all following calls as exe-calls self.scriptsAsExe = True else: # yes, perform all following calls as python-calls self.scriptsAsExe = False # check working-directory, is it already the build-dir? if (not os.getcwd().find(buildPath) >= 0): os.chdir("." + bs + buildPath) # set name of the progressFile self.progressFileName = progressFileName # creating tkinter window self.root = Tk() self.root.title('The Strak Machine V1.1') # Same size will be defined in variable for center screen in Tk_Width and Tk_height Tk_Width = 400 Tk_Height = 300 #calculate coordination of screen and window form x_Left = int(self.root.winfo_screenwidth() * 2 / 3 - Tk_Width / 2) y_Top = int(self.root.winfo_screenheight() / 2 - Tk_Height / 2) # Write following format for center screen self.root.geometry("+{}+{}".format(x_Left, y_Top)) # set background-colour self.root.configure(bg=bg_colour) # display logo of the strak machine imagename = (".." + bs + ressourcesPath + bs + logoName) # Creates a Tkinter-compatible photo image, which can be used everywhere # Tkinter expects an image object. img = ImageTk.PhotoImage(Image.open(imagename).resize((400, 100))) # The Label widget is a standard Tkinter widget used to display a text # or image on the screen. panel = tk.Label(self.root, image=img, bg=bg_colour) # The Pack geometry manager packs widgets in rows or columns. panel.pack(side="top", fill="both", expand="yes") # configure progress-bars self.s_main = Style(self.root) self.s_sub = Style(self.root) # add the label to the progressbar style self.s_main.layout("MainProgressbar", [('MainProgressbar.trough', { 'children': [('MainProgressbar.pbar', { 'side': 'left', 'sticky': 'ns' }), ("MainProgressbar.label", { "sticky": "" })], 'sticky': 'nswe' })]) # add the label to the progressbar style self.s_sub.layout("SubProgressbar", [('SubProgressbar.trough', { 'children': [('SubProgressbar.pbar', { 'side': 'left', 'sticky': 'ns' }), ("SubProgressbar.label", { "sticky": "" })], 'sticky': 'nswe' })]) # main-Progress bar widget self.main_progressBar = Progressbar(self.root, orient="horizontal", length=300, mode='determinate', style="MainProgressbar") # sub-Progress bar widget self.sub_progressBar = Progressbar(self.root, orient="horizontal", length=300, mode='determinate', style="SubProgressbar") self.main_progressBar.pack(pady=10) self.sub_progressBar.pack(pady=10) # change the text of the progressbar, # the trailing spaces are here to properly center the text self.s_main.configure("MainProgressbar", text="0 % ", background='green2') self.s_sub.configure("SubProgressbar", text="0 % ", background='green2') # create a scrollbar scrollbar = tk.Scrollbar(self.root) scrollbar.pack(side='right', fill='y') # create textbox to display content of progress-file self.progressLog = tk.Listbox(self.root, height=10, width=100, yscrollcommand=scrollbar.set) self.progressLog.pack(pady=10) scrollbar.config(command=self.progressLog.yview) # This button will start the visualizer Button(self.root, text='Start Visualizer', command=self.start_visualizer).pack(pady=10) # This button will Quit the application #Button(self.root, text = 'Quit', command = self.quit).pack(pady = 10) # update with actual values self.update_progressbars() # infinite loop self.root.mainloop() def read_progressFile(self): file_content = None airfoilname = "" #global main_progress Debug main_progress = 0.0 sub_progress = 0.0 try: file = open(self.progressFileName, 'r') file_content = file.readlines() file.close() except: print("Error, File %s could not be opened !" % self.progressFileName) sys.exit(-1) for line in file_content: # look for name of current airfoil if line.find("current airfoil") >= 0: splitlines = line.split(": ") airfoilname = splitlines[1] # look for main-task-progress if line.find("main-task progress") >= 0: splitlines = line.split(": ") main_progress = float(splitlines[1]) # look for sub-task-progress if line.find("sub-task progress") >= 0: splitlines = line.split(": ") sub_progress = float(splitlines[1]) return (main_progress, sub_progress, airfoilname, file_content) # gets the name of the airfoil that is currently processed by the strak-machine def get_CurrentAirfoilName(self): file_content = None airfoilname = "" try: file = open(self.progressFileName, 'r') file_content = file.readlines() file.close() except: print("Error, File %s could not be opened !" % self.progressFileName) sys.exit(-1) for line in file_content: # look for name of current airfoil if (line.find("finalizing airfoil") >= 0) or\ (line.find("creating preliminary-airfoil") >=0): splitlines = line.split(": ") airfoilname = splitlines[1] airfoilname = airfoilname.strip("\r\n\t '") return airfoilname # function to filter out some kind of output def filterLines(self, line): filteredLine = line # several filters if (line.find("progress") >= 0): filteredLine = None if (line.find("task") >= 0): filteredLine = None if (line.find("timestamp") >= 0): filteredLine = None return filteredLine # Function responsible for the update of the progress bar values def update_progressbars(self): global old_length global new_length global finished # read actual values from progress-file (main_progress, sub_progress, current_airfoil, content) = self.read_progressFile() # store lengths old_length = new_length new_length = len(content) # update progress-bars self.main_progressBar['value'] = main_progress self.sub_progressBar['value'] = sub_progress self.s_main.configure( "MainProgressbar", text="all airfoils: {0} % ".format(main_progress)) self.s_sub.configure( "SubProgressbar", text="current airfoil: {0} % ".format(sub_progress)) # update progress-log-widget (only the new lines) for idx in range(old_length, new_length): line = self.filterLines(content[idx]) if line != None: self.progressLog.insert(tk.END, content[idx]) # always show the last line, if there is a new one self.progressLog.see(tk.END) self.root.update() # check if strak-machine has finished if (finished == False): if (main_progress == 100.0): print(os.getcwd()) soundFileName = '..' + bs + ressourcesPath + bs + finishSound winsound.PlaySound(soundFileName, winsound.SND_FILENAME | winsound.SND_NOWAIT) finished = True # setup next cylce self.root.after(200, self.update_progressbars) def start_visualizer(self): # get current airfoilname from progressfile for starting the visualizer airfoilname = self.get_CurrentAirfoilName() # setup tool-calls exeCallString = " .." + bs + exePath + bs pythonCallString = pythonInterpreterName + ' ..' + bs + scriptPath + bs if (self.scriptsAsExe): xoptfoilVisualizerCall = exeCallString + xoptfoilVisualizerName + '.exe' else: xoptfoilVisualizerCall = pythonCallString + xoptfoilVisualizerName + '.py' # compose subprocess-string cmd = (" %s -o 3 -c %s\n") % (xoptfoilVisualizerCall, airfoilname) # now open subprocess p = subprocess.Popen(cmd, shell=True) def quit(self): self.root.destroy()
class ytDownloader: """ ytDownloader e' un youtube video downloader """ def __init__(self, _root): # start objects self.root = _root self.selected_language = 'ENG' self.lang = Translator(language) self.root.geometry("960x346") self.root.title("Yt Video Downloader") # self.root.title(self.lang.title) self.root.resizable(False, False) self.init_objects() self.v_counter = 0 self.v_down = 0 self.context_menu = Menu(self.root, tearoff=0) self.context_menu.add_command(label="Cut") self.context_menu.add_command(label="Copy") self.context_menu.add_command(label="Paste") self.downloading = False self.global_downloading = False self.ThreadLauncher = None self.root.protocol('WM_DELETE_WINDOW', self.prevent_close) def change_language(self, _language): self.lang.set_language(_language) self.set_values() # changing language menubar self.menubar.set_language() self.root.update_idletasks() def prevent_close(self): ''' Check if Thread is running then delete it :return: None ''' _title = '' _message = '' if self.global_downloading: _title = self.lang.running _message = f"{self.lang.running_msg_1}"\ f"\n {self.lang.close_app}" question = messagebox.askyesno(title=_title, message=_message) if question: # destroy thread runnning self.stop_download() else: return None self.root.destroy() # messagebox.showinfo('Running', 'The downloader app is already running') # root.destroy() def reset_values(self): self.v_counter = 0 self.v_down = 0 def popup(self, event, element): self.context_menu.post(event.x_root, event.y_root) self.context_menu.entryconfigure( "Cut", command=lambda: element.event_generate("<<Cut>>")) self.context_menu.entryconfigure( "Copy", command=lambda: element.event_generate("<<Copy>>")) self.context_menu.entryconfigure( "Paste", command=lambda: element.event_generate("<<Paste>>")) def init_objects(self): def _redo(text_object): var = text_object.edit_redo def _undo(text_object): var = text_object.edit_undo self.s = Style(self.root) self.s.theme_use('default') self.s.layout( "LabeledProgressbar", [( 'LabeledProgressbar.trough', { 'children': [ ('LabeledProgressbar.pbar', { 'side': 'left', 'sticky': 'ns' }), ( "LabeledProgressbar.label", # label inside the bar { "sticky": "" }) ], 'sticky': 'nswe' })]) self.s.configure("LabeledProgressbar", background='#06b025') self.font_title = ('consolas', 14) self.font_specs = ('consolas', 11) # variables used on proccess self.link = StringVar() self.save_path = StringVar() self.save_path.set(os.path.expanduser('~/Downloads')) self.title_var = StringVar() self.lb_title = Label(self.root, textvariable=self.title_var, font="consolas 14 bold") self.lb_title.grid(row=0, column=0, columnspan=3, padx=(10, 20)) self.d_descr_link = StringVar() self.descr_link = Label(self.root, textvariable=self.d_descr_link, font="consolas 11") self.descr_link.grid(row=1, column=0) self.d_lab_one = StringVar() self.lab_one = Label(self.root, textvariable=self.d_lab_one, font="Consolas 10") self.lab_one.grid(row=2, column=0) # frame for text where put the links self.my_frame = Frame(self.root) self.my_scroll = Scrollbar(self.my_frame, orient=VERTICAL) self.my_text = Text(self.my_frame, width=55, height=8, font=self.font_specs, yscrollcommand=self.my_scroll.set, undo=True) self.my_text.bind('<Control-z>', _undo(self.my_text)) self.my_text.bind('<Control-y>', _redo(self.my_text)) self.my_text.bind("<Button-3>", lambda event, text_element=self.my_text: self.popup( event=event, element=text_element)) self.my_scroll.config(command=self.my_text.yview) self.my_scroll.pack(side=RIGHT, fill=Y) self.my_frame.grid(row=3, column=0, padx=10) self.my_text.pack() # adding buttons self.button_frame = Frame(self.root) self.button_frame.grid(row=4, column=0, padx=10, pady=10) # clear button self.d_clear_button = StringVar() self.clear_button = Button(self.button_frame, textvariable=self.d_clear_button, command=self.clear_text) self.clear_button.grid(row=0, column=0, padx=10) self.d_start_down_btn = StringVar() self.start_down_btn = Button(self.button_frame, textvariable=self.d_start_down_btn, command=self.launch_down, fg="blue") self.start_down_btn.grid(row=0, column=1, padx=10) #stop button self.button_frame1 = Frame(self.root) self.button_frame1.grid(row=4, column=1, padx=10, pady=10) self.d_stop_button = StringVar() self.stop_button = Button(self.button_frame1, textvariable=self.d_stop_button, command=self.stop_download, fg="red") self.stop_button.grid(row=0, column=0, padx=10) self.stop_button.grid_remove() self.d_lb_save_in = StringVar() self.lb_save_in = Label(self.root, textvariable=self.d_lb_save_in, font="Consolas 11") self.lb_save_in.grid(row=1, column=1) # frame for path and button self.search_frame = Frame(self.root) self.search_frame.grid(row=2, column=1, sticky='w', padx=10) self.path_saver = Entry(self.search_frame, textvariable=self.save_path, width=65) self.path_saver.grid(row=0, column=0, sticky='w') self.d_brownse_btn = StringVar() self.browse_btn = Button(self.search_frame, textvariable=self.d_brownse_btn, command=self.browse_button) self.browse_btn.grid(row=0, column=1) # format file option # options frame self.option_format = IntVar() self.option_buttons = Frame(self.root) self.option_buttons.grid(row=5, column=0, sticky='w', padx=10) self.R1 = Radiobutton(self.option_buttons, text="Video", variable=self.option_format, value=1) self.R1.grid(row=0, column=1) self.R2 = Radiobutton(self.option_buttons, text="Audio", variable=self.option_format, value=2) self.R2.grid(row=0, column=2) # set value on audio self.option_format.set(2) # adding status frame self.status_frame = Frame(self.root, bd=1, relief=SUNKEN) self.status_frame.grid(row=6, column=0, columnspan=3, sticky=W + E, pady=(20, 0)) Label(self.status_frame, text="EA Yt Downloader", font="consolas 11 bold").grid(row=0, column=0, padx=10) self.status_text = StringVar() self.status_text_label = Label(self.status_frame, textvariable=self.status_text, font="consolas 11").grid(row=0, column=1, sticky='e') # adding status bar self.my_progress = ttk.Progressbar(self.status_frame, orient=HORIZONTAL, length=250, mode='determinate', style="LabeledProgressbar") self.my_progress.grid(row=0, column=2, sticky='e') # hiding progressbar self.my_progress.grid_remove() # debug text self.deb_frame = Frame(self.root) self.scr_deb = Scrollbar(self.deb_frame, orient=VERTICAL) self.my_text1 = Text(self.deb_frame, state='disabled', width=55, height=8, yscrollcommand=self.scr_deb.set, bg="white") self.scr_deb.config(command=self.my_text1.yview) self.scr_deb.pack(side=RIGHT, fill=Y) self.deb_frame.grid(row=3, column=1, columnspan=2, padx=10) self.my_text1.pack(side=LEFT, fill=BOTH, expand=YES) self.menubar = Menubar(self) self.statusbar = Statusbar(self) self.set_values() def set_values(self): ''' setting values of label, messages and buttons :return: None ''' try: self.title_var.set(self.lang.title) self.d_descr_link.set(self.lang.title_entry) self.d_lab_one.set(self.lang.one_line) self.d_clear_button.set(self.lang.clear) self.d_start_down_btn.set(self.lang.start_download) self.d_stop_button.set(self.lang.stop_download) self.d_lb_save_in.set(self.lang.save_title) self.d_brownse_btn.set(self.lang.browse) except AttributeError: # at the moment do nothing pass def clear_text(self): self.my_text.delete(1.0, END) def browse_button(self): # Allow user to select a directory and store it in global var # called folder_path # global save_path self.filename = filedialog.askdirectory() if self.filename != '': self.save_path.set(self.filename) def stop_download(self): ''' stop download thread and reset all necessary values ''' if not self.ThreadLauncher.stopped(): self.ThreadLauncher.terminate() self.ThreadLauncher.join() # reset values self.global_downloading = False # set message download stopped # get message on current lang self.status_text.set(self.lang.down_videos_stopped) # hide stop button self.stop_button.grid_remove() return None def launch_down(self): if self.global_downloading: messagebox.showinfo(self.lang.running, self.lang.running_msg) return None self.ThreadLauncher = DownloaderThread(target=self.download) self.ThreadLauncher.start() # pass def download(self): # using try and except to execute program without errors try: # reset counters self.reset_values() # global v_counter, v_down format_video = '' if self.option_format.get() == 1: # video format_video = 'bestvideo+bestaudio/best' elif self.option_format.get() == 2: # audio format_video = 'bestaudio/best' else: raise RuntimeError( "Opzione formato di download non selezionato") self.where_save = self.save_path.get() self.my_progress.grid_remove() # default for audio format for_audio = { 'postprocessors': [{ 'key': 'FFmpegExtractAudio', 'preferredcodec': 'mp3', 'preferredquality': '192', }] } download_options = { 'format': f'{format_video}', 'outtmpl': f'{self.where_save}\\%(title)s.%(ext)s', 'getfilename': '--get-filename', '--get-filename': True, 'progress_hooks': [self.my_hook], } if self.option_format.get() == 2: download_options.update(for_audio) list_links = self.my_text.get(1.0, END) list_down = list_links.splitlines() list_down = [line.rstrip('\n') for line in list_down] # check for correct links c_l = 0 for v_l in list_down: c_l += 1 if v_l.count('http') > 1 or v_l.count('HTTP') > 1: # self.status_text.set(f"Url not valid at line: {c_l}") raise RuntimeError(f"{self.lang.url_invalid}: {c_l}") self.v_counter = len(list_down) # check if empty list if (self.v_counter == 0 or (len(list_down) and list_down[0] == '')): self.status_text.set(self.lang.no_videos) return None with youtube_dl.YoutubeDL(download_options) as dl: self.my_progress.grid() self.stop_button.grid() self.global_downloading = True for video_link in list_down: if video_link == '': continue self.v_down += 1 dl.download([video_link]) self.status_text.set( f"{self.lang.all_downloaded} [{self.v_down}/{self.v_counter}]" ) self.global_downloading = False self.stop_button.grid_remove() # link.set("Video downloaded successfully") except RuntimeError as e: self.status_text.set(e) pass except Exception as e: self.status_text.set(e) self.global_downloading = False # hide stop button self.stop_button.grid_remove() print(e) pass def my_hook(self, d): try: # global percentage, curr_progress_bar # tmpfilename # here we should check if stop process is requested or not file_tuple = os.path.split(os.path.abspath(d['filename'])) # tmp_file_tuple = os.path.split(os.path.abspath(d['tmpfilename'])) # print(tmp_file_tuple) if not self.downloading: self.my_text1.configure(state='normal') self.my_text1.insert( 'end', f"{self.v_down}) {self.lang.starts_down_video}:" f"\n{file_tuple[1]}\n") self.my_text1.configure(state='disabled') self.downloading = True if d['status'] == 'finished': now = datetime.now() date_up = time.mktime(now.timetuple()) os.utime(d['filename'], (date_up, date_up)) # message_fin.set(f"Video \n {file_tuple[1]} \ndownloaded successfully") self.my_progress['value'] = 100 self.downloading = False self.my_text1.configure(state='normal') self.my_text1.insert( 'end', f"{self.v_down}) {self.lang.video_downloaded}\n") self.my_text1.configure(state='disabled') # self.percentage.config( # text=f"Downloaded... {self.my_progress['value']}%") self.s.configure("LabeledProgressbar", text=f"{self.my_progress['value']} % ") self.status_text.set( f"{self.lang.downloaded}... [{self.v_down}/{self.v_counter}]" ) self.root.update_idletasks() if d['status'] == 'downloading': p = d['_percent_str'] p = p.replace('%', '') p = d['_percent_str'] p = p.replace('%', '') percentage = float(p) self.my_progress['value'] = percentage self.status_text.set( f"{self.lang.downloading}... [{self.v_down}/{self.v_counter}]" ) self.s.configure("LabeledProgressbar", text=f"{self.my_progress['value']} % ") self.root.update_idletasks() except Exception as e: self.status_text.set(e) print(e) raise Exception(e)
class RegularityRallyGUI(RegularityRally): STATES = {-1: 'Select configuration', 0: 'Ready', 1: 'Fast Lap', 2: 'Untimed Lap', 3: 'Set Lap', 4: 'Confirmation Lap'} def __init__(self): super().__init__() # Data attribute initialization. self.root_dir = pathlib.Path(__file__).parent.parent.absolute() self.config_dir = os.path.join(self.root_dir, 'config') # Overwrite initial state. Config read is mandatory here. self.state = -1 # GUI attribute initialization. self.master = None self.font = None self.w = None self.h = None self.l_state = None self.l_mark = None self.l_cur_lap = None self.l_cur_lap_disp = None self.l_set_lap = None self.l_set_lap_disp = None self.t_laps = None self.bar_progress = None self.style_progress = None # Get start time. self.time_stamps = [] # Window initialization. self.init_window() def init_window(self): # Create main window self.master = Tk() self.master.title('Regularity Rally Timer') # Resize window. self.master.state('zoomed') self.w = self.master.winfo_screenwidth() self.h = self.master.winfo_screenheight() # Init font variable. self.font = tk_font.Font(family="Helvetica", size=36, weight="bold") # Create menu menu_bar = Menu(self.master) file_menu = Menu(menu_bar, tearoff=0) file_menu.add_command(label='New', command=self.new_cb) file_menu.add_command(label='Exit', command=self.master.quit) menu_bar.add_cascade(label="File", menu=file_menu) extras_menu = Menu(menu_bar, tearoff=0) extras_menu.add_command(label='Set new start time', command=None) extras_menu.add_command(label='Manage Config Files', command=None) extras_menu.add_command(label='Options', command=None) menu_bar.add_cascade(label="Extras", menu=extras_menu) self.master.config(menu=menu_bar) # Init Widgets. # TODO: Resize and anchor Labels. # Information row. row_count = 0 self.l_state = Label(self.master, text='Select Configuration', font=self.font) self.l_state.grid(row=row_count, column=0) self.l_mark = Label(self.master, text='', font=self.font) self.l_mark.grid(row=row_count, column=1) self.t_laps = Text(self.master, width=20, font=("Consolas", 32)) self.t_laps.grid(row=row_count, column=2, rowspan=3) self.master.grid_rowconfigure(row_count, weight=1) # Labels row. row_count += 1 self.l_cur_lap = Label(self.master, text='Current Lap', anchor='w', font=self.font) self.l_cur_lap.grid(row=row_count, column=0) self.l_set_lap = Label(self.master, text='Current Set Lap', anchor='w', font=self.font) self.l_set_lap.grid(row=row_count, column=1) self.master.grid_rowconfigure(row_count, weight=1) # Time display row. row_count += 1 self.l_cur_lap_disp = Label(self.master, text='__:__:__._', anchor='w', font=self.font) self.l_cur_lap_disp.grid(row=row_count, column=0) self.l_set_lap_disp = Label(self.master, text='__:__:__._', anchor='w', font=self.font) self.l_set_lap_disp.grid(row=row_count, column=1) self.master.grid_rowconfigure(row_count, weight=1) # Progressbar and countdown row. row_count += 1 # Create the progressbar style to be labeled. self.style_progress = Style(self.master) self.style_progress.layout("LabeledProgressbar", [('LabeledProgressbar.trough', {'children': [('LabeledProgressbar.pbar', {'side': 'left', 'sticky': 'ns'}), ("LabeledProgressbar.label", # label inside the bar {"sticky": ""})], 'sticky': 'nswe'})]) self.bar_progress = Progressbar(self.master, orient='horizontal', length=1000, mode='determinate', style="LabeledProgressbar") self.bar_progress.grid(row=row_count, column=0, columnspan=3, ipady=25) # Column configuration. self.master.grid_columnconfigure(0, weight=1) self.master.grid_columnconfigure(1, weight=1) # Define Binds. self.master.bind('1', self.one_cb) self.master.bind('2', self.two_cb) # Start master mainloop. self.master.after(10, self.gui_update) self.master.mainloop() def gui_update(self): # Calculate time delta of current lap. if self.state > 0: # Call regularity update. self.reg_update() # Update current lap time display. self.l_cur_lap_disp['text'] = '{:02}:{:02}:{:02}.{}'.format(self.curlap_decoded[0], self.curlap_decoded[1], self.curlap_decoded[2], floor(self.curlap_decoded[3]/100)) # Update countdown display, if confirmation lap. if self.state == 4: # Update progress bar. self.bar_progress['value'] = self.curlap_countdown_seconds self.style_progress.configure("LabeledProgressbar", text='{:.2f}'.format(self.curlap_countdown_seconds)) self.bar_progress.update() # Update mark label. if self.mark_count < len(self.mark_labels): self.l_mark['text'] = self.mark_labels[self.mark_count] else: self.bar_progress['value'] = 0 self.style_progress.configure("LabeledProgressbar", text='') # Schedule next call. self.master.after(10, self.gui_update) # Method called, when new race should be created. def new_cb(self): # Select and open a configuration. conf_file = open(filedialog.askopenfilename(initialdir=self.config_dir, title='Select file', filetypes=(('Configuration', "*.cfg"), ("all files", "*.*")))) # Read configuration. self.read_config(conf_file.name) # Write state. self.state = 0 self.l_state['text'] = 'Ready' # Method for new lap. # Only update displays here. Set data in regularityRally.reg_new_lap() def one_cb(self, _): if self.state == -1: self.new_cb() else: # Store last state. last_state = self.state # Perform new lap method. self.reg_new_lap() # Change label. self.l_state['text'] = self.STATES[self.state] if self.state in [3, 4]: marks = list(self.config['marks'].keys()) self.l_mark['text'] = marks[0] # Update lap times text field. self.t_laps.delete(1.0, 'end') for lt_id, lt in enumerate(self.lap_times_decoded): # Get text char from config. If id out of config, set 'F. if lt_id < len(self.config['states']): state_char = self.STATES[self.config['states'][lt_id]][0] else: state_char = 'F' # Set char for each lap. self.t_laps.insert('end', '{:>2}: {:02}:{:02}:{:02}.{:03} {}\n'.format(lt_id + 1, lt[0], lt[1], lt[2], lt[3], state_char)) # Update GUI items related to set lap. if self.cur_set_time is not None: # Update shown set time. self.l_set_lap_disp['text'] = '{:02}:{:02}:{:02}.{:01}'.format(self.cur_set_time_decoded[0], self.cur_set_time_decoded[1], self.cur_set_time_decoded[2], floor(self.cur_set_time_decoded[3]/100)) # Update progressbar maximum to set lap time to show countdown. self.bar_progress['maximum'] = self.cur_set_time def two_cb(self, _): # Execute superclass method. self.mark_reached() # Write next mark to label. marks = list(self.config['marks'].keys()) if len(marks) > self.mark_count: self.l_mark['text'] = marks[self.mark_count] else: self.l_mark['text'] = 'countdown'
and a standard ttk theme is not used. Most ttk widgets set the font when using configure, Entry is an exception in that we set the font as a property. ''' from tkinter import Tk from tkinter.ttk import Style, Entry root_window = Tk() estyle = Style() estyle.theme_use( 'clam') # if not used then uncomment element_create and layout # estyle.element_create("plain.field", "from", "clam") # "plain.field" is the cross reference # follows the layout of "vista", need layout to enable configure with vista ''' estyle.layout("EntryStyle.TEntry", [('Entry.plain.field', {'children': [( # using the cross reference 'Entry.background', {'children': [( 'Entry.padding', {'children': [( 'Entry.textarea', {'sticky': 'nswe'})], 'sticky': 'nswe'})], 'sticky': 'nswe'})], 'border':'2', 'sticky': 'nswe'})]) ''' estyle.configure( "EntryStyle.TEntry", # handle for style in Entry fieldbackground="light blue" # Set colour here "EntryStyle.TEntry" ) # font must be set in entry properties - not here entry = Entry(root_window, style="EntryStyle.TEntry", font="Gigi 12") entry.pack(padx=10, pady=10)
class MainDialog(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master) master.config(background=ctl.BACKGROUND_COLOR) toplevel = self.winfo_toplevel() toplevel.title('Corners Detector') toplevel.iconbitmap(os.path.abspath(os.path.join('resource', 'corners-icon.ico'))) # Declare all of the instance attributes here, and initialize them in separate methods for code separation # Keep them as attributes so we will not have issues with garbage collection. Especially with the PhotoImage self.settings = Settings().load() self.__title_frame = None # tk.Frame self.__action_frame = None # tk.Frame self.__status_frame = None # tk.Frame self.__figure_frame = None # tk.Frame self.__title = None # tk.Label self.__status_bar = None # tk.Label self.__progress_bar = None # tk.Progressbar self.__settings_button = None # tk.Button self.__go_button = None # tk.Button self.__open_file_button = None # tk.Button self.__popup_image_button = None # tk.Button self.__settings_button_tooltip = None # view.tooltip.Tooltip self.__go_button_tooltip = None # view.tooltip.Tooltip self.__open_file_button_tooltip = None # view.tooltip.Tooltip self.__popup_image_button_tooltip = None # view.tooltip.Tooltip self.__file_path_entry_tooltip = None # view.tooltip.Tooltip self.__canny_check_var = None # tk.IntVar - to hold the value of the canny edge checkbox self.__canny_checkbutton = None # tk.Checkbutton self.__gauss_check_var = None # tk.IntVar - to hold the value of the gaussian checkbox self.__gauss_checkbutton = None # tk.Checkbutton self.__file_path_entry = None # tk.Entry self.__magnifying_icon = None # tk.PhotoImage self.__play_icon = None # tk.PhotoImage self.__popout_icon = None # tk.PhotoImage self.__settings_icon = None # tk.PhotoImage self.__figure = None # A reference to pyplot figure, so we can destroy it when there is a new input self.__style = None # tk.ttk.Style self.__running = False # Indication of when we wait for the Harris Detector worker to finish self.__image = None # A reference to the input image. It is being set by the Harris Detector action self.__processed_image = None # A reference to the processed image (outcome). It is being set by the Harris Detector action self.__error = False # Indication for a failure during Harris Detector algorithm self.progress_text_format = '{0}%' self.__style = Style(master) self.__style.theme_use('clam') self.__style.configure("Horizontal.TProgressbar", foreground='#E0E0E0', background=ctl.ACCEPT_COLOR, troughcolor=ctl.BACKGROUND_TOOLTIP_COLOR, bordercolor=ctl.ACCEPT_COLOR, lightcolor=ctl.ACCEPT_COLOR, darkcolor=ctl.ACCEPT_COLOR) self.__style.configure('TButton', font=ctl.FONT_BUTTON, borderwidth='1', background=ctl.BACKGROUND_COLOR, relief='FLAT') self.__style.configure('TLabel', font=ctl.FONT_REGULAR) self.__style.map('TButton', background=[('active', '!disabled', '#4E6067')]) self.__style.map('TCombobox', foreground=[('readonly', '!disabled', ctl.FOREGROUND_EDITOR_COLOR)]) self.__style.map('TCombobox', background=[('readonly', '!disabled', ctl.BACKGROUND_EDITOR_COLOR)]) self.__style.map('TCombobox', fieldbackground=[('readonly', '!disabled', ctl.BACKGROUND_EDITOR_COLOR)]) self.__style.map('TCombobox', lightcolor=[('readonly', '!disabled', 'black')]) self.__style.map('TCombobox', darkcolor=[('readonly', '!disabled', 'black')]) self.__style.map('TCombobox', bordercolor=[('readonly', '!disabled', '#E0E0E0')]) master.option_add("*TCombobox*Listbox*Background", ctl.BACKGROUND_EDITOR_COLOR) master.option_add("*TCombobox*Listbox*Foreground", ctl.FOREGROUND_EDITOR_COLOR) self.create_title_section(master) self.create_action_section(master) self.create_status_section(master) self.create_workarea_section(master) master.bind('<Return>', self.on_enter_pressed) master.geometry("800x600") def create_title_section(self, master): """ Title area contains a frame with a label in it. :param master: Master dialog to add the frame to :return: None """ self.__title_frame = ctl.create_frame(master, tk.X) self.__title = ctl.create_title(self.__title_frame, 'Corners Detector') self.__settings_icon = tk.PhotoImage(file=os.path.abspath(os.path.join('resource', 'settings-icon.png'))) self.__settings_button = ttk.Button(master=self.__title_frame, image=self.__settings_icon, command=self.show_settings_dialog, style='TButton') self.__settings_button_tooltip = Tooltip(self.__settings_button, 'Settings') # First display the button and only then the label, so they will sit in one row self.__settings_button.pack(side=tk.RIGHT) self.__title.pack(fill=tk.X) def create_action_section(self, master): """ Action area contains a frame with an entry (text edit for image path), open file dialog button and Go button. Open file dialog button will display an open file dialog to select image Go button will execute Harris Detector action on the selected image :param master: Master dialog to add the frame to :return: None """ self.__action_frame = ctl.create_frame(master, tk.X) self.__magnifying_icon = tk.PhotoImage(file=os.path.abspath(os.path.join('resource', 'magnifying-icon.png'))) self.__file_path_entry = ctl.create_entry(self.__action_frame) self.__file_path_entry.pack(fill=tk.BOTH, side=tk.LEFT, expand=True) self.__file_path_entry_tooltip = Tooltip(self.__file_path_entry, 'Click the magnifying icon in order to open an image') self.__open_file_button = ttk.Button(master=self.__action_frame, image=self.__magnifying_icon, command=self.open_file_action, style='TButton', width=4) self.__open_file_button.pack(side=tk.LEFT) self.__open_file_button_tooltip = Tooltip(self.__open_file_button, 'Open image or video') ctl.create_pad(self.__action_frame, tk.LEFT) self.__play_icon = tk.PhotoImage(file=os.path.abspath(os.path.join('resource', 'play-icon.png'))) self.__go_button = ttk.Button(master=self.__action_frame, image=self.__play_icon, command=self.on_enter_pressed, style='TButton', width=4) self.__go_button_tooltip = Tooltip(self.__go_button, 'Execute corners detection') self.__go_button.pack(side=tk.RIGHT) # Get a second line in the actions area, so the checkbox will be under the entry helper_frame = ctl.create_frame(master, tk.X) self.__canny_checkbutton, self.__canny_check_var = ctl.create_checkbutton(helper_frame, 'Go through Canny Edge Detection', tk.LEFT) self.__gauss_checkbutton, self.__gauss_check_var = ctl.create_checkbutton(helper_frame, 'Apply Gaussian Blur', tk.LEFT) self.__gauss_checkbutton.select() self.__popout_icon = tk.PhotoImage(file=os.path.abspath(os.path.join('resource', 'pop-out-icon.png'))) self.__popup_image_button = ttk.Button(master=helper_frame, image=self.__popout_icon, command=self.popup_image, style='TButton') self.__popup_image_button_tooltip = Tooltip(self.__popup_image_button, 'Pops out the image with marks as a pyplot dialog') self.__popup_image_button.pack(side=tk.RIGHT) self.__popup_image_button['state'] = 'disabled' def create_status_section(self, master): """ Status area contains a progress bar which we put at the bottom of the frame, to show a progress indication when the algorithm is executing. (Can take some seconds) We also add a label into the progress bar to use it as a status bar as well :param master: Master dialog to add the frame to :return: None """ self.__status_frame = ctl.create_frame(master, tk.X, 0, 0) self.__status_frame.pack(fill=tk.X, side=tk.BOTTOM, anchor=tk.S) # Add label into the layout self.__style.layout('text.Horizontal.TProgressbar', [('Horizontal.Progressbar.trough', {'children': [('Horizontal.Progressbar.pbar', {'side': 'left', 'sticky': 's'})], 'sticky': 'swe'}), ('Horizontal.Progressbar.label', {'sticky': 'we'})]) self.__style.configure('text.Horizontal.TProgressbar', font=ctl.FONT_REGULAR) self.__progress_bar = tk.ttk.Progressbar(master=self.__status_frame, orient=tk.HORIZONTAL, style='text.Horizontal.TProgressbar', length=101, mode='determinate', value=0, maximum=101) self.__progress_bar.pack(fill=tk.X) self.update_status('Open an image using the magnifying button and click Play') def create_workarea_section(self, master): """ Workarea contains a frame onto which we are displaying the plots when we finish executing Harris Detector algorithm :param master: Master dialog to add the frame to :return: None """ self.__figure_frame = ctl.create_frame(master, tk.BOTH) self.__figure_frame.configure(background='white') # Make it white because I could not modify the PyPlot background def update_status(self, text): """ Updates the text of the progress bar (status bar) with the specified text :param text: The text to set into the status bar :return: None """ self.__style.configure('text.Horizontal.TProgressbar', text=' ' + text) self.progress_text_format = ' ' + text + ' {0}%' print(text) def update_progress(self, progress): """ Updates the progress bar with a progress, so user can see how much time remains :param progress: The progress sto set to the progressbar :return: None """ progress = np.min([100, int(progress)]) self.__progress_bar['value'] = progress self.__style.configure('text.Horizontal.TProgressbar', text=self.progress_text_format.format(progress)) def start_progress(self): """ Prepare for starting a progress in the progress bar, for visualizing process. We also start checking the job queue to detect if the algorithm has finished its execution so we can display the outcome :return: None """ self.set_user_components_state('disabled') self.__image = self.__processed_image = None self.__running = True self.periodically_check_outcome() if self.__figure is not None: self.__figure.set_visible(False) self.__figure.clear() self.__figure = None # Remove all children so we will not have issues with pyplot's painting for child in self.__figure_frame.winfo_children(): child.destroy() def stop_progress(self): """ Stops the progress of the progress bar, and the periodic check of the job queue :return: None """ # self.__progress_bar.stop() self.__progress_bar['value'] = 0 self.__running = False self.set_user_components_state('normal') def set_user_components_state(self, new_state): """ Sets the state of user components to 'normal' or 'disabled'. When the algorithm is running in background, we disable the user components so there won't be a mess :param new_state: The new state to set. Can be one of 'normal' or 'disabled' :return: None """ self.__go_button['state'] = new_state self.__open_file_button['state'] = new_state self.__file_path_entry['state'] = new_state self.__canny_checkbutton['state'] = new_state self.__gauss_checkbutton['state'] = new_state self.__popup_image_button['state'] = new_state def open_file_action(self): """ Displaying a file open dialog in image selecting mode, to select an image for executing the algorithm on :return: None """ file_name = tkfiledialog.askopenfilename(filetypes=[("Image File", '.jpg .jpeg .png .bmp'), ("Video File", '.mp4 .avi .wmv')]) self.__file_path_entry.delete(0, tk.END) self.__file_path_entry.insert(0, file_name) def show_settings_dialog(self): """ Displaying Settings dialog so user can customize the algorithm settings :return: None """ settings = SettingsDialog(self.master, self.settings).result if settings: self.settings = settings self.settings.save() def on_enter_pressed(self, event=None): """ This event is raised whenever user presses the Go button or Enter In this case we are going to execute Harris Detector algorithm using the selected image, in case there is a valid selection. :param event: The keypress event (We register on the master frame) :return: None """ file_path = self.__file_path_entry.get().strip() if file_path == '': messagebox.showerror('Illegal Input', 'Please select a file first') return None if not os.path.exists(file_path) or not os.path.isfile(file_path): messagebox.showerror('Illegal Input', 'Selected file is not a file or it does not exist:\n{}'.format(file_path)) return None if not self.__running: if is_video(file_path): self.play_selected_video(file_path) else: self.start_progress() # Run it in background so the progress bar will not get blocked. (We cannot block te gui thread) t = Thread(target=self.execute_harris_detector, args=(file_path,)) t.start() else: print('Already running. Cannot run multiple detections in parallel.') def execute_harris_detector(self, path): """ The job of executing Harris Detector algorithm. It takes time so we have a specific action for that, such that we can run it using a background thread :param path: Path to the selected image :return: None """ self.__image, self.__processed_image = corners_and_line_intersection_detector(cv2.imread(path), lambda text: self.update_status(text), lambda progress: self.update_progress(progress), bool(self.__canny_check_var.get()), bool(self.__gauss_check_var.get()), self.settings) if self.__image is None or self.__processed_image is None: self.__error = True def show_images(self, img, processed_image): """ When Harris Detector job has finished we display the results as embedded figure :param img: Original image :param processed_image: Image with corner marks :return: None """ self.__figure = Figure(figsize=(5, 5), dpi=100) axes = self.__figure.add_subplot(121) axes.imshow(np.uint8(img[:, :, ::-1])) axes = self.__figure.add_subplot(122) axes.imshow(np.uint8(processed_image[:, :, ::-1])) self.__figure.subplots_adjust(0.05, 0.2, 0.95, 0.9, 0.2, 0.2) canvas = FigureCanvasTkAgg(self.__figure, self.__figure_frame) canvas.draw() canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True) toolbar = NavigationToolbar2Tk(canvas, self.__figure_frame) toolbar.update() canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True) def periodically_check_outcome(self): """ Check every 200 ms if there is something new to show, which means a harris_detector worker has finished and we can pick the output and show it in the GUI """ if self.__error: self.__error = False self.stop_progress() messagebox.showerror('Error', 'Error has occurred while trying to detect corners') self.__image = self.__processed_image = None return None if self.__image is not None and self.__processed_image is not None: self.stop_progress() # Plot the images, embedded within our dialog rather than popping up another dialog. self.show_images(self.__image, self.__processed_image) if self.__running: self.master.after(50, self.periodically_check_outcome) def popup_image(self): """ Action corresponding to when user presses the popup button, to plot the outcome outside the main window :return: None """ if self.__processed_image is not None: plt.figure('Corners Detector') plt.tight_layout(pad=0.5) plt.imshow(np.uint8(self.__processed_image[:, :, ::-1])) plt.subplots_adjust(0.05, 0.05, 0.95, 0.95, 0.1, 0.1) figure_manager = plt.get_current_fig_manager() figure_manager.window.iconbitmap(os.path.abspath(os.path.join('resource', 'corners-icon.ico'))) try: # In case there would be an issue on Ubuntu or something like that, just catch the error and use resize figure_manager.window.state('zoomed') except Exception as e: print('Error has occurred while trying to show window as maximized. Fallback to resize strategy:', str(e)) figure_manager.resize(*figure_manager.window.maxsize()) figure_manager.window.wm_geometry("+0+0") plt.show() def play_selected_video(self, file_path): """ This method will load a video from file and display it in cv2's window. We will execute corners detection, using the application settings, over the frames of the selected video and draw them live. :param file_path: Path to the video file to load :return: None """ font = cv2.FONT_HERSHEY_COMPLEX bottom_left_corner_of_text = (10, 30) font_scale = 1 font_color = (0, 255, 0) lineType = 1 cap = cv2.VideoCapture(file_path) while cap.isOpened(): ret, frame = cap.read() if frame is None: break # Don't send listeners while playing a video in order to avoid of flooding the console image, processed_image = corners_and_line_intersection_detector(frame, is_using_canny=bool(self.__canny_check_var.get()), is_applying_gauss=bool(self.__gauss_check_var.get()), settings=self.settings) self.__progress_bar['value'] = 0 if processed_image is not None: cv2.putText(processed_image, 'Press q to stop', bottom_left_corner_of_text, font, font_scale, font_color, lineType) cv2.imshow('Corners Detector', processed_image) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release()
def __init__(self): try: with open(r'C:\Users\barry\PycharmProjects\face_rec\labels.pickle', 'rb') as self.f: self.og_labels = pickle.load(self.f) print(self.og_labels) # error checking except FileNotFoundError: print( "No label.pickle file detected, will create required pickle files" ) # this will be used to for selecting the photos self.current_id = 0 # creating a blank ids dictionary self.labels_ids = {} # this is the directory where all the users are stored self.BASE_DIR = os.path.dirname(os.path.abspath(__file__)) self.image_dir = os.path.join(self.BASE_DIR, 'images') for self.root, self.dirs, self.files in os.walk(self.image_dir): # checking each folder in the images directory for self.file in self.files: # looking for any png or jpg files of the users if self.file.endswith('png') or self.file.endswith('jpg'): # getting the folder name, as the name of the folder will be the user self.path = os.path.join(self.root, self.file) self.label = os.path.basename(os.path.dirname( self.path)).replace(' ', '-').lower() if not self.label in self.labels_ids: # adding the user into the labels_id dictionary self.labels_ids[self.label] = self.current_id self.current_id += 1 self.id = self.labels_ids[self.label] print(self.labels_ids) # this is compare the new label ids to the old label ids dictionary seeing if their has been any new users or old users # being added to the system, if there is no change then nothing will happen self.og_labels = 0 toolbar_width = 40 root = Tk() s = Style(root) # add the label to the progressbar style s.layout( "LabeledProgressbar", [( 'LabeledProgressbar.trough', { 'children': [ ('LabeledProgressbar.pbar', { 'side': 'right', 'sticky': 'ns' }), ( "LabeledProgressbar.label", # label inside the bar { "sticky": "" }) ], 'sticky': 'nswe' })]) p = Progressbar(root, orient="horizontal", length=300, style="LabeledProgressbar") p.pack() # change the text of the progressbar, # the trailing spaces are here to properly center the text s.configure("LabeledProgressbar", text="0%") if self.labels_ids != self.og_labels: # if the dictionary change then the new dictionary will be dump into the pickle file with open('labels.pickle', 'wb') as self.file: pickle.dump(self.labels_ids, self.file) self.known_faces = [] for self.i in self.labels_ids: noOfImgs = len([ filename for filename in os.listdir('images/' + mssv.get() + self.i) if os.path.isfile( os.path.join('images/' + mssv.get() + self.i, filename)) ]) print("Dang load ", end='') print(noOfImgs, end='') print(" hinh cua ", end='') print(self.i) # setup toolbar sys.stdout.write("[%s]" % (" " * toolbar_width)) sys.stdout.flush() sys.stdout.write( "\b" * (toolbar_width + 1)) # return to start of line, after '[' for imgNo in range(1, (noOfImgs + 1)): self.directory = os.path.join(self.image_dir, self.i, str(imgNo) + '.png') self.img = face_recognition.load_image_file(self.directory) self.img_encoding = face_recognition.face_encodings( self.img)[0] self.known_faces.append([self.i, self.img_encoding]) #sf = self.directory.count(self.image_dir) #sfd = self.directory.count(self.i) #time.sleep(0.1) # do real work here # update the bar percent = str(round((imgNo / noOfImgs) * 100)) #percent2 = str("Dang load " + str(imgNo) + "/" + str(noOfImgs) + "anh cua " + self.i) #p.step((noOfImgs/imgNo)/1.22) #s.configure("LabeledProgressbar", text="{0}".format(percent2)) #root.update() sys.stdout.write(colored(percent + "% ", "green")) sys.stdout.flush() sys.stdout.write("]\n") print(self.known_faces) print("No Of Imgs" + str(len(self.known_faces))) root.mainloop() with open('KnownFace.pickle', 'wb') as self.known_faces_file: pickle.dump(self.known_faces, self.known_faces_file) else: with open( r'CC:\Users\barry\PycharmProjects\face_rec\KnownFace.pickle', 'rb') as self.faces_file: self.known_faces = pickle.load(self.faces_file) print(self.known_faces)
class EventScheduler(Tk): def __init__(self): Tk.__init__(self, className='Scheduler') logging.info('Start') self.protocol("WM_DELETE_WINDOW", self.hide) self._visible = BooleanVar(self, False) self.withdraw() self.icon_img = PhotoImage(master=self, file=ICON48) self.iconphoto(True, self.icon_img) # --- systray icon self.icon = TrayIcon(ICON, fallback_icon_path=ICON_FALLBACK) # --- menu self.menu_widgets = SubMenu(parent=self.icon.menu) self.menu_eyes = Eyes(self.icon.menu, self) self.icon.menu.add_checkbutton(label=_('Manager'), command=self.display_hide) self.icon.menu.add_cascade(label=_('Widgets'), menu=self.menu_widgets) self.icon.menu.add_cascade(label=_("Eyes' rest"), menu=self.menu_eyes) self.icon.menu.add_command(label=_('Settings'), command=self.settings) self.icon.menu.add_separator() self.icon.menu.add_command(label=_('About'), command=lambda: About(self)) self.icon.menu.add_command(label=_('Quit'), command=self.exit) self.icon.bind_left_click(lambda: self.display_hide(toggle=True)) add_trace(self._visible, 'write', self._visibility_trace) self.menu = Menu(self, tearoff=False) self.menu.add_command(label=_('Edit'), command=self._edit_menu) self.menu.add_command(label=_('Delete'), command=self._delete_menu) self.right_click_iid = None self.menu_task = Menu(self.menu, tearoff=False) self._task_var = StringVar(self) menu_in_progress = Menu(self.menu_task, tearoff=False) for i in range(0, 110, 10): prog = '{}%'.format(i) menu_in_progress.add_radiobutton(label=prog, value=prog, variable=self._task_var, command=self._set_progress) for state in ['Pending', 'Completed', 'Cancelled']: self.menu_task.add_radiobutton(label=_(state), value=state, variable=self._task_var, command=self._set_progress) self._img_dot = tkPhotoImage(master=self) self.menu_task.insert_cascade(1, menu=menu_in_progress, compound='left', label=_('In Progress'), image=self._img_dot) self.title('Scheduler') self.rowconfigure(1, weight=1) self.columnconfigure(0, weight=1) self.scheduler = BackgroundScheduler(coalesce=False, misfire_grace_time=86400) self.scheduler.add_jobstore('sqlalchemy', url='sqlite:///%s' % JOBSTORE) self.scheduler.add_jobstore('memory', alias='memo') # --- style self.style = Style(self) self.style.theme_use("clam") self.style.configure('title.TLabel', font='TkdefaultFont 10 bold') self.style.configure('title.TCheckbutton', font='TkdefaultFont 10 bold') self.style.configure('subtitle.TLabel', font='TkdefaultFont 9 bold') self.style.configure('white.TLabel', background='white') self.style.configure('border.TFrame', background='white', border=1, relief='sunken') self.style.configure("Treeview.Heading", font="TkDefaultFont") bgc = self.style.lookup("TButton", "background") fgc = self.style.lookup("TButton", "foreground") bga = self.style.lookup("TButton", "background", ("active", )) self.style.map('TCombobox', fieldbackground=[('readonly', 'white'), ('readonly', 'focus', 'white')], background=[("disabled", "active", "readonly", bgc), ("!disabled", "active", "readonly", bga)], foreground=[('readonly', '!disabled', fgc), ('readonly', '!disabled', 'focus', fgc), ('readonly', 'disabled', 'gray40'), ('readonly', 'disabled', 'focus', 'gray40') ], arrowcolor=[("disabled", "gray40")]) self.style.configure('menu.TCombobox', foreground=fgc, background=bgc, fieldbackground=bgc) self.style.map('menu.TCombobox', fieldbackground=[('readonly', bgc), ('readonly', 'focus', bgc)], background=[("disabled", "active", "readonly", bgc), ("!disabled", "active", "readonly", bga)], foreground=[('readonly', '!disabled', fgc), ('readonly', '!disabled', 'focus', fgc), ('readonly', 'disabled', 'gray40'), ('readonly', 'disabled', 'focus', 'gray40') ], arrowcolor=[("disabled", "gray40")]) self.style.map('DateEntry', arrowcolor=[("disabled", "gray40")]) self.style.configure('cal.TFrame', background='#424242') self.style.configure('month.TLabel', background='#424242', foreground='white') self.style.configure('R.TButton', background='#424242', arrowcolor='white', bordercolor='#424242', lightcolor='#424242', darkcolor='#424242') self.style.configure('L.TButton', background='#424242', arrowcolor='white', bordercolor='#424242', lightcolor='#424242', darkcolor='#424242') active_bg = self.style.lookup('TEntry', 'selectbackground', ('focus', )) self.style.map('R.TButton', background=[('active', active_bg)], bordercolor=[('active', active_bg)], darkcolor=[('active', active_bg)], lightcolor=[('active', active_bg)]) self.style.map('L.TButton', background=[('active', active_bg)], bordercolor=[('active', active_bg)], darkcolor=[('active', active_bg)], lightcolor=[('active', active_bg)]) self.style.configure('txt.TFrame', background='white') self.style.layout('down.TButton', [('down.TButton.downarrow', { 'side': 'right', 'sticky': 'ns' })]) self.style.map('TRadiobutton', indicatorforeground=[('disabled', 'gray40')]) self.style.map('TCheckbutton', indicatorforeground=[('disabled', 'gray40')], indicatorbackground=[ ('pressed', '#dcdad5'), ('!disabled', 'alternate', 'white'), ('disabled', 'alternate', '#a0a0a0'), ('disabled', '#dcdad5') ]) self.style.map('down.TButton', arrowcolor=[("disabled", "gray40")]) self.style.map('TMenubutton', arrowcolor=[('disabled', self.style.lookup('TMenubutton', 'foreground', ['disabled']))]) bg = self.style.lookup('TFrame', 'background', default='#ececec') self.configure(bg=bg) self.option_add('*Toplevel.background', bg) self.option_add('*Menu.background', bg) self.option_add('*Menu.tearOff', False) # toggle text self._open_image = PhotoImage(name='img_opened', file=IM_OPENED, master=self) self._closed_image = PhotoImage(name='img_closed', file=IM_CLOSED, master=self) self._open_image_sel = PhotoImage(name='img_opened_sel', file=IM_OPENED_SEL, master=self) self._closed_image_sel = PhotoImage(name='img_closed_sel', file=IM_CLOSED_SEL, master=self) self.style.element_create( "toggle", "image", "img_closed", ("selected", "!disabled", "img_opened"), ("active", "!selected", "!disabled", "img_closed_sel"), ("active", "selected", "!disabled", "img_opened_sel"), border=2, sticky='') self.style.map('Toggle', background=[]) self.style.layout('Toggle', [('Toggle.border', { 'children': [('Toggle.padding', { 'children': [('Toggle.toggle', { 'sticky': 'nswe' })], 'sticky': 'nswe' })], 'sticky': 'nswe' })]) # toggle sound self._im_sound = PhotoImage(master=self, file=IM_SOUND) self._im_mute = PhotoImage(master=self, file=IM_MUTE) self._im_sound_dis = PhotoImage(master=self, file=IM_SOUND_DIS) self._im_mute_dis = PhotoImage(master=self, file=IM_MUTE_DIS) self.style.element_create( 'mute', 'image', self._im_sound, ('selected', '!disabled', self._im_mute), ('selected', 'disabled', self._im_mute_dis), ('!selected', 'disabled', self._im_sound_dis), border=2, sticky='') self.style.layout('Mute', [('Mute.border', { 'children': [('Mute.padding', { 'children': [('Mute.mute', { 'sticky': 'nswe' })], 'sticky': 'nswe' })], 'sticky': 'nswe' })]) self.style.configure('Mute', relief='raised') # widget scrollbar self._im_trough = {} self._im_slider_vert = {} self._im_slider_vert_prelight = {} self._im_slider_vert_active = {} self._slider_alpha = Image.open(IM_SCROLL_ALPHA) for widget in ['Events', 'Tasks']: bg = CONFIG.get(widget, 'background', fallback='gray10') fg = CONFIG.get(widget, 'foreground') widget_bg = self.winfo_rgb(bg) widget_fg = tuple( round(c * 255 / 65535) for c in self.winfo_rgb(fg)) active_bg = active_color(*widget_bg) active_bg2 = active_color(*active_color(*widget_bg, 'RGB')) slider_vert = Image.new('RGBA', (13, 28), active_bg) slider_vert_active = Image.new('RGBA', (13, 28), widget_fg) slider_vert_prelight = Image.new('RGBA', (13, 28), active_bg2) self._im_trough[widget] = tkPhotoImage(width=15, height=15, master=self) self._im_trough[widget].put(" ".join( ["{" + " ".join([bg] * 15) + "}"] * 15)) self._im_slider_vert_active[widget] = PhotoImage( slider_vert_active, master=self) self._im_slider_vert[widget] = PhotoImage(slider_vert, master=self) self._im_slider_vert_prelight[widget] = PhotoImage( slider_vert_prelight, master=self) self.style.element_create('%s.Vertical.Scrollbar.trough' % widget, 'image', self._im_trough[widget]) self.style.element_create( '%s.Vertical.Scrollbar.thumb' % widget, 'image', self._im_slider_vert[widget], ('pressed', '!disabled', self._im_slider_vert_active[widget]), ('active', '!disabled', self._im_slider_vert_prelight[widget]), border=6, sticky='ns') self.style.layout( '%s.Vertical.TScrollbar' % widget, [('%s.Vertical.Scrollbar.trough' % widget, { 'children': [('%s.Vertical.Scrollbar.thumb' % widget, { 'expand': '1' })], 'sticky': 'ns' })]) # --- tree columns = { _('Summary'): ({ 'stretch': True, 'width': 300 }, lambda: self._sort_by_desc(_('Summary'), False)), _('Place'): ({ 'stretch': True, 'width': 200 }, lambda: self._sort_by_desc(_('Place'), False)), _('Start'): ({ 'stretch': False, 'width': 150 }, lambda: self._sort_by_date(_('Start'), False)), _('End'): ({ 'stretch': False, 'width': 150 }, lambda: self._sort_by_date(_("End"), False)), _('Category'): ({ 'stretch': False, 'width': 100 }, lambda: self._sort_by_desc(_('Category'), False)) } self.tree = Treeview(self, show="headings", columns=list(columns)) for label, (col_prop, cmd) in columns.items(): self.tree.column(label, **col_prop) self.tree.heading(label, text=label, anchor="w", command=cmd) self.tree.tag_configure('0', background='#ececec') self.tree.tag_configure('1', background='white') self.tree.tag_configure('outdated', foreground='red') scroll = AutoScrollbar(self, orient='vertical', command=self.tree.yview) self.tree.configure(yscrollcommand=scroll.set) # --- toolbar toolbar = Frame(self) self.img_plus = PhotoImage(master=self, file=IM_ADD) Button(toolbar, image=self.img_plus, padding=1, command=self.add).pack(side="left", padx=4) Label(toolbar, text=_("Filter by")).pack(side="left", padx=4) # --- TODO: add filter by start date (after date) self.filter_col = Combobox( toolbar, state="readonly", # values=("",) + self.tree.cget('columns')[1:], values=("", _("Category")), exportselection=False) self.filter_col.pack(side="left", padx=4) self.filter_val = Combobox(toolbar, state="readonly", exportselection=False) self.filter_val.pack(side="left", padx=4) Button(toolbar, text=_('Delete All Outdated'), padding=1, command=self.delete_outdated_events).pack(side="right", padx=4) # --- grid toolbar.grid(row=0, columnspan=2, sticky='we', pady=4) self.tree.grid(row=1, column=0, sticky='eswn') scroll.grid(row=1, column=1, sticky='ns') # --- restore data data = {} self.events = {} self.nb = 0 try: with open(DATA_PATH, 'rb') as file: dp = Unpickler(file) data = dp.load() except Exception: l = [ f for f in os.listdir(os.path.dirname(BACKUP_PATH)) if f.startswith('data.backup') ] if l: l.sort(key=lambda x: int(x[11:])) shutil.copy(os.path.join(os.path.dirname(BACKUP_PATH), l[-1]), DATA_PATH) with open(DATA_PATH, 'rb') as file: dp = Unpickler(file) data = dp.load() self.nb = len(data) backup() now = datetime.now() for i, prop in enumerate(data): iid = str(i) self.events[iid] = Event(self.scheduler, iid=iid, **prop) self.tree.insert('', 'end', iid, values=self.events[str(i)].values()) tags = [str(self.tree.index(iid) % 2)] self.tree.item(iid, tags=tags) if not prop['Repeat']: for rid, d in list(prop['Reminders'].items()): if d < now: del self.events[iid]['Reminders'][rid] self.after_id = self.after(15 * 60 * 1000, self.check_outdated) # --- bindings self.bind_class("TCombobox", "<<ComboboxSelected>>", self.clear_selection, add=True) self.bind_class("TCombobox", "<Control-a>", self.select_all) self.bind_class("TEntry", "<Control-a>", self.select_all) self.tree.bind('<3>', self._post_menu) self.tree.bind('<1>', self._select) self.tree.bind('<Double-1>', self._edit_on_click) self.menu.bind('<FocusOut>', lambda e: self.menu.unpost()) self.filter_col.bind("<<ComboboxSelected>>", self.update_filter_val) self.filter_val.bind("<<ComboboxSelected>>", self.apply_filter) # --- widgets self.widgets = {} prop = { op: CONFIG.get('Calendar', op) for op in CONFIG.options('Calendar') } self.widgets['Calendar'] = CalendarWidget(self, locale=CONFIG.get( 'General', 'locale'), **prop) self.widgets['Events'] = EventWidget(self) self.widgets['Tasks'] = TaskWidget(self) self.widgets['Timer'] = Timer(self) self.widgets['Pomodoro'] = Pomodoro(self) self._setup_style() for item, widget in self.widgets.items(): self.menu_widgets.add_checkbutton( label=_(item), command=lambda i=item: self.display_hide_widget(i)) self.menu_widgets.set_item_value(_(item), widget.variable.get()) add_trace(widget.variable, 'write', lambda *args, i=item: self._menu_widgets_trace(i)) self.icon.loop(self) self.tk.eval(""" apply {name { set newmap {} foreach {opt lst} [ttk::style map $name] { if {($opt eq "-foreground") || ($opt eq "-background")} { set newlst {} foreach {st val} $lst { if {($st eq "disabled") || ($st eq "selected")} { lappend newlst $st $val } } if {$newlst ne {}} { lappend newmap $opt $newlst } } else { lappend newmap $opt $lst } } ttk::style map $name {*}$newmap }} Treeview """) # react to scheduler --update-date in command line signal.signal(signal.SIGUSR1, self.update_date) # update selected date in calendar and event list every day self.scheduler.add_job(self.update_date, CronTrigger(hour=0, minute=0, second=1), jobstore='memo') self.scheduler.start() def _setup_style(self): # scrollbars for widget in ['Events', 'Tasks']: bg = CONFIG.get(widget, 'background', fallback='gray10') fg = CONFIG.get(widget, 'foreground', fallback='white') widget_bg = self.winfo_rgb(bg) widget_fg = tuple( round(c * 255 / 65535) for c in self.winfo_rgb(fg)) active_bg = active_color(*widget_bg) active_bg2 = active_color(*active_color(*widget_bg, 'RGB')) slider_vert = Image.new('RGBA', (13, 28), active_bg) slider_vert.putalpha(self._slider_alpha) slider_vert_active = Image.new('RGBA', (13, 28), widget_fg) slider_vert_active.putalpha(self._slider_alpha) slider_vert_prelight = Image.new('RGBA', (13, 28), active_bg2) slider_vert_prelight.putalpha(self._slider_alpha) self._im_trough[widget].put(" ".join( ["{" + " ".join([bg] * 15) + "}"] * 15)) self._im_slider_vert_active[widget].paste(slider_vert_active) self._im_slider_vert[widget].paste(slider_vert) self._im_slider_vert_prelight[widget].paste(slider_vert_prelight) for widget in self.widgets.values(): widget.update_style() def report_callback_exception(self, *args): err = ''.join(traceback.format_exception(*args)) logging.error(err) showerror('Exception', str(args[1]), err, parent=self) def save(self): logging.info('Save event database') data = [ev.to_dict() for ev in self.events.values()] with open(DATA_PATH, 'wb') as file: pick = Pickler(file) pick.dump(data) def update_date(self, *args): """Update Calendar's selected day and Events' list.""" self.widgets['Calendar'].update_date() self.widgets['Events'].display_evts() self.update_idletasks() # --- bindings def _select(self, event): if not self.tree.identify_row(event.y): self.tree.selection_remove(*self.tree.selection()) def _edit_on_click(self, event): sel = self.tree.selection() if sel: sel = sel[0] self.edit(sel) # --- class bindings @staticmethod def clear_selection(event): combo = event.widget combo.selection_clear() @staticmethod def select_all(event): event.widget.selection_range(0, "end") return "break" # --- show / hide def _menu_widgets_trace(self, item): self.menu_widgets.set_item_value(_(item), self.widgets[item].variable.get()) def display_hide_widget(self, item): value = self.menu_widgets.get_item_value(_(item)) if value: self.widgets[item].show() else: self.widgets[item].hide() def hide(self): self._visible.set(False) self.withdraw() self.save() def show(self): self._visible.set(True) self.deiconify() def _visibility_trace(self, *args): self.icon.menu.set_item_value(_('Manager'), self._visible.get()) def display_hide(self, toggle=False): value = self.icon.menu.get_item_value(_('Manager')) if toggle: value = not value self.icon.menu.set_item_value(_('Manager'), value) self._visible.set(value) if not value: self.withdraw() self.save() else: self.deiconify() # --- event management def event_add(self, event): self.nb += 1 iid = str(self.nb) self.events[iid] = event self.tree.insert('', 'end', iid, values=event.values()) self.tree.item(iid, tags=str(self.tree.index(iid) % 2)) self.widgets['Calendar'].add_event(event) self.widgets['Events'].display_evts() self.widgets['Tasks'].display_tasks() self.save() def event_configure(self, iid): self.tree.item(iid, values=self.events[iid].values()) self.widgets['Calendar'].add_event(self.events[iid]) self.widgets['Events'].display_evts() self.widgets['Tasks'].display_tasks() self.save() def add(self, date=None): iid = str(self.nb + 1) if date is not None: event = Event(self.scheduler, iid=iid, Start=date) else: event = Event(self.scheduler, iid=iid) Form(self, event, new=True) def delete(self, iid): index = self.tree.index(iid) self.tree.delete(iid) for k, item in enumerate(self.tree.get_children('')[index:]): tags = [ t for t in self.tree.item(item, 'tags') if t not in ['1', '0'] ] tags.append(str((index + k) % 2)) self.tree.item(item, tags=tags) self.events[iid].reminder_remove_all() self.widgets['Calendar'].remove_event(self.events[iid]) del (self.events[iid]) self.widgets['Events'].display_evts() self.widgets['Tasks'].display_tasks() self.save() def edit(self, iid): self.widgets['Calendar'].remove_event(self.events[iid]) Form(self, self.events[iid]) def check_outdated(self): """Check for outdated events every 15 min.""" now = datetime.now() for iid, event in self.events.items(): if not event['Repeat'] and event['Start'] < now: tags = list(self.tree.item(iid, 'tags')) if 'outdated' not in tags: tags.append('outdated') self.tree.item(iid, tags=tags) self.after_id = self.after(15 * 60 * 1000, self.check_outdated) def delete_outdated_events(self): now = datetime.now() outdated = [] for iid, prop in self.events.items(): if prop['End'] < now: if not prop['Repeat']: outdated.append(iid) elif prop['Repeat']['Limit'] != 'always': end = prop['End'] enddate = datetime.fromordinal( prop['Repeat']['EndDate'].toordinal()) enddate.replace(hour=end.hour, minute=end.minute) if enddate < now: outdated.append(iid) for item in outdated: self.delete(item) logging.info('Deleted outdated events') def refresh_reminders(self): """ Reschedule all reminders. Required when APScheduler is updated. """ for event in self.events.values(): reminders = [date for date in event['Reminders'].values()] event.reminder_remove_all() for date in reminders: event.reminder_add(date) logging.info('Refreshed reminders') # --- sorting def _move_item(self, item, index): self.tree.move(item, "", index) tags = [t for t in self.tree.item(item, 'tags') if t not in ['1', '0']] tags.append(str(index % 2)) self.tree.item(item, tags=tags) @staticmethod def to_datetime(date): date_format = get_date_format("short", CONFIG.get("General", "locale")).pattern dayfirst = date_format.startswith("d") yearfirst = date_format.startswith("y") return parse(date, dayfirst=dayfirst, yearfirst=yearfirst) def _sort_by_date(self, col, reverse): l = [(self.to_datetime(self.tree.set(k, col)), k) for k in self.tree.get_children('')] l.sort(reverse=reverse) # rearrange items in sorted positions for index, (val, k) in enumerate(l): self._move_item(k, index) # reverse sort next time self.tree.heading(col, command=lambda: self._sort_by_date(col, not reverse)) def _sort_by_desc(self, col, reverse): l = [(self.tree.set(k, col), k) for k in self.tree.get_children('')] l.sort(reverse=reverse, key=lambda x: x[0].lower()) # rearrange items in sorted positions for index, (val, k) in enumerate(l): self._move_item(k, index) # reverse sort next time self.tree.heading(col, command=lambda: self._sort_by_desc(col, not reverse)) # --- filter def update_filter_val(self, event): col = self.filter_col.get() self.filter_val.set("") if col: l = set() for k in self.events: l.add(self.tree.set(k, col)) self.filter_val.configure(values=tuple(l)) else: self.filter_val.configure(values=[]) self.apply_filter(event) def apply_filter(self, event): col = self.filter_col.get() val = self.filter_val.get() items = list(self.events.keys()) if not col: for item in items: self._move_item(item, int(item)) else: i = 0 for item in items: if self.tree.set(item, col) == val: self._move_item(item, i) i += 1 else: self.tree.detach(item) # --- manager's menu def _post_menu(self, event): self.right_click_iid = self.tree.identify_row(event.y) self.tree.selection_remove(*self.tree.selection()) self.tree.selection_add(self.right_click_iid) if self.right_click_iid: try: self.menu.delete(_('Progress')) except TclError: pass state = self.events[self.right_click_iid]['Task'] if state: self._task_var.set(state) if '%' in state: self._img_dot = PhotoImage(master=self, file=IM_DOT) else: self._img_dot = tkPhotoImage(master=self) self.menu_task.entryconfigure(1, image=self._img_dot) self.menu.insert_cascade(0, menu=self.menu_task, label=_('Progress')) self.menu.tk_popup(event.x_root, event.y_root) def _delete_menu(self): if self.right_click_iid: self.delete(self.right_click_iid) def _edit_menu(self): if self.right_click_iid: self.edit(self.right_click_iid) def _set_progress(self): if self.right_click_iid: self.events[self.right_click_iid]['Task'] = self._task_var.get() self.widgets['Tasks'].display_tasks() if '%' in self._task_var.get(): self._img_dot = PhotoImage(master=self, file=IM_DOT) else: self._img_dot = tkPhotoImage(master=self) self.menu_task.entryconfigure(1, image=self._img_dot) # --- icon menu def exit(self): self.save() rep = self.widgets['Pomodoro'].stop(self.widgets['Pomodoro'].on) if not rep: return self.menu_eyes.quit() self.after_cancel(self.after_id) try: self.scheduler.shutdown() except SchedulerNotRunningError: pass self.destroy() def settings(self): splash_supp = CONFIG.get('General', 'splash_supported', fallback=True) dialog = Settings(self) self.wait_window(dialog) self._setup_style() if splash_supp != CONFIG.get('General', 'splash_supported'): for widget in self.widgets.values(): widget.update_position() # --- week schedule def get_next_week_events(self): """Return events scheduled for the next 7 days """ locale = CONFIG.get("General", "locale") next_ev = {} today = datetime.now().date() for d in range(7): day = today + timedelta(days=d) evts = self.widgets['Calendar'].get_events(day) if evts: evts = [self.events[iid] for iid in evts] evts.sort(key=lambda ev: ev.get_start_time()) desc = [] for ev in evts: if ev["WholeDay"]: date = "" else: date = "%s - %s " % ( format_time(ev['Start'], locale=locale), format_time(ev['End'], locale=locale)) place = "(%s)" % ev['Place'] if place == "()": place = "" desc.append(("%s%s %s\n" % (date, ev['Summary'], place), ev['Description'])) next_ev[day.strftime('%A')] = desc return next_ev # --- tasks def get_tasks(self): # TODO: find events with repetition in the week # TODO: better handling of events on several days tasks = [] for event in self.events.values(): if event['Task']: tasks.append(event) return tasks
buttonPosition.configure(text='Left Window') else: buttonPosition.configure(text='Right Window') app.wm_attributes('-alpha', alpha) app.wm_attributes('-topmost', 1) app.resizable(0, 0) app.overrideredirect(1) app.protocol("WM_DELETE_WINDOW", on_closing) s = Style(fr) s.layout("LabeledProgressbar", [('LabeledProgressbar.trough', { 'children': [('LabeledProgressbar.pbar', { 'sticky': 'ns' }), ("LabeledProgressbar.label", { "sticky": "" })], 'sticky': 'nswe' })]) l1 = tk.Label(fr, borderwidth=3, relief="ridge") l1.place(x=int(ws * (24 / dw)), y=int(hs * (40 / dh))) # Progress bar widget progress = Progressbar(l1, orient='horizontal', length=int(ws * (220 / dw)), mode='determinate', style="LabeledProgressbar") progress.pack() s.configure("LabeledProgressbar",
class App: def __init__(self): self.root = Tk() self.s = Style() self.fr = Frame(self.root) self.fr.grid(sticky='nsew') self.img = PhotoImage("frameBorder", data=""" R0lGODlhPwA/APcAAAAAAJWVlby8vMTExAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAP8ALAAAAAA/AD8A AAj/AP8JHEiwoMGDCBMqXMiwocOHECNKnEixokWGATJq3Mixo8ePF/9pDLlw48SRJB2iVBkgpcSM LF2ebIlRJkWaCmHafIkTYc+dEH8aFAq0IVGBAnQWjRhAwMGkS086NQg1KtOpBatafdj06dGtPrES 1AoWo9iBZMvmPIv0q1qCXam6fSswbta5dO2OxftWL1q+av22pVuS7b+0hAsKPgy47GLEiQc+bgx2 cuSwXi8ftKxZsWHIlzl3lvyZ8lbRo0WWTk06M2vVrlmjHj27c23Nt0Ovfp07cu/EvwkH7zvZ9NKM pY0XRf40qXKbyA0zfi6TOUIBznE3ld5WaV7rCbGbNQysETtD7M5XLi9v3iH6j/Djy4/OXSH69PPz e0Qf8r7//wAG+J9LAxRo4IEIJqjggq81CFZAADs= """) self.img1 = PhotoImage("frameFocusBorder", data=""" R0lGODlhPwA/APcAAAAAAP9jR5WVlby8vMTExAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAP8ALAAAAAA/AD8A AAj/AP8JHEiwoMGDCBMqXMiwocOHECNKnEixokWGAjJq3Mixo8ePF/9pDECypMmTKFOm3DhxZICQ C0tqhJiRJMyHJDM6rPnyJs4AOjHa9AkxJ0YBPYkWDZoQqdKJQBc6fRoxKsIBNalKBDrgINakWnEK 6Grwa9iqY71OPeuQq1qwbGOmLbs2rlyyBc3aZeiWLty9B/vmrQs48NzBfwsTFExQr2LDeBsTfjyQ 8UDHlBcflpyYsmWBmDML/PwvtGjSpjOjnqx682XWnl2Dhv14defaskvTVmxbdMHevivnTh078uvb vIfvLgw8+L/mwaH7ln5aOXLm1p2Pzq6demvjs6/vaQWqfLld8uB1m4+L3ivW9WHRp1c/tHb7q+/r A845dv5snsyRl1tj7/Ek3kX8ZTSAf5ctyJFKEEao0kYLMpiXgx9lqOGG/VmIH4YchvhRhSFVaOKJ KKaoIok3EeDiizDGKOOMNGpno2IBAQA7 """) self.s.element_create("RoundedFrame", "image", "frameBorder", ("focus", "frameFocusBorder"), border=16, sticky="nsew") self.s.layout("RoundedFrame", [("RoundedFrame", {"sticky": "nsew"})]) self.build() 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 enabled(self, event): # from widget selection self.lf6.state(["!focus"]) if self.ord.get() == 0: if self.widget_value.get() in ('Progressbar', 'Scale', 'Scrollbar'): self.lF5.state(["focus"]) for opt in self.rbs: opt.state(['!disabled']) for opt in self.rbs1: opt.state(['disabled']) else: for opt in self.rbs1: opt.state(['!disabled']) self.lF1.state(["focus"]) self.lF1['text'] = 'Select Theme' self.theme_value.set(None) if self.ord.get() == 1: self.lf6['text'] = 'Widget' if self.widget_value.get() in ('Progressbar', 'Scale', 'Scrollbar'): self.lF5.state(["focus"]) for opt in self.rbs: opt.state(['!disabled']) def orient_command(self): # from orient selection self.lF5.state(["!focus"]) if self.ord.get() == 0: try: for opt in self.rbs1: opt.state(['!disabled']) self.lF1.state(["focus"]) self.theme_value.set(None) self.lF1['text'] = 'Select Theme' except (NameError, AttributeError): pass def theme_command(self): # from theme selection self.s.theme_use(self.theme_value.get()) self.lF1.state(["!focus"]) if self.ord.get() == 0: self.lF1['text'] = 'Theme' if self.ord.get() == 1: self.cb.state(['!disabled']) self.lF1['text'] = 'Theme' self.lf6.state(["focus"]) def selord(self): # from select ord if self.ord.get() == 0: self.lf6.state(["focus"]) self.lF12['text'] = 'Select widget before theme' self.theme_value.set(None) self.orient.set(None) self.cb.set('') self.lF1.state(["!focus"]) self.lF5.state(["!focus"]) if self.ord.get() == 1: self.lF12['text'] = 'Select theme before widget' self.cb.state(['disabled']) for opt in self.rbs1: opt.state(['!disabled']) self.lF1.state(["focus"]) self.theme_value.set(None) self.orient.set(None) self.cb.set('') self.lf6.state(["!focus"]) self.lF5.state(["!focus"])
class App(Tk): def __init__(self): Tk.__init__(self, className=cst.APP_NAME) self.protocol("WM_DELETE_WINDOW", self.quit) self.withdraw() logging.info('Starting %s', cst.APP_NAME) self.im_icon = PhotoImage(master=self, file=cst.IM_ICON_48) self.iconphoto(True, self.im_icon) # --- style self.style = Style(self) self.style.theme_use("clam") self.style.configure("TScale", sliderlength=20) self.style.map("TCombobox", fieldbackground=[('readonly', 'white')], selectbackground=[('readonly', 'white')], selectforeground=[('readonly', 'black')]) self.style.configure("title.TLabel", font="TkDefaultFont 9 bold") self.style.configure("white.TLabel", background="white") self.style.map("white.TLabel", background=[("active", "white")]) self.style.configure('heading.TLabel', relief='ridge', borderwidth=1, padding=(10, 4)) self.style.configure('manager.TButton', padding=0) self.style.map('manager.Treeview', background=[], foreground=[]) self.style.layout( 'no_edit.TEntry', [('Entry.padding', { 'children': [('Entry.textarea', { 'sticky': 'nswe' })], 'sticky': 'nswe' })]) self.style.configure('no_edit.TEntry', background='white', padding=[4, 0]) self.style.configure('manager.TEntry', padding=[2, 1]) self.style.layout('manager.Treeview.Row', [('Treeitem.row', { 'sticky': 'nswe' }), ('Treeitem.image', { 'side': 'right', 'sticky': 'e' })]) self.style.layout('manager.Treeview.Item', [('Treeitem.padding', { 'children': [('Checkbutton.indicator', { 'side': 'left', 'sticky': '' }), ('Treeitem.text', { 'side': 'left', 'sticky': '' })], 'sticky': 'nswe' })]) self._im_trough = tkPhotoImage(name='trough-scrollbar-vert', width=15, height=15, master=self) bg = CONFIG.get("Widget", 'background', fallback='gray10') widget_bg = (0, 0, 0) widget_fg = (255, 255, 255) vmax = self.winfo_rgb('white')[0] color = tuple(int(val / vmax * 255) for val in widget_bg) active_bg = cst.active_color(color) active_bg2 = cst.active_color(cst.active_color(color, 'RGB')) slider_vert_insens = Image.new('RGBA', (13, 28), widget_bg) slider_vert = Image.new('RGBA', (13, 28), active_bg) slider_vert_active = Image.new('RGBA', (13, 28), widget_fg) slider_vert_prelight = Image.new('RGBA', (13, 28), active_bg2) self._im_trough.put(" ".join(["{" + " ".join([bg] * 15) + "}"] * 15)) self._im_slider_vert_active = PhotoImage(slider_vert_active, name='slider-vert-active', master=self) self._im_slider_vert = PhotoImage(slider_vert, name='slider-vert', master=self) self._im_slider_vert_prelight = PhotoImage(slider_vert_prelight, name='slider-vert-prelight', master=self) self._im_slider_vert_insens = PhotoImage(slider_vert_insens, name='slider-vert-insens', master=self) self.style.element_create('widget.Vertical.Scrollbar.trough', 'image', 'trough-scrollbar-vert') self.style.element_create( 'widget.Vertical.Scrollbar.thumb', 'image', 'slider-vert', ('pressed', '!disabled', 'slider-vert-active'), ('active', '!disabled', 'slider-vert-prelight'), ('disabled', 'slider-vert-insens'), border=6, sticky='ns') self.style.layout('widget.Vertical.TScrollbar', [ ('widget.Vertical.Scrollbar.trough', { 'children': [('widget.Vertical.Scrollbar.thumb', { 'expand': '1' })], 'sticky': 'ns' }) ]) hide = Image.new('RGBA', (12, 12), active_bg2) hide_active = Image.new('RGBA', (12, 12), widget_fg) hide_pressed = Image.new('RGBA', (12, 12), (150, 0, 0)) toggle_open = Image.new('RGBA', (9, 9), widget_fg) toggle_open_active = Image.new('RGBA', (9, 9), active_bg2) toggle_close = Image.new('RGBA', (9, 9), widget_fg) toggle_close_active = Image.new('RGBA', (9, 9), active_bg2) self._im_hide = PhotoImage(hide, master=self) self._im_hide_active = PhotoImage(hide_active, master=self) self._im_hide_pressed = PhotoImage(hide_pressed, master=self) self._im_open = PhotoImage(toggle_open, master=self) self._im_open_active = PhotoImage(toggle_open_active, master=self) self._im_close = PhotoImage(toggle_close, master=self) self._im_close_active = PhotoImage(toggle_close_active, master=self) self.style.element_create( "toggle", "image", self._im_close, ("!hover", "selected", "!disabled", self._im_open), ("hover", "!selected", "!disabled", self._im_close_active), ("hover", "selected", "!disabled", self._im_open_active), border=2, sticky='') self.style.layout('Toggle', [('Toggle.border', { 'children': [('Toggle.padding', { 'children': [('Toggle.toggle', { 'sticky': 'nswe' })], 'sticky': 'nswe' })], 'sticky': 'nswe' })]) self.style.configure('widget.close.TButton', background=bg, relief='flat', image=self._im_hide, padding=0) self.style.map('widget.close.TButton', background=[], relief=[], image=[('active', '!pressed', self._im_hide_active), ('active', 'pressed', self._im_hide_pressed)]) self.option_add('*Toplevel.background', self.style.lookup('TFrame', 'background')) self.option_add('*{app_name}.background'.format(app_name=cst.APP_NAME), self.style.lookup('TFrame', 'background')) self.widget_style_init() # --- tray icon menu self.icon = TrayIcon(cst.ICON) self.menu_widgets = SubMenu(parent=self.icon.menu) self.menu_categories = SubMenu(parent=self.menu_widgets) self.menu_categories.add_command(label=_('Hide all'), command=self.hide_all_cats) self.menu_categories.add_command(label=_('Show all'), command=self.hide_all_cats) self.menu_categories.add_separator() self.menu_feeds = SubMenu(parent=self.menu_widgets) self.menu_feeds.add_command(label=_('Hide all'), command=self.hide_all_feeds) self.menu_feeds.add_command(label=_('Show all'), command=self.show_all_feeds) self.menu_feeds.add_separator() self.menu_widgets.add_command(label=_('Hide all'), command=self.hide_all) self.menu_widgets.add_command(label=_('Show all'), command=self.show_all) self.menu_widgets.add_separator() self.menu_widgets.add_cascade(label=_('Categories'), menu=self.menu_categories) self.menu_widgets.add_cascade(label=_('Feeds'), menu=self.menu_feeds) self.icon.menu.add_cascade(label=_('Widgets'), menu=self.menu_widgets) self.icon.menu.add_command(label=_('Add feed'), command=self.add) self.icon.menu.add_command(label=_('Update feeds'), command=self.feed_update) self.icon.menu.add_command(label=_('Manage feeds'), command=self.feed_manage) self.icon.menu.add_command(label=_("Suspend"), command=self.start_stop) self.icon.menu.add_separator() self.icon.menu.add_command(label=_('Settings'), command=self.settings) self.icon.menu.add_command(label=_("Check for updates"), command=lambda: UpdateChecker(self, True)) self.icon.menu.add_command(label=_("Help"), command=lambda: Help(self)) self.icon.menu.add_command(label=_("About"), command=lambda: About(self)) self.icon.menu.add_command(label=_('Quit'), command=self.quit) self.icon.loop(self) self._notify_no_internet = True self._internet_id = "" self._update_id = "" self._check_add_id = "" self._check_end_update_id = "" self._check_result_update_id = {} self._check_result_init_id = {} self.queues = {} self.threads = {} # --- category widgets self.cat_widgets = {} self.cat_widgets['All'] = CatWidget(self, 'All') self.cat_widgets['All'].event_generate('<Configure>') self.menu_widgets.add_checkbutton(label=_('Latests'), command=self.toggle_latests_widget) cst.add_trace(self.cat_widgets['All'].variable, 'write', self.latests_widget_trace) self.cat_widgets['All'].variable.set( LATESTS.getboolean('All', 'visible')) cats = LATESTS.sections() cats.remove('All') for category in cats: self.cat_widgets[category] = CatWidget(self, category) self.cat_widgets[category].event_generate('<Configure>') self.menu_categories.add_checkbutton( label=category, command=lambda c=category: self.toggle_category_widget(c)) cst.add_trace(self.cat_widgets[category].variable, 'write', lambda *args, c=category: self.cat_widget_trace(c)) self.cat_widgets[category].variable.set( LATESTS.getboolean(category, 'visible')) # --- feed widgets self.feed_widgets = {} for title in FEEDS.sections(): self._check_result_update_id[title] = '' self._check_result_init_id[title] = '' self.queues[title] = Queue(1) self.threads[title] = None self.menu_feeds.add_checkbutton( label=title, command=lambda t=title: self.toggle_feed_widget(t)) self.feed_widgets[title] = FeedWidget(self, title) cst.add_trace(self.feed_widgets[title].variable, 'write', lambda *args, t=title: self.feed_widget_trace(t)) self.feed_widgets[title].variable.set( FEEDS.getboolean(title, 'visible', fallback=True)) self.feed_init() # --- check for updates if CONFIG.getboolean("General", "check_update"): UpdateChecker(self) self.bind_class('TEntry', '<Control-a>', self.entry_select_all) def widget_style_init(self): """Init widgets style.""" bg = CONFIG.get('Widget', 'background', fallback='gray10') feed_bg = CONFIG.get('Widget', 'feed_background', fallback='gray20') fg = CONFIG.get('Widget', 'foreground') vmax = self.winfo_rgb('white')[0] widget_bg = tuple(int(val / vmax * 255) for val in self.winfo_rgb(bg)) widget_fg = tuple(int(val / vmax * 255) for val in self.winfo_rgb(fg)) active_bg = cst.active_color(widget_bg) active_bg2 = cst.active_color(cst.active_color(widget_bg, 'RGB')) slider_alpha = Image.open(cst.IM_SCROLL_ALPHA) slider_vert_insens = Image.new('RGBA', (13, 28), widget_bg) slider_vert = Image.new('RGBA', (13, 28), active_bg) slider_vert.putalpha(slider_alpha) slider_vert_active = Image.new('RGBA', (13, 28), widget_fg) slider_vert_active.putalpha(slider_alpha) slider_vert_prelight = Image.new('RGBA', (13, 28), active_bg2) slider_vert_prelight.putalpha(slider_alpha) self._im_slider_vert_active.paste(slider_vert_active) self._im_slider_vert.paste(slider_vert) self._im_slider_vert_prelight.paste(slider_vert_prelight) self._im_slider_vert_insens.paste(slider_vert_insens) self._im_trough.put(" ".join(["{" + " ".join([bg] * 15) + "}"] * 15)) hide_alpha = Image.open(cst.IM_HIDE_ALPHA) hide = Image.new('RGBA', (12, 12), active_bg) hide.putalpha(hide_alpha) hide_active = Image.new('RGBA', (12, 12), active_bg2) hide_active.putalpha(hide_alpha) hide_pressed = Image.new('RGBA', (12, 12), widget_fg) hide_pressed.putalpha(hide_alpha) toggle_open_alpha = Image.open(cst.IM_OPENED_ALPHA) toggle_open = Image.new('RGBA', (9, 9), widget_fg) toggle_open.putalpha(toggle_open_alpha) toggle_open_active = Image.new('RGBA', (9, 9), active_bg2) toggle_open_active.putalpha(toggle_open_alpha) toggle_close_alpha = Image.open(cst.IM_CLOSED_ALPHA) toggle_close = Image.new('RGBA', (9, 9), widget_fg) toggle_close.putalpha(toggle_close_alpha) toggle_close_active = Image.new('RGBA', (9, 9), active_bg2) toggle_close_active.putalpha(toggle_close_alpha) self._im_hide.paste(hide) self._im_hide_active.paste(hide_active) self._im_hide_pressed.paste(hide_pressed) self._im_open.paste(toggle_open) self._im_open_active.paste(toggle_open_active) self._im_close.paste(toggle_close) self._im_close_active.paste(toggle_close_active) self.style.configure('widget.TFrame', background=bg) self.style.configure('widget.close.TButton', background=bg) # relief='flat', image=self._im_hide, padding=0) # self.style.map('widget.close.TButton', background=[], relief=[], # image=[('active', '!pressed', self._im_hide_active), # ('active', 'pressed', self._im_hide_pressed)]) self.style.configure('widget.interior.TFrame', background=feed_bg) self.style.configure('widget.TSizegrip', background=bg) self.style.configure('widget.Horizontal.TSeparator', background=bg) self.style.configure('widget.TLabel', background=bg, foreground=fg, font=CONFIG.get('Widget', 'font')) self.style.configure('widget.title.TLabel', background=bg, foreground=fg, font=CONFIG.get('Widget', 'font_title')) self.style.configure('widget.TButton', background=bg, foreground=fg, padding=1, relief='flat') self.style.map('widget.TButton', background=[('disabled', active_bg), ('pressed', fg), ('active', active_bg)], foreground=[('pressed', bg)]) # relief=[('pressed', 'sunken')]) # bordercolor=[('pressed', active_bg)], # darkcolor=[('pressed', bg)], # lightcolor=[('pressed', fg)]) self.update_idletasks() def hide_all(self): """Withdraw all widgets.""" for widget in self.cat_widgets.values(): widget.withdraw() for widget in self.feed_widgets.values(): widget.withdraw() def show_all(self): """Deiconify all widgets.""" for widget in self.cat_widgets.values(): widget.deiconify() for widget in self.feed_widgets.values(): widget.deiconify() def hide_all_feeds(self): """Withdraw all feed widgets.""" for widget in self.feed_widgets.values(): widget.withdraw() def show_all_feeds(self): """Deiconify all feed widgets.""" for widget in self.feed_widgets.values(): widget.deiconify() def hide_all_cats(self): """Withdraw all category widgets.""" for cat, widget in self.cat_widgets.items(): if cat != 'All': widget.withdraw() def show_all_cats(self): """Deiconify all category widgets.""" for cat, widget in self.cat_widgets.items(): if cat != 'All': widget.deiconify() def start_stop(self): """Suspend / restart update checks.""" if self.icon.menu.get_item_label(4) == _("Suspend"): after_ids = [ self._update_id, self._check_add_id, self._internet_id, self._check_end_update_id, self._update_id ] after_ids.extend(self._check_result_update_id.values()) after_ids.extend(self._check_result_init_id.values()) for after_id in after_ids: try: self.after_cancel(after_id) except ValueError: pass self.icon.menu.set_item_label(4, _("Restart")) self.icon.menu.disable_item(1) self.icon.menu.disable_item(2) self.icon.change_icon(cst.ICON_DISABLED, 'feedagregator suspended') else: self.icon.menu.set_item_label(4, _("Suspend")) self.icon.menu.enable_item(1) self.icon.menu.enable_item(2) self.icon.change_icon(cst.ICON, 'feedagregator') for widget in self.feed_widgets.values(): widget.clear() self.feed_init() @staticmethod def entry_select_all(event): event.widget.selection_clear() event.widget.selection_range(0, 'end') def test_connection(self): """ Launch update check if there is an internet connection otherwise check again for an internet connection after 30s. """ if cst.internet_on(): logging.info('Connected to Internet') self._notify_no_internet = True for widget in self.feed_widgets.values(): widget.clear() self.feed_init() else: self._internet_id = self.after(30000, self.test_connection) def quit(self): for after_id in self.tk.call('after', 'info'): try: self.after_cancel(after_id) except ValueError: pass for thread in self.threads.values(): try: thread.terminate() except AttributeError: pass for title, widget in self.feed_widgets.items(): FEEDS.set(title, 'visible', str(widget.variable.get())) for cat, widget in self.cat_widgets.items(): LATESTS.set(cat, 'visible', str(widget.variable.get())) try: self.destroy() except TclError: logging.error("Error on quit") self.after(500, self.quit) def feed_widget_trace(self, title): value = self.feed_widgets[title].variable.get() self.menu_feeds.set_item_value(title, value) FEEDS.set(title, 'visible', str(value)) cst.save_feeds() def cat_widget_trace(self, category): value = self.cat_widgets[category].variable.get() self.menu_categories.set_item_value(category, value) LATESTS.set(category, 'visible', str(value)) cst.save_latests() def latests_widget_trace(self, *args): value = self.cat_widgets['All'].variable.get() self.menu_widgets.set_item_value(_('Latests'), value) LATESTS.set('All', 'visible', str(value)) cst.save_latests() def toggle_category_widget(self, category): value = self.menu_categories.get_item_value(category) if value: self.cat_widgets[category].deiconify() else: self.cat_widgets[category].withdraw() self.update_idletasks() def toggle_latests_widget(self): value = self.menu_widgets.get_item_value(_('Latests')) if value: self.cat_widgets['All'].deiconify() else: self.cat_widgets['All'].withdraw() self.update_idletasks() def toggle_feed_widget(self, title): value = self.menu_feeds.get_item_value(title) if value: self.feed_widgets[title].deiconify() else: self.feed_widgets[title].withdraw() self.update_idletasks() def report_callback_exception(self, *args): """Log exceptions.""" err = "".join(traceback.format_exception(*args)) logging.error(err) showerror(_("Error"), str(args[1]), err, True) def settings(self): update_delay = CONFIG.get('General', 'update_delay') splash_supp = CONFIG.get('General', 'splash_supported', fallback=True) dialog = Config(self) self.wait_window(dialog) cst.save_config() self.widget_style_init() splash_change = splash_supp != CONFIG.get('General', 'splash_supported') for widget in self.cat_widgets.values(): widget.update_style() if splash_change: widget.update_position() for widget in self.feed_widgets.values(): widget.update_style() if splash_change: widget.update_position() if update_delay != CONFIG.get('General', 'update_delay'): self.feed_update() def add(self): dialog = Add(self) self.wait_window(dialog) url = dialog.url self.feed_add(url) def category_remove(self, category): self.cat_widgets[category].destroy() del self.cat_widgets[category] self.menu_categories.delete(category) LATESTS.remove_section(category) cst.save_feeds() cst.save_latests() @staticmethod def feed_get_info(url, queue, mode='latest'): feed = feedparser.parse(url) feed_title = feed['feed'].get('title', '') entries = feed['entries'] today = datetime.now().strftime('%Y-%m-%d %H:%M') if entries: entry_title = entries[0].get('title', '') summary = entries[0].get('summary', '') link = entries[0].get('link', '') latest = """<p id=title>{}</p>\n{}""".format(entry_title, summary) if 'updated' in entries[0]: updated = entries[0].get('updated') else: updated = entries[0].get('published', today) updated = dateutil.parser.parse( updated, tzinfos=cst.TZINFOS).strftime('%Y-%m-%d %H:%M') else: entry_title = "" summary = "" link = "" latest = "" updated = today if mode == 'all': data = [] for entry in entries: title = entry.get('title', '') summary = entry.get('summary', '') if 'updated' in entry: date = entry.get('updated') else: date = entry.get('published', today) date = dateutil.parser.parse( date, tzinfos=cst.TZINFOS).strftime('%Y-%m-%d %H:%M') link = entry.get('link', '') data.append((title, date, summary, link)) queue.put((feed_title, latest, updated, data)) else: queue.put( (feed_title, latest, updated, entry_title, summary, link)) def _check_result_add(self, thread, queue, url, manager_queue=None): if thread.is_alive(): self._check_add_id = self.after(1000, self._check_result_add, thread, queue, url, manager_queue) else: title, latest, date, data = queue.get(False) if title: try: # check if feed's title already exists FEEDS.add_section(title) except configparser.DuplicateSectionError: i = 2 duplicate = True while duplicate: # increment i until title~#i does not already exist try: FEEDS.add_section("{}~#{}".format(title, i)) except configparser.DuplicateSectionError: i += 1 else: duplicate = False name = "{}~#{}".format(title, i) else: name = title if manager_queue is not None: manager_queue.put(name) logging.info("Added feed '%s' %s", name, url) if CONFIG.getboolean("General", "notifications", fallback=True): run([ "notify-send", "-i", cst.IM_ICON_SVG, name, cst.html2text(latest) ]) self.cat_widgets['All'].entry_add(name, date, latest, url) filename = cst.new_data_file() cst.save_data(filename, latest, data) FEEDS.set(name, 'url', url) FEEDS.set(name, 'updated', date) FEEDS.set(name, 'data', filename) FEEDS.set(name, 'visible', 'True') FEEDS.set(name, 'geometry', '') FEEDS.set(name, 'position', 'normal') FEEDS.set(name, 'category', '') FEEDS.set(name, 'sort_is_reversed', 'False') FEEDS.set(name, 'active', 'True') cst.save_feeds() self.queues[name] = queue self.feed_widgets[name] = FeedWidget(self, name) self.menu_feeds.add_checkbutton( label=name, command=lambda: self.toggle_feed_widget(name)) cst.add_trace(self.feed_widgets[name].variable, 'write', lambda *args: self.feed_widget_trace(name)) self.feed_widgets[name].variable.set(True) for entry_title, date, summary, link in data: self.feed_widgets[name].entry_add(entry_title, date, summary, link, -1) else: if manager_queue is not None: manager_queue.put('') if cst.internet_on(): logging.error('%s is not a valid feed.', url) showerror(_('Error'), _('{url} is not a valid feed.').format(url=url)) else: logging.warning('No Internet connection.') showerror(_('Error'), _('No Internet connection.')) def feed_add(self, url, manager=False): """ Add feed with given url. manager: whether this command is run from the feed manager. """ if url: queue = Queue(1) manager_queue = Queue(1) if manager else None thread = Process(target=self.feed_get_info, args=(url, queue, 'all'), daemon=True) thread.start() self._check_result_add(thread, queue, url, manager_queue) if manager: return manager_queue def feed_set_active(self, title, active): FEEDS.set(title, 'active', str(active)) cst.save_feeds() cat = FEEDS.get(title, 'category', fallback='') if active: self.menu_feeds.enable_item(title) if FEEDS.getboolean(title, 'visible'): self.feed_widgets[title].deiconify() if cat != '': self.cat_widgets[cat].show_feed(title) self.cat_widgets['All'].show_feed(title) self._feed_update(title) else: self.menu_feeds.disable_item(title) self.feed_widgets[title].withdraw() if cat != '': self.cat_widgets[cat].hide_feed(title) self.cat_widgets['All'].hide_feed(title) def feed_change_cat(self, title, old_cat, new_cat): if old_cat != new_cat: FEEDS.set(title, 'category', new_cat) if old_cat != '': self.cat_widgets[old_cat].remove_feed(title) if new_cat != '': if new_cat not in LATESTS.sections(): LATESTS.add_section(new_cat) LATESTS.set(new_cat, 'visible', 'True') LATESTS.set(new_cat, 'geometry', '') LATESTS.set(new_cat, 'position', 'normal') LATESTS.set(new_cat, 'sort_order', 'A-Z') self.cat_widgets[new_cat] = CatWidget(self, new_cat) self.cat_widgets[new_cat].event_generate('<Configure>') self.menu_categories.add_checkbutton( label=new_cat, command=lambda: self.toggle_category_widget(new_cat)) cst.add_trace(self.cat_widgets[new_cat].variable, 'write', lambda *args: self.cat_widget_trace(new_cat)) self.cat_widgets[new_cat].variable.set(True) else: try: filename = FEEDS.get(title, 'data') latest = cst.feed_get_latest(filename) except (configparser.NoOptionError, pickle.UnpicklingError): latest = '' self.cat_widgets[new_cat].entry_add( title, FEEDS.get(title, 'updated'), latest, FEEDS.get(title, 'url')) def feed_rename(self, old_name, new_name): options = { opt: FEEDS.get(old_name, opt) for opt in FEEDS.options(old_name) } FEEDS.remove_section(old_name) try: # check if feed's title already exists FEEDS.add_section(new_name) except configparser.DuplicateSectionError: i = 2 duplicate = True while duplicate: # increment i until new_name~#i does not already exist try: FEEDS.add_section("{}~#{}".format(new_name, i)) except configparser.DuplicateSectionError: i += 1 else: duplicate = False name = "{}~#{}".format(new_name, i) else: name = new_name logging.info("Renamed feed '%s' to '%s'", old_name, name) for opt, val in options.items(): FEEDS.set(name, opt, val) self._check_result_init_id[name] = self._check_result_init_id.pop( old_name, '') self._check_result_update_id[name] = self._check_result_update_id.pop( old_name, '') self.threads[name] = self.threads.pop(old_name, None) self.queues[name] = self.queues.pop(old_name) self.feed_widgets[name] = self.feed_widgets.pop(old_name) self.feed_widgets[name].rename_feed(name) self.cat_widgets['All'].rename_feed(old_name, name) category = FEEDS.get(name, 'category', fallback='') if category != '': self.cat_widgets[category].rename_feed(old_name, name) self.menu_feeds.delete(old_name) self.menu_feeds.add_checkbutton( label=name, command=lambda: self.toggle_feed_widget(name)) trace_info = cst.info_trace(self.feed_widgets[name].variable) if trace_info: cst.remove_trace(self.feed_widgets[name].variable, 'write', trace_info[0][1]) cst.add_trace(self.feed_widgets[name].variable, 'write', lambda *args: self.feed_widget_trace(name)) self.menu_feeds.set_item_value(name, self.feed_widgets[name].variable.get()) cst.save_feeds() return name def feed_remove(self, title): self.feed_widgets[title].destroy() del self.queues[title] try: del self.threads[title] except KeyError: pass del self.feed_widgets[title] try: del self._check_result_init_id[title] except KeyError: pass try: del self._check_result_update_id[title] except KeyError: pass try: os.remove(os.path.join(cst.PATH_DATA, FEEDS.get(title, 'data'))) except FileNotFoundError: pass self.menu_feeds.delete(title) logging.info("Removed feed '%s' %s", title, FEEDS.get(title, 'url')) category = FEEDS.get(title, 'category', fallback='') self.cat_widgets['All'].remove_feed(title) if category != '': self.cat_widgets[category].remove_feed(title) FEEDS.remove_section(title) def feed_manage(self): dialog = Manager(self) self.wait_window(dialog) self.update_idletasks() cst.save_latests() if dialog.change_made: cst.save_feeds() self.feed_update() def feed_init(self): """Update feeds.""" for title in FEEDS.sections(): if FEEDS.getboolean(title, 'active', fallback=True): logging.info("Updating feed '%s'", title) self.threads[title] = Process(target=self.feed_get_info, args=(FEEDS.get(title, 'url'), self.queues[title], 'all'), daemon=True) self.threads[title].start() self._check_result_init(title) self._check_end_update_id = self.after(2000, self._check_end_update) def _check_result_init(self, title): if self.threads[title].is_alive(): self._check_result_init_id[title] = self.after( 1000, self._check_result_init, title) else: t, latest, updated, data = self.queues[title].get() if not t: if cst.internet_on(): run([ "notify-send", "-i", "dialog-error", _("Error"), _('{url} is not a valid feed.').format( url=FEEDS.get(title, 'url')) ]) logging.error('%s is not a valid feed.', FEEDS.get(title, 'url')) else: if self._notify_no_internet: run([ "notify-send", "-i", "dialog-error", _("Error"), _('No Internet connection.') ]) logging.warning('No Internet connection') self._notify_no_internet = False self._internet_id = self.after(30000, self.test_connection) after_ids = [ self._update_id, self._check_add_id, self._check_end_update_id, self._update_id ] after_ids.extend(self._check_result_update_id.values()) after_ids.extend(self._check_result_init_id.values()) for after_id in after_ids: try: self.after_cancel(after_id) except ValueError: pass else: date = datetime.strptime(updated, '%Y-%m-%d %H:%M') if (date > datetime.strptime(FEEDS.get(title, 'updated'), '%Y-%m-%d %H:%M') or not FEEDS.has_option(title, 'data')): if CONFIG.getboolean("General", "notifications", fallback=True): run([ "notify-send", "-i", cst.IM_ICON_SVG, title, cst.html2text(latest) ]) FEEDS.set(title, 'updated', updated) category = FEEDS.get(title, 'category', fallback='') self.cat_widgets['All'].update_display( title, latest, updated) if category != '': self.cat_widgets[category].update_display( title, latest, updated) logging.info("Updated feed '%s'", title) self.feed_widgets[title].clear() for entry_title, date, summary, link in data: self.feed_widgets[title].entry_add( entry_title, date, summary, link, -1) logging.info("Populated widget for feed '%s'", title) self.feed_widgets[title].event_generate('<Configure>') self.feed_widgets[title].sort_by_date() try: filename = FEEDS.get(title, 'data') except configparser.NoOptionError: filename = cst.new_data_file() FEEDS.set(title, 'data', filename) cst.save_feeds() cst.save_data(filename, latest, data) else: logging.info("Feed '%s' is up-to-date", title) def _feed_update(self, title): """Update feed with given title.""" logging.info("Updating feed '%s'", title) self.threads[title] = Process(target=self.feed_get_info, args=(FEEDS.get(title, 'url'), self.queues[title]), daemon=True) self.threads[title].start() self._check_result_update(title) def feed_update(self): """Update all feeds.""" try: self.after_cancel(self._update_id) except ValueError: pass for thread in self.threads.values(): try: thread.terminate() except AttributeError: pass self.threads.clear() for title in FEEDS.sections(): if FEEDS.getboolean(title, 'active', fallback=True): self._feed_update(title) self._check_end_update_id = self.after(2000, self._check_end_update) def _check_result_update(self, title): if self.threads[title].is_alive(): self._check_result_update_id[title] = self.after( 1000, self._check_result_update, title) else: t, latest, updated, entry_title, summary, link = self.queues[ title].get(False) if not t: if cst.internet_on(): run([ "notify-send", "-i", "dialog-error", _("Error"), _('{url} is not a valid feed.').format( url=FEEDS.get(title, 'url')) ]) logging.error('%s is not a valid feed.', FEEDS.get(title, 'url')) else: if self._notify_no_internet: run([ "notify-send", "-i", "dialog-error", _("Error"), _('No Internet connection.') ]) logging.warning('No Internet connection') self._notify_no_internet = False self._internet_id = self.after(30000, self.test_connection) after_ids = [ self._update_id, self._check_add_id, self._check_end_update_id, self._update_id ] after_ids.extend(self._check_result_update_id.values()) after_ids.extend(self._check_result_init_id.values()) for after_id in after_ids: try: self.after_cancel(after_id) except ValueError: pass else: date = datetime.strptime(updated, '%Y-%m-%d %H:%M') if date > datetime.strptime(FEEDS.get(title, 'updated'), '%Y-%m-%d %H:%M'): logging.info("Updated feed '%s'", title) if CONFIG.getboolean("General", "notifications", fallback=True): run([ "notify-send", "-i", cst.IM_ICON_SVG, title, cst.html2text(latest) ]) FEEDS.set(title, 'updated', updated) category = FEEDS.get(title, 'category', fallback='') self.cat_widgets['All'].update_display( title, latest, updated) if category != '': self.cat_widgets[category].update_display( title, latest, updated) self.feed_widgets[title].entry_add(entry_title, updated, summary, link, 0) self.feed_widgets[title].sort_by_date() try: filename = FEEDS.get(title, 'data') old, data = cst.load_data(filename) except pickle.UnpicklingError: cst.save_data(filename, latest, [(entry_title, updated, summary, link)]) except configparser.NoOptionError: filename = cst.new_data_file() FEEDS.set(title, 'data', filename) cst.save_data(filename, latest, [(entry_title, updated, summary, link)]) else: data.insert(0, (entry_title, updated, summary, link)) cst.save_data(filename, latest, data) else: logging.info("Feed '%s' is up-to-date", title) def _check_end_update(self): b = [t.is_alive() for t in self.threads.values() if t is not None] if sum(b): self._check_end_update_id = self.after(1000, self._check_end_update) else: cst.save_feeds() for widget in self.cat_widgets.values(): widget.sort() self._update_id = self.after( CONFIG.getint("General", "update_delay"), self.feed_update)
class Application(Frame): def __init__(self, master=None): super().__init__(master) self.master = master self.master.geometry("200x150") self.master.pack_propagate(0) self.master.resizable(0, 0) self.working_directory = str() self.s = Style(master) self.s.layout("LabeledProgressbar", [('LabeledProgressbar.trough', { 'children': [('LabeledProgressbar.pbar', { 'side': 'left', 'sticky': 'ns' }), ("LabeledProgressbar.label", { "sticky": "" })], 'sticky': 'nswe' })]) self.s.configure("LabeledProgressbar", background='green') self.extract_button = Button(self) self.get_dir_button = Button(self) self.progress_bar = Progressbar(self.master, orient=HORIZONTAL, length=180, mode='determinate', style="LabeledProgressbar") self.configure_widgets() self.pack() def wire_extract_function(self, extract): self.extract_button["command"] = extract def update_progressbar(self, value, max_value): progress = round(value / max_value * 100, 2) self.progress_bar["value"] = progress self.s.configure("LabeledProgressbar", text="{0} % ".format(progress)) self.master.update_idletasks() def configure_widgets(self): self.get_dir_button["text"] = "SELECT DIRECTORY" self.get_dir_button["command"] = self.ask_dir self.get_dir_button.pack(fill=BOTH, pady=10) self.extract_button["text"] = "EXTRACT" self.extract_button.pack(fill=BOTH, pady=10) self.progress_bar.pack(side=BOTTOM, pady=20) self.s.configure("LabeledProgressbar", text="0 % ") def ask_dir(self): tmp = filedialog.askdirectory() self.working_directory = tmp.replace('\\', '/')
#************************************************************************************************************************* # Initilizing progress bars # progressbar with text inside it s = Style(root) # add the label to the progressbar style s.layout( "LabeledProgressbar", [( 'LabeledProgressbar.trough', { 'children': [ ('LabeledProgressbar.pbar', { 'side': 'left', 'sticky': 'ns' }), ( "LabeledProgressbar.label", # label inside the bar { "sticky": "" }) ], 'sticky': 'nswe' })]) progress = Progressbar(commandframe, orient="horizontal", length=300, style="LabeledProgressbar") progress.pack(side=TOP, padx=10, pady=10)
def add_fram_search(self): self.frame = Frame(self.window, width=850, height=385, bg="#ef5151") self.frame.place(x=77, y=20) self.l1 = Label(self.frame, text="Enter Reg_N/Blood_G:", font="Times 16 bold") self.l1.place(x=50, y=43) self.Entry_reg = Entry(self.frame, font="courior 14 bold") self.Entry_reg.place(x=280, y=43, height=30) # search-Button self.b1 = Button(self.frame, text="Search-RN", font="Times 12 bold") self.b1.place(x=510, y=40, height=37) self.b1.bind('<Button-1>', self.regno) self.b1 = Button(self.frame, text="Search-BG", font="Times 12 bold") self.b1.place(x=599, y=40, height=37) self.b1.bind('<Button-1>', self.bg) # Back-Buton self.b1 = Button(self.frame, text="Back", font="Times 12 bold") self.b1.place(x=690, y=40, height=37) self.b1.bind('<Button-1>', self.back) # for Styling Table style = Style() style.configure("mystyle.Treeview", highlightthickness=0, bd=0, font=('Calibri 11')) # Modify the font of the body style.configure( "mystyle.Treeview.Heading", font=('Calibri 13 bold')) # Modify the font of the headings style.layout("mystyle.Treeview", [('mystyle.Treeview.treearea', { 'sticky': 'nswe' })]) # Remove the borders # import TreeViewe self.tr = Treeview(self.frame, columns=('A', 'B', 'C', 'D', 'E', 'F'), selectmode="extended", style="mystyle.Treeview") # heading key+text self.tr.heading("#0", text="Sr_No") self.tr.column("#0", minwidth=0, width=100, stretch=NO) self.tr.heading("#1", text="Name") self.tr.column("#1", minwidth=0, width=100, stretch=NO) self.tr.heading("#2", text="Reg_No") self.tr.column("#2", minwidth=0, width=100, stretch=NO) self.tr.heading("#3", text="Blood_Group") self.tr.column("#3", minwidth=0, width=100, stretch=NO) self.tr.heading("#4", text="Phone_No") self.tr.column("#4", minwidth=0, width=100, stretch=NO) self.tr.heading("#5", text="update") self.tr.column("#5", minwidth=0, width=100, stretch=NO) self.tr.heading("#6", text="delete") self.tr.column("#6", minwidth=0, width=100, stretch=NO) self.sb = Scrollbar(self.tr) self.sb.place(x=673, y=2, height=223, width=20, bordermode=OUTSIDE) self.sb.config(command=self.tr.yview) self.tr.config(yscrollcommand=self.sb.set) self.tr.place(x=50, y=100, width=691) # Remove All ROW self.Entry_reg.bind('<KeyRelease>', self.remove) self.tr.bind('<Double-Button-1>', self.action) self.window.mainloop()
from tkinter.ttk import Separator, Style, Combobox, Treeview from time import sleep from snapFood import * mydb = SnapFoodDB() style = Style() style.configure("mystyle.Treeview", highlightthickness=0, bd=0, font=('Calibri', 11)) # Modify the font of the body style.configure("mystyle.Treeview.Heading", font=('Calibri', 13, 'bold')) # Modify the font of the headings style.layout("mystyle.Treeview", [('mystyle.Treeview.treearea', { 'sticky': 'nswe' })]) # Remove the borders class Application: def __init__(self, master): self.username = "" self.password = "" # self.db_cursor = mydb.cursor() self.master = master master.title("Snapp Food") #Bring the window to the center of screen windowWidth = master.winfo_reqwidth() windowHeight = master.winfo_reqheight() # print("Width",windowWidth,"Height",windowHeight)
Frame with Image replace the image with your own image, if the screen size is large enough, about 1000 wide by 350 high. ''' from tkinter import Tk, PhotoImage from tkinter.ttk import Frame, Style, Label, Entry from PIL import Image, ImageTk root = Tk() s = Style() im = Image.open('../images/BountyMoorea.jpg') # change to your own file tkim = ImageTk.PhotoImage(im) width,height = im.size s.element_create("ship", "image", tkim) s.layout("ship", [("ship", {"sticky": "nsew"})]) fr=Frame(root, style="ship", height=height,width=width) fr.grid(column=0, row=1, sticky='nsew') il = Label(fr, text= 'Label') il.place(relx=0.1, rely=0.1, anchor='center') # using place to position widget en = Entry(fr, width=15) en.place(relx=0.1, rely=0.15, anchor='center') root.mainloop()
5pguUnClehS4tuMEDARQgH8FBMBBBExGwIGdAxywXAUBKHCZkAIoEEAFp33W QGl47ZgBAwZEwKigE1SQgAUCUDCXiwtQIIAFCTQwgaCrZeCABAzIleIGHDD/ oIAHGUznmXABGMABT4xpmBYBHGgAKGq1ZbppThgAG8EEAW61KwYMSOBAApdy pNp/BkhAAQLcEqCTt+ACJW645I5rLrgEeOsTBtwiQIEElRZg61sTNBBethSw CwEA/Pbr778ABywwABBAgAAG7xpAq6mGUUTdAPZ6YIACsRKAAbvtZqzxxhxn jDG3ybbKFHf36ZVYpuE5oIGhHMTqcqswvyxzzDS/HDMHEiiggQMLDxCZXh8k BnEBCQTggAUGGKCB0ktr0PTTTEfttNRQT22ABR4EkEABDXgnGUEn31ZABglE EEAAWaeN9tpqt832221HEEECW6M3wc+Hga3SBgtMODBABw00UEEBgxdO+OGG J4744oZzXUEDHQxwN7F5G7QRdXxPoPkAnHfu+eeghw665n1vIKhJBQUEADs=""") style = Style() # img1 is frameFocusBorder, img2 is frameBorder - cross reference style.element_create("RoundedFrame", "image", "frameBorder", ("focus", "frameFocusBorder"), border=16, sticky="nsew") style.layout("RoundedFrame", [("RoundedFrame", {"sticky": "nsew"})]) style.configure("TEntry", borderwidth=0) # general handle widget class Entry frame = Frame(style="RoundedFrame", padding=10) frame.pack(fill='x') frame2 = Frame(style="RoundedFrame", padding=10) frame2.pack(fill='both', expand=1) entry = Entry(frame, text='Test') entry.pack(fill='x') entry.bind("<FocusIn>", lambda evt: frame.state(["focus"])) entry.bind("<FocusOut>", lambda evt: frame.state(["!focus"])) text = Text(frame2, borderwidth=0, bg="white", highlightthickness=0) text.pack(fill='both', expand=1)
RRRWENHwRQEBADs=""" # the images are combined gif data s1 = PhotoImage("search1", data=data, format="gif -index 0") s2 = PhotoImage("search2", data=data, format="gif -index 1") style = Style() style.theme_use('default') style.element_create("Search.field", "image", "search1", ("focus", "search2"), border=[22, 7, 14], sticky="ew") style.layout("Search.entry", [ ("Search.field", {"sticky": "nswe", "border": 1, "children": [("Entry.padding", {"sticky": "nswe", "children": [("Entry.textarea", {"sticky": "nswe"})] })] })] ) style.configure("Search.entry", background="#b2b2b2", font=font.nametofont("TkDefaultFont")) root.configure(background="#b2b2b2") e1 = Entry(style="Search.entry", width=20) e1.grid(padx=10, pady=10) e2 = Entry(style="Search.entry", width=20) e2.grid(padx=10, pady=10)