def scroll(view, vert=True, horiz=True, resize=True): """Sets up scrollbars on view's master. The view should not have any layout settings of its own.""" kw = dict() if resize: if not horiz: kw.update(rowspan=2) if not vert: kw.update(colspan=2) view.grid(sticky=(tkinter.EW, tkinter.NS), **kw) view.master.rowconfigure(0, weight=1) view.master.columnconfigure(0, weight=1) if vert: scroll = Scrollbar(view.master, orient=tkinter.VERTICAL, command=view.yview) scroll.grid(row=0, column=1, sticky=(tkinter.W, tkinter.NS)) view.configure(yscrollcommand=scroll.set) if horiz: scroll = Scrollbar(view.master, orient=tkinter.HORIZONTAL, command=view.xview) scroll.grid(row=1, column=0, sticky=(tkinter.N, tkinter.EW)) view.configure(xscrollcommand=scroll.set) if resize: resize = Sizegrip(view.master) resize.grid(row=1, column=1, sticky=(tkinter.EW, tkinter.NS))
def create_content(self, **kw): self.rowconfigure(2, weight=1) self.columnconfigure(0, weight=1) self.minsize(50, 50) self.hide_completed = CONFIG.getboolean('Tasks', 'hide_completed') # --- elements label = Label(self, text=_('Tasks').upper(), style='title.Tasks.TLabel', anchor='center') label.grid(row=0, columnspan=2, pady=4, sticky='ew') Separator(self, style='Tasks.TSeparator').grid(row=1, columnspan=2, sticky='we') self.display = Text(self, width=20, height=10, relief='flat', cursor='arrow', wrap='word', highlightthickness=0, state='disabled', spacing1=5, tabs=('35', 'right', '45', 'left')) self.display.grid(sticky='nsew', row=2, column=0, padx=2, pady=2) scroll = AutoScrollbar(self, orient='vertical', style='Tasks.Vertical.TScrollbar', command=self.display.yview) scroll.grid(row=2, column=1, sticky='ns', pady=(2, 16)) self.display.configure(yscrollcommand=scroll.set) self.display_tasks() corner = Sizegrip(self, style="Tasks.TSizegrip") corner.place(relx=1, rely=1, anchor='se') # --- bindings self.bind('<3>', lambda e: self.menu.tk_popup(e.x_root, e.y_root)) label.bind('<ButtonPress-1>', self._start_move) label.bind('<ButtonRelease-1>', self._stop_move) label.bind('<B1-Motion>', self._move)
def scroll(view, vert=True, horiz=True, resize=True): kw = dict() if resize: if not horiz: kw.update(rowspan=2) if not vert: kw.update(colspan=2) view.grid(sticky=(tkinter.EW, tkinter.NS), **kw) view.master.rowconfigure(0, weight=1) view.master.columnconfigure(0, weight=1) if vert: scroll = Scrollbar(view.master, orient=tkinter.VERTICAL, command=view.yview) scroll.grid(row=0, column=1, sticky=(tkinter.W, tkinter.NS)) view.configure(yscrollcommand=scroll.set) if horiz: scroll = Scrollbar(view.master, orient=tkinter.HORIZONTAL, command=view.xview) scroll.grid(row=1, column=0, sticky=(tkinter.N, tkinter.EW)) view.configure(xscrollcommand=scroll.set) if resize: resize = Sizegrip(view.master) resize.grid(row=1, column=1, sticky=(tkinter.EW, tkinter.NS))
def _create_treeview_tab(self, nb): # Populate the second pane. Note that the content doesn't really matter tree = None self.backg = ["white", '#f0f0ff'] tree_columns = ("country", "capital", "currency") tree_data = [("Argentina", "Buenos Aires", "ARS"), ("Australia", "Canberra", "AUD"), ("Brazil", "Brazilia", "BRL"), ("Canada", "Ottawa", "CAD"), ("China", "Beijing", "CNY"), ("France", "Paris", "EUR"), ("Germany", "Berlin", "EUR"), ("India", "New Delhi", "INR"), ("Italy", "Rome", "EUR"), ("Japan", "Tokyo", "JPY"), ("Mexico", "Mexico City", "MXN"), ("Russia", "Moscow", "RUB"), ("South Africa", "Pretoria", "ZAR"), ("United Kingdom", "London", "GBP"), ("United States", "Washington, D.C.", "USD")] container = Frame(nb) container.pack(fill='both', expand=False) self.tree = Treeview(container, columns=tree_columns, show="headings") vsb = Scrollbar(container, orient="vertical", command=self.tree.yview) hsb = Scrollbar(container, orient="horizontal", command=self.tree.xview) self.tree.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set) self.tree.grid(column=0, row=0, sticky='ns', in_=container) vsb.grid(column=1, row=0, sticky='ns', in_=container) hsb.grid(column=0, row=1, sticky='ew', in_=container) container.grid_columnconfigure(0, weight=1) container.grid_rowconfigure(0, weight=1) for col in tree_columns: self.tree.heading( col, text=col.title(), command=lambda c=col: self.sortby(self.tree, c, 0)) # XXX tkFont.Font().measure expected args are incorrect according # to the Tk docs self.tree.column(col, width=Font().measure(col.title()), stretch=False) for ix, item in enumerate(tree_data): itemID = self.tree.insert('', 'end', values=item) self.tree.item(itemID, tags=itemID) self.tree.tag_configure(itemID, background=self.backg[ix % 2]) # adjust columns lengths if necessary for indx, val in enumerate(item): ilen = Font().measure(val) if self.tree.column(tree_columns[indx], width=None) < ilen: self.tree.column(tree_columns[indx], width=ilen) sg = Sizegrip(container) sg.grid(sticky='e') nb.add(container, text='Treeview', underline=0, padding=2)
def create_content(self, **kw): self.minsize(50, 120) self._time = [0, 0, 0] self._on = False self._after_id = '' self.img_play = PhotoImage(master=self, file=IM_START) self.img_pause = PhotoImage(master=self, file=IM_PAUSE) self.img_stop = PhotoImage(master=self, file=IM_STOP) self.rowconfigure(2, weight=1) self.columnconfigure(0, weight=1) self.columnconfigure(1, weight=1) # --- GUI elements self.display = Label(self, text='%i:%.2i:%.2i' % tuple(self._time), anchor='center', style='timer.TLabel') self.intervals = Text(self, highlightthickness=0, relief='flat', height=3, width=1, inactiveselectbackground=self.style.lookup('TEntry', 'selectbackground')) self.intervals.tag_configure('center', justify='center') self.intervals.configure(state='disabled') self.b_interv = Button(self, text=_('Interval'), style='timer.TButton', command=self.add_interval) self.b_interv.state(('disabled',)) self.b_launch = Button(self, image=self.img_play, padding=2, command=self.launch, style='timer.TButton') self.b_stop = Button(self, image=self.img_stop, padding=2, command=self.stop, style='timer.TButton') # --- placement self.display.grid(row=0, columnspan=2, sticky='ew', padx=8, pady=(4, 0)) Label(self, text=_('Intervals:'), style='timer.TLabel').grid(row=1, columnspan=2, sticky='w', padx=4) self.intervals.grid(row=2, columnspan=2, sticky='eswn') self.b_interv.grid(row=3, columnspan=2, sticky='ew') self.b_launch.grid(row=4, column=0, sticky='ew') self.b_stop.grid(row=4, column=1, sticky='ew') self._corner = Sizegrip(self, style="timer.TSizegrip") self._corner.place(relx=1, rely=1, anchor='se') # --- bindings self.intervals.bind("<1>", lambda event: self.intervals.focus_set()) self.bind('<3>', lambda e: self.menu.tk_popup(e.x_root, e.y_root)) self.display.bind('<ButtonPress-1>', self._start_move) self.display.bind('<ButtonRelease-1>', self._stop_move) self.display.bind('<B1-Motion>', self._move) self.b_stop.bind('<Enter>', self._on_enter) self.b_stop.bind('<Leave>', self._on_leave)
def add_btns(self): root = self.root frame = Frame(root) for key, text, command in ( (3, _("F3 View"), self.on_F3), (4, _("F4 Edit"), self.on_F4), (5, _("F5 Copy"), self.on_F5), (6, _("F6 Move"), self.on_F6), (7, _("F7 Make Directory"), self.on_F7), (8, _("F8 Remove"), self.on_F8), (10, _("F10 Exit"), self.on_F10)): btn = Button(frame, text=text, command=command, takefocus=False) btn.pack(side="left", fill="x", expand=True) root.bind_all("<F%d>" % key, func=command) sz = Sizegrip(frame) sz.pack(side="right", anchor="se") frame.grid(column=0, row=2, columnspan=2, sticky="we")
def create_content(self, **kw): self.minsize(50, 50) self.rowconfigure(2, weight=1) self.columnconfigure(0, weight=1) label = Label(self, text=_('Events').upper(), style='title.Events.TLabel', anchor='center') label.grid(row=0, columnspan=2, pady=4, sticky='ew') Separator(self, style='Events.TSeparator').grid(row=1, columnspan=2, sticky='we') self.canvas = Canvas(self, highlightthickness=0) self.canvas.grid(sticky='nsew', row=2, column=0, padx=2, pady=2) scroll = AutoScrollbar(self, orient='vertical', style='Events.Vertical.TScrollbar', command=self.canvas.yview) scroll.grid(row=2, column=1, sticky='ns', pady=(2, 16)) self.canvas.configure(yscrollcommand=scroll.set) self.display = Frame(self.canvas, style='Events.TFrame') self.canvas.create_window(0, 0, anchor='nw', window=self.display, tags=('display', )) self.display_evts() corner = Sizegrip(self, style="Events.TSizegrip") corner.place(relx=1, rely=1, anchor='se') # --- bindings self.bind('<3>', lambda e: self.menu.tk_popup(e.x_root, e.y_root)) label.bind('<ButtonPress-1>', self._start_move) label.bind('<ButtonRelease-1>', self._stop_move) label.bind('<B1-Motion>', self._move) self.bind('<4>', lambda e: self._scroll(-1)) self.bind('<5>', lambda e: self._scroll(1)) self.update_idletasks() self.canvas.configure(scrollregion=self.canvas.bbox('all'))
def _setup_status_bar(self): # Status bar self.statusbar = tk.Frame(self.root) self.statusbar.grid(column=0, row=2, sticky=(tk.W, tk.E)) # Coverage summary for currently selected file. self.coverage_file_summary = tk.StringVar() self.coverage_file_summary_label = Label( self.statusbar, textvariable=self.coverage_file_summary) self.coverage_file_summary_label.grid(column=0, row=0, sticky=(tk.W, tk.E)) self.coverage_file_summary.set('No file selected') # Main window resize handle self.grip = Sizegrip(self.statusbar) self.grip.grid(column=1, row=0, sticky=(tk.S, tk.E)) # Set up weights for status bar frame self.statusbar.columnconfigure(0, weight=1) self.statusbar.columnconfigure(1, weight=0) self.statusbar.rowconfigure(0, weight=0)
def _setup_status_bar(self): # Status bar self.statusbar = tk.Frame(self.root) self.statusbar.grid(column=0, row=2, sticky=(tk.W, tk.E)) # Coverage summary for currently selected file. self.coverage_file_summary = tk.StringVar() self.coverage_file_summary_label = Label(self.statusbar, textvariable=self.coverage_file_summary) self.coverage_file_summary_label.grid(column=0, row=0, sticky=(tk.W, tk.E)) self.coverage_file_summary.set('No file selected') # Main window resize handle self.grip = Sizegrip(self.statusbar) self.grip.grid(column=1, row=0, sticky=(tk.S, tk.E)) # Set up weights for status bar frame self.statusbar.columnconfigure(0, weight=1) self.statusbar.columnconfigure(1, weight=0) self.statusbar.rowconfigure(0, weight=0)
def create_widgets(self, container): """This method defines UI of the main window""" self.frm_main = Frame(container) self.frm_main.pack(fill=BOTH, expand=True) self.frm_left_main = Frame(self.frm_main, width=__class__.left_panel_width, height=self.total_height, bg=__class__.base_color) self.frm_left_main.pack(side=LEFT) self.frm_left_top = Frame(self.frm_left_main, width=__class__.left_panel_width, height=__class__.left_upper_height, bg=__class__.cover_color) self.frm_left_top.grid(row=0) self.frm_left_top.grid_propagate(False) self.calendar = Calendar(self.frm_left_top, padx_param=__class__.left_panel_width / 5, pady_param=__class__.left_upper_height / 8) self.frm_left_middle = Frame(self.frm_left_main, width=__class__.left_panel_width, height=self.left_middle_height, bg=__class__.base_color) self.frm_left_middle.grid(row=1) self.cnvs_left_middle = Canvas( self.frm_left_middle, width=self.left_panel_width, height=self.left_middle_height) #, bg='white' self.cnvs_left_middle.grid(row=0, column=0) self.cnvs_left_middle.create_image(self.left_panel_width // 2, self.left_middle_height // 2, image=self.justdoit_img) self.frm_left_btm = Frame(self.frm_left_main, width=__class__.left_panel_width, height=self.left_lower_height, bg=__class__.base_color) self.frm_left_btm.grid(row=2) self.cnvs_left_bottom = Canvas( self.frm_left_btm, width=self.left_panel_width, height=self.left_lower_height) #, bg='white' self.cnvs_left_bottom.grid(row=0, column=0) self.cnvs_left_bottom.create_image(self.left_panel_width // 2, self.left_lower_height // 2, image=self.colornotes_img) self.frm_right_main = Frame(self.frm_main, width=self.right_area_width, height=self.total_height, bg=__class__.base_color) self.frm_right_main.pack() self.frm_notes_control = Frame(self.frm_right_main, width=self.right_area_width, height=__class__.notes_ctrl_height, bg=__class__.cover_color) self.frm_notes_control.pack(fill=X) self.frm_notes_control.pack_propagate(False) self.lbl_filter_by_category = Label(self.frm_notes_control, font=__class__.lbl_font, fg=__class__.lbl_fontcolor, bg=__class__.cover_color, text=__class__.str_by_category) self.lbl_filter_by_category.pack(side=LEFT) self.cmbx_filter_by_category = Combobox( self.frm_notes_control, font=__class__.input_font, foreground=__class__.input_fontcolor, textvariable=category_filter_ctrlvar) # ttk.Combobox self.cmbx_filter_by_category.configure(values=['All'] + category_names_lst) self.cmbx_filter_by_category.current(0) # i.e. default value is 'All' self.cmbx_filter_by_category.bind("<<ComboboxSelected>>", self.action_filter_notes) self.cmbx_filter_by_category.pack(side=LEFT) self.lbl_filter_by_text = Label(self.frm_notes_control, font=__class__.lbl_font, fg=__class__.lbl_fontcolor, bg=__class__.cover_color, text=__class__.str_by_text) self.lbl_filter_by_text.pack(side=LEFT) self.entry_filter_by_text = Entry(self.frm_notes_control, font=__class__.input_font, fg=__class__.input_fontcolor, textvariable=descr_filter_ctrlvar) self.entry_filter_by_text.bind('<KeyRelease>', self.action_filter_notes) self.entry_filter_by_text.pack(side=LEFT) self.btn_new_note = Button(self.frm_notes_control, font=__class__.btn_font, fg=__class__.btn_fontcolor, text=__class__.str_newnote) self.btn_new_note.bind("<Button-1>", action_new_note_dialog) self.btn_new_note.pack(side=RIGHT, padx=5) self.frm_notes_display = Frame(self.frm_right_main, width=self.right_area_width, height=self.notes_display_height, bg='bisque') self.frm_notes_display.pack() self.cnvs_notes_display = Canvas(self.frm_notes_display, width=self.right_area_width, height=self.notes_display_height) self.cnvs_notes_display.bind('<Configure>', self.action_config_display) self.cnvs_notes_display.pack(fill=BOTH) self.cnvs_notes_display.create_image(self.right_area_width // 2, self.notes_display_height // 2, image=self.msgboard_img) self.sizegrip = Sizegrip(self) #ttk.Sizegrip self.sizegrip.pack(side=RIGHT)
class MainWindow(Frame): """This class defines UI and functionality of the application main window Derives from class Frame""" # class variables left_panel_width = 300 left_upper_height = 270 # width required to display the calendar (date picker) notes_ctrl_height = 50 base_color = 'bisque' cover_color = 'indianred' lbl_font = '"Century Gothic" 16' lbl_fontcolor = 'floralwhite' input_font = '"Segoe UI" 16' input_fontcolor = 'brown' btn_font = '"Century Gothic" 12 bold' btn_fontcolor = 'brown' str_by_category = 'filter by category:' str_by_text = 'filter by text:' str_newnote = 'newnote' noteview_side = 250 note_padding = 5 def __init__(self, container): """ctor""" super().__init__(container) container.update_idletasks() self.total_height = container.winfo_height() self.total_width = container.winfo_width() self.left_middle_height = self.left_lower_height = ( self.total_height - __class__.left_upper_height) // 2 self.right_area_width = self.total_width - __class__.left_panel_width self.notes_display_height = self.total_height - self.__class__.notes_ctrl_height self.msgboard_img = PhotoImage( file=msgboard_img_path ) # object that represents the background as cork-board texture self.justdoit_img = PhotoImage( file=justdoit_img_path) # placeholder #1 self.colornotes_img = PhotoImage( file=colornotes_img_path) # placeholder #2 self.create_widgets(container) # initial calculation of the grid dimensions according to the available space and the note frame size self.cnvs_notes_display.gridcols_cnt = self.right_area_width // ( __class__.noteview_side + __class__.note_padding) self.cnvs_notes_display.gridrows_cnt = self.notes_display_height // ( __class__.noteview_side + __class__.note_padding) self.cnvs_notes_display.grid_propagate(False) def create_widgets(self, container): """This method defines UI of the main window""" self.frm_main = Frame(container) self.frm_main.pack(fill=BOTH, expand=True) self.frm_left_main = Frame(self.frm_main, width=__class__.left_panel_width, height=self.total_height, bg=__class__.base_color) self.frm_left_main.pack(side=LEFT) self.frm_left_top = Frame(self.frm_left_main, width=__class__.left_panel_width, height=__class__.left_upper_height, bg=__class__.cover_color) self.frm_left_top.grid(row=0) self.frm_left_top.grid_propagate(False) self.calendar = Calendar(self.frm_left_top, padx_param=__class__.left_panel_width / 5, pady_param=__class__.left_upper_height / 8) self.frm_left_middle = Frame(self.frm_left_main, width=__class__.left_panel_width, height=self.left_middle_height, bg=__class__.base_color) self.frm_left_middle.grid(row=1) self.cnvs_left_middle = Canvas( self.frm_left_middle, width=self.left_panel_width, height=self.left_middle_height) #, bg='white' self.cnvs_left_middle.grid(row=0, column=0) self.cnvs_left_middle.create_image(self.left_panel_width // 2, self.left_middle_height // 2, image=self.justdoit_img) self.frm_left_btm = Frame(self.frm_left_main, width=__class__.left_panel_width, height=self.left_lower_height, bg=__class__.base_color) self.frm_left_btm.grid(row=2) self.cnvs_left_bottom = Canvas( self.frm_left_btm, width=self.left_panel_width, height=self.left_lower_height) #, bg='white' self.cnvs_left_bottom.grid(row=0, column=0) self.cnvs_left_bottom.create_image(self.left_panel_width // 2, self.left_lower_height // 2, image=self.colornotes_img) self.frm_right_main = Frame(self.frm_main, width=self.right_area_width, height=self.total_height, bg=__class__.base_color) self.frm_right_main.pack() self.frm_notes_control = Frame(self.frm_right_main, width=self.right_area_width, height=__class__.notes_ctrl_height, bg=__class__.cover_color) self.frm_notes_control.pack(fill=X) self.frm_notes_control.pack_propagate(False) self.lbl_filter_by_category = Label(self.frm_notes_control, font=__class__.lbl_font, fg=__class__.lbl_fontcolor, bg=__class__.cover_color, text=__class__.str_by_category) self.lbl_filter_by_category.pack(side=LEFT) self.cmbx_filter_by_category = Combobox( self.frm_notes_control, font=__class__.input_font, foreground=__class__.input_fontcolor, textvariable=category_filter_ctrlvar) # ttk.Combobox self.cmbx_filter_by_category.configure(values=['All'] + category_names_lst) self.cmbx_filter_by_category.current(0) # i.e. default value is 'All' self.cmbx_filter_by_category.bind("<<ComboboxSelected>>", self.action_filter_notes) self.cmbx_filter_by_category.pack(side=LEFT) self.lbl_filter_by_text = Label(self.frm_notes_control, font=__class__.lbl_font, fg=__class__.lbl_fontcolor, bg=__class__.cover_color, text=__class__.str_by_text) self.lbl_filter_by_text.pack(side=LEFT) self.entry_filter_by_text = Entry(self.frm_notes_control, font=__class__.input_font, fg=__class__.input_fontcolor, textvariable=descr_filter_ctrlvar) self.entry_filter_by_text.bind('<KeyRelease>', self.action_filter_notes) self.entry_filter_by_text.pack(side=LEFT) self.btn_new_note = Button(self.frm_notes_control, font=__class__.btn_font, fg=__class__.btn_fontcolor, text=__class__.str_newnote) self.btn_new_note.bind("<Button-1>", action_new_note_dialog) self.btn_new_note.pack(side=RIGHT, padx=5) self.frm_notes_display = Frame(self.frm_right_main, width=self.right_area_width, height=self.notes_display_height, bg='bisque') self.frm_notes_display.pack() self.cnvs_notes_display = Canvas(self.frm_notes_display, width=self.right_area_width, height=self.notes_display_height) self.cnvs_notes_display.bind('<Configure>', self.action_config_display) self.cnvs_notes_display.pack(fill=BOTH) self.cnvs_notes_display.create_image(self.right_area_width // 2, self.notes_display_height // 2, image=self.msgboard_img) self.sizegrip = Sizegrip(self) #ttk.Sizegrip self.sizegrip.pack(side=RIGHT) def action_config_display(self, event): """This method deploys according to the new screen dimensions""" #self.after(250, clean_display()) root.update_idletasks() self.total_height = root.winfo_height() self.total_width = root.winfo_width() self.left_middle_height = self.left_lower_height = ( self.total_height - __class__.left_upper_height) // 2 self.right_area_width = self.total_width - __class__.left_panel_width self.notes_display_height = self.total_height - self.__class__.notes_ctrl_height # recalculation of the grid dimensions according to available space and note frame size self.cnvs_notes_display.gridcols_cnt = self.right_area_width // ( __class__.noteview_side + __class__.note_padding) self.cnvs_notes_display.gridrows_cnt = self.notes_display_height // ( __class__.noteview_side + __class__.note_padding) self.action_filter_notes(event) def action_filter_notes(self, event=None): """This method displays notes that meet currentl filtering criteria""" aux_filter_notes(category_filter_ctrlvar.get(), descr_filter_ctrlvar.get()) display_notes(event)
def __init__(self, master, name, config, save_config): """Create base desktop widget.""" Toplevel.__init__(self, master, class_=APP_NAME) self.rowconfigure(2, weight=1) self.columnconfigure(0, weight=1) self.minsize(50, 50) self.protocol('WM_DELETE_WINDOW', self.withdraw) self.ewmh = EWMH() self.name = name self.config = config # configparser self.save_config = save_config # save config method # get splash window type compatibility if CONFIG.getboolean('General', 'splash_supported', fallback=True): self.attributes('-type', 'splash') else: self.attributes('-type', 'toolbar') # control main menu checkbutton self.variable = BooleanVar(self, False) # save widget's position self._position = StringVar( self, self.config.get(name, 'position', fallback='normal')) add_trace(self._position, 'write', self._position_trace) self.title('feedagregator.widget.{}'.format(name.replace(' ', '_'))) self.withdraw() # window dragging self.x = None self.y = None # --- menu self._create_menu() # --- elements # --- --- title bar frame = Frame(self, style='widget.TFrame') Button(frame, style='widget.close.TButton', command=self.withdraw).pack(side='left') self.label = Label(frame, text=name, style='widget.title.TLabel', anchor='center') self.label.pack(side='left', fill='x', expand=True) frame.grid(row=0, columnspan=2, padx=4, pady=4, sticky='ew') sep = Separator(self, style='widget.Horizontal.TSeparator') sep.grid(row=1, columnspan=2, sticky='ew') # --- --- widget body self.canvas = Canvas(self, highlightthickness=0) self.canvas.grid(row=2, column=0, sticky='ewsn', padx=(2, 8), pady=(2, 4)) scroll = AutoScrollbar(self, orient='vertical', style='widget.Vertical.TScrollbar', command=self.canvas.yview) scroll.grid(row=2, column=1, sticky='ns', pady=(2, 14)) self.canvas.configure(yscrollcommand=scroll.set) self.display = Frame(self.canvas, style='widget.TFrame') self.canvas.create_window(0, 0, anchor='nw', window=self.display, tags=('display', )) self.display.columnconfigure(0, weight=1) # --- style self.style = Style(self) self._font_size = 10 self.update_style() # --- resizing and geometry corner = Sizegrip(self, style="widget.TSizegrip") corner.place(relx=1, rely=1, anchor='se', bordermode='outside') geometry = self.config.get(self.name, 'geometry') if geometry: self.geometry(geometry) self.update_idletasks() if self.config.getboolean(self.name, 'visible', fallback=True): self.deiconify() # --- bindings self.bind('<3>', lambda e: self.menu.tk_popup(e.x_root, e.y_root)) for widget in [self.label, self.canvas, sep]: widget.bind('<ButtonPress-1>', self._start_move) widget.bind('<ButtonRelease-1>', self._stop_move) widget.bind('<B1-Motion>', self._move) self.label.bind('<Map>', self._change_position) self.bind('<Configure>', self._on_configure) self.bind('<4>', lambda e: self._scroll(-1)) self.bind('<5>', lambda e: self._scroll(1)) self.update_idletasks() self.canvas.configure(scrollregion=self.canvas.bbox('all')) self.populate_widget() if not CONFIG.getboolean('General', 'splash_supported', fallback=True) and self.config.getboolean( self.name, 'visible', fallback=True): Toplevel.withdraw(self) Toplevel.deiconify(self)
def configure_ui(info): """ UI config function. @param info: UI config info dict. @return: None. """ # Background color bg_color = 'white smoke' # Create ttk style object STYLE = Style() # Configure TFrame style's background STYLE.configure( 'TFrame', background=bg_color, ) # Configure TLabelframe style's background STYLE.configure( 'TLabelframe', background=bg_color, ) # Configure TLabelframe.Label style's background STYLE.configure( 'TLabelframe.Label', background=bg_color, ) # Configure TLabel style's background STYLE.configure( 'TLabel', background=bg_color, ) # Configure TRadiobutton style's background STYLE.configure( 'TRadiobutton', background=bg_color, ) # Get TK root window tk = info['tk'] # Set window title tk.title('AoikRegistryEditor') # Set window geometry tk.geometry('1280x720') # Configure layout weights for children. # Row 0 is for registry editor. tk.rowconfigure(0, weight=1) # Row 1 is for status bar tk.rowconfigure(1, weight=0) # Use only one column tk.columnconfigure(0, weight=1) # Get menu tree menutree = info['menutree'] # Add `File` menu menutree.add_menu(pid='/', id='File', index=0) # Add `Exit` command menutree.add_command(pid='/File', id='Exit', command=tk.quit) # Get status bar label status_bar_label = info['status_bar_label'] # Set status bar label's main frame's height status_bar_label.widget().config(height=20) # Set status bar label's background status_bar_label.config(background='#F0F0F0') # Lay out the status bar label status_bar_label.grid( in_=tk, row=2, column=0, sticky='NSEW', padx=(5, 0), ) # Create size grip sizegrip = Sizegrip(master=tk) # Lay out the size grip sizegrip.grid( in_=tk, row=2, column=0, sticky='E', ) # Get registry editor editor = info['editor'] # Lay out the registry editor editor.grid( row=0, column=0, sticky='NSEW', ) # Set registry editor's inner padding editor.config(padding=10) # Get path bar label path_bar_label = info['path_bar_label'] # Get static files' directory path static_dir = os.path.dirname( os.path.abspath(aoikregistryeditor.static.__file__) ) # Get path bar label's normal state image file path image_path = os.path.join(static_dir, 'path_bar_label_normal.png') # Load path bar label's normal state image file path_bar_label._normal_image = PhotoImage(file=image_path) # Get path bar label's disabled state image file path image_path = os.path.join(static_dir, 'path_bar_label_disabled.png') # Load path bar label's disabled state image file path_bar_label._disabled_image = PhotoImage(file=image_path) # Set path bar label's images path_bar_label.config( image=( path_bar_label._normal_image, 'disabled', path_bar_label._disabled_image, ) ) # Get path bar textfield path_bar = info['path_bar'] # Set path bar textfield's font path_bar.config(font=('Consolas', 12)) # Set path bar textfield's outer padding path_bar.grid(padx=(3, 0)) # Get child keys labelframe child_keys_labelframe = info['child_keys_labelframe'] # Set child keys labelframe's outer padding child_keys_labelframe.grid(pady=(5, 0)) # Set child keys labelframe's inner padding child_keys_labelframe.config(padding=5) # Get child keys listbox child_keys_listbox = info['child_keys_listbox'] # Set child keys listbox's font child_keys_listbox.config(font=('Consolas', 12)) # Get fields labelframe fields_labelframe = info['fields_labelframe'] # Set fields labelframe's outer padding fields_labelframe.grid(padx=(10, 0), pady=(5, 0)) # Set fields labelframe's inner padding fields_labelframe.config(padding=5) # Get fields listbox fields_listbox = info['fields_listbox'] # Set fields listbox's font fields_listbox.config(font=('Consolas', 12)) # Create event handler to set fields listbox background def _fields_listbox_set_background(): # If fields listbox is not empty if fields_listbox.size() > 0: # Set background color for non-empty listbox fields_listbox.config(background='white') # If fields listbox is empty else: # Set background color for empty listbox fields_listbox.config(background='gainsboro') # Call the event handler to initialize the background color _fields_listbox_set_background() # Add the event handler to fields listbox fields_listbox.handler_add( fields_listbox.ITEMS_CHANGE_DONE, _fields_listbox_set_background ) # Get field editor labelframe field_editor_labelframe = info['field_editor_labelframe'] # Set field editor labelframe's outer padding field_editor_labelframe.grid(padx=(10, 0), pady=(5, 0)) # Set field editor labelframe's inner padding field_editor_labelframe.config(padding=5) # Get field add label field_add_label = info['field_add_label'] # Set field add label's main frame size field_add_label.widget().config(width=40, height=40) # Get field add label's normal state image file path image_path = os.path.join(static_dir, 'field_add_normal.png') # Load field add label's normal state image file field_add_label._normal_image = PhotoImage(file=image_path) # Get field add label's active state image file path image_path = os.path.join(static_dir, 'field_add_active.png') # Load field add label's active state image file field_add_label._active_image = PhotoImage(file=image_path) # Get field add label's hover state image file path image_path = os.path.join(static_dir, 'field_add_hover.png') # Load field add label' hover state image file field_add_label._hover_image = PhotoImage(file=image_path) # Set field add label's images. # Notice `disabled` state is excluded from other states. # Notice `active` state takes precedence over `hover` state. field_add_label.config( image=( field_add_label._normal_image, '!disabled active', field_add_label._active_image, '!disabled hover', field_add_label._hover_image, ) ) # Get field delete label field_del_label = info['field_del_label'] # Set field delete label's main frame size field_del_label.widget().config(width=40, height=40) # Get field delete label's normal state image file path image_path = os.path.join(static_dir, 'field_del_normal.png') # Load field delete label's normal state image file field_del_label._normal_image = PhotoImage(file=image_path) # Get field delete label's active state image file path image_path = os.path.join(static_dir, 'field_del_active.png') # Load field delete label's active state image file field_del_label._active_image = PhotoImage(file=image_path) # Get field delete label's hover state image file path image_path = os.path.join(static_dir, 'field_del_hover.png') # Load field delete label's hover state image file field_del_label._hover_image = PhotoImage(file=image_path) # Set field delete label's images. # Notice `disabled` state is excluded from other states. # Notice `active` state takes precedence over `hover` state. field_del_label.config( image=( field_del_label._normal_image, '!disabled active', field_del_label._active_image, '!disabled hover', field_del_label._hover_image, ) ) # Get field load label field_load_label = info['field_load_label'] # Set field load label's main frame size field_load_label.widget().config(width=40, height=40) # Get field load label's normal state image file path image_path = os.path.join(static_dir, 'field_load_normal.png') # Load field load label's normal state image file field_load_label._normal_image = PhotoImage(file=image_path) # Get field load label's active state image file path image_path = os.path.join(static_dir, 'field_load_active.png') # Load field load label's active state image file field_load_label._active_image = PhotoImage(file=image_path) # Get field load label's hover state image file path image_path = os.path.join(static_dir, 'field_load_hover.png') # Load field load label's hover state image file field_load_label._hover_image = PhotoImage(file=image_path) # Set field load label's images. # Notice `disabled` state is excluded from other states. # Notice `active` state takes precedence over `hover` state. field_load_label.config( image=( field_load_label._normal_image, '!disabled active', field_load_label._active_image, '!disabled hover', field_load_label._hover_image, ) ) # Get field save label field_save_label = info['field_save_label'] # Set field save label's main frame size field_save_label.widget().config(width=40, height=40) # Get field save label's normal state image file path image_path = os.path.join(static_dir, 'field_save_normal.png') # Load field save label's normal state image file field_save_label._normal_image = PhotoImage(file=image_path) # Get field save label's active state image file path image_path = os.path.join(static_dir, 'field_save_active.png') # Load field save label's active state image file field_save_label._active_image = PhotoImage(file=image_path) # Get field save label's hover state image file path image_path = os.path.join(static_dir, 'field_save_hover.png') # Load field save label's hover state image file field_save_label._hover_image = PhotoImage(file=image_path) # Set field save label's images. # Notice `disabled` state is excluded from other states. # Notice `active` state takes precedence over `hover` state. field_save_label.config( image=( field_save_label._normal_image, '!disabled active', field_save_label._active_image, '!disabled hover', field_save_label._hover_image, ) ) # Get field add dialog field_add_dialog = info['field_add_dialog'] # Set field add dialog's geometry field_add_dialog.toplevel().geometry('300x110') # Set field add dialog to not resizable field_add_dialog.toplevel().resizable(width=False, height=False) # Set field add dialog's background field_add_dialog.toplevel().config(background=bg_color) # Set field add dialog's main frame's outer padding field_add_dialog.main_frame().grid(padx=5, pady=5) # Set field add dialog's confirm button's outer padding field_add_dialog.confirm_button().grid(pady=(15, 0)) # Set field add dialog's cancel button's outer padding field_add_dialog.cancel_button().grid(pady=(15, 0)) # Set field add dialog's field add type label's outer padding editor._field_add_type_label.grid( pady=(10, 0), ) # Set field add dialog's field add type radio buttons frame's outer padding editor._field_add_type_rbuttons_frame.grid( padx=(3, 0), pady=(10, 0), )
st_rb = Radiobutton(fr, value=state, text=state, variable=state_val, command=change_state) st_rb.grid(column=0, row=iy, padx=5, pady=5, sticky='nw') img1 = PhotoImage("sizegrip", file='../images/piratz/sizegrip.png') style = Style() # both theme_create and theme_settings worked style.theme_create( "yummy", parent="clam", settings={ #style.theme_settings('default', { # start of theme extract 'sizegrip': { "element create": ('image', "sizegrip") } # end of theme extract - don't forget to add comma at end when inserting }) style.theme_use('yummy') # 'default' root.columnconfigure(0, weight=1) root.rowconfigure(0, weight=1) widg = Sizegrip(root) widg.grid(row=1, column=1, sticky='se') root.mainloop()
class Timer(BaseWidget): def __init__(self, master): BaseWidget.__init__(self, 'Timer', master) def create_content(self, **kw): self.minsize(50, 120) self._time = [0, 0, 0] self._on = False self._after_id = '' self.img_play = PhotoImage(master=self, file=IM_START) self.img_pause = PhotoImage(master=self, file=IM_PAUSE) self.img_stop = PhotoImage(master=self, file=IM_STOP) self.rowconfigure(2, weight=1) self.columnconfigure(0, weight=1) self.columnconfigure(1, weight=1) # --- GUI elements self.display = Label(self, text='%i:%.2i:%.2i' % tuple(self._time), anchor='center', style='timer.TLabel') self.intervals = Text(self, highlightthickness=0, relief='flat', height=3, width=1, inactiveselectbackground=self.style.lookup('TEntry', 'selectbackground')) self.intervals.tag_configure('center', justify='center') self.intervals.configure(state='disabled') self.b_interv = Button(self, text=_('Interval'), style='timer.TButton', command=self.add_interval) self.b_interv.state(('disabled',)) self.b_launch = Button(self, image=self.img_play, padding=2, command=self.launch, style='timer.TButton') self.b_stop = Button(self, image=self.img_stop, padding=2, command=self.stop, style='timer.TButton') # --- placement self.display.grid(row=0, columnspan=2, sticky='ew', padx=8, pady=(4, 0)) Label(self, text=_('Intervals:'), style='timer.TLabel').grid(row=1, columnspan=2, sticky='w', padx=4) self.intervals.grid(row=2, columnspan=2, sticky='eswn') self.b_interv.grid(row=3, columnspan=2, sticky='ew') self.b_launch.grid(row=4, column=0, sticky='ew') self.b_stop.grid(row=4, column=1, sticky='ew') self._corner = Sizegrip(self, style="timer.TSizegrip") self._corner.place(relx=1, rely=1, anchor='se') # --- bindings self.intervals.bind("<1>", lambda event: self.intervals.focus_set()) self.bind('<3>', lambda e: self.menu.tk_popup(e.x_root, e.y_root)) self.display.bind('<ButtonPress-1>', self._start_move) self.display.bind('<ButtonRelease-1>', self._stop_move) self.display.bind('<B1-Motion>', self._move) self.b_stop.bind('<Enter>', self._on_enter) self.b_stop.bind('<Leave>', self._on_leave) def update_style(self): self.attributes('-alpha', CONFIG.get(self.name, 'alpha', fallback=0.85)) bg = CONFIG.get('Timer', 'background') fg = CONFIG.get('Timer', 'foreground') active_bg = active_color(*self.winfo_rgb(bg)) self.configure(bg=bg) self.menu.configure(bg=bg, fg=fg, selectcolor=fg, activeforeground=fg, activebackground=active_bg) self.menu_pos.configure(bg=bg, fg=fg, selectcolor=fg, activeforeground=fg, activebackground=active_bg) self.display.configure(font=CONFIG.get('Timer', 'font_time')) self.intervals.configure(bg=bg, fg=fg, font=CONFIG.get('Timer', 'font_intervals')) self.style.configure('timer.TButton', background=bg, relief='flat', foreground=fg, borderwidth=0) self.style.configure('timer.TLabel', background=bg, foreground=fg) self.style.configure('timer.TSizegrip', background=bg) self.style.map('timer.TSizegrip', background=[('active', active_bg)]) self.style.map('timer.TButton', background=[('disabled', bg), ('!disabled', 'active', active_bg)]) def _on_enter(self, event=None): self._corner.state(('active',)) def _on_leave(self, event=None): self._corner.state(('!active',)) def show(self): self.deiconify() self.update_idletasks() self.withdraw() if self._position.get() == 'above': self.overrideredirect(True) else: self.overrideredirect(False) BaseWidget.show(self) self.update_idletasks() self.withdraw() self.deiconify() def _run(self): if self._on: self._time[2] += 1 if self._time[2] == 60: self._time[2] = 0 self._time[1] += 1 if self._time[1] == 60: self._time[1] = 0 self._time[0] += 1 self.display.configure(text='%i:%.2i:%.2i' % tuple(self._time)) self._after_id = self.after(1000, self._run) def launch(self): if self._on: self._on = False self.b_launch.configure(image=self.img_play) self.b_interv.state(('disabled',)) else: self._on = True self.b_interv.state(('!disabled',)) self.b_launch.configure(image=self.img_pause) self.after(1000, self._run) def add_interval(self): tps = '\n%i:%.2i:%.2i' % tuple(self._time) if self.intervals.get('1.0', 'end') == '\n': tps = tps[1:] self.intervals.configure(state='normal') self.intervals.insert('end', tps, 'center') self.intervals.configure(state='disabled') def stop(self): self._on = False self.b_interv.state(('disabled',)) self.b_launch.configure(image=self.img_play) self._time = [0, 0, 0] self.intervals.configure(state='normal') self.intervals.delete('1.0', 'end') self.intervals.configure(state='disabled') self.display.configure(text='%i:%.2i:%.2i' % tuple(self._time))
class Sticky(Toplevel): """ Sticky note class """ def __init__(self, master, key, **kwargs): """ Create a new sticky note. master: main app key: key identifying this note in master.note_data kwargs: dictionnary of the other arguments (title, txt, category, color, tags, geometry, locked, checkboxes, images, rolled) """ Toplevel.__init__(self, master) # --- window properties self.id = key self.is_locked = not (kwargs.get("locked", False)) self.images = [] self.links = {} self.latex = {} self.nb_links = 0 self.title('mynotes%s' % key) self.attributes("-type", "splash") self.attributes("-alpha", CONFIG.getint("General", "opacity") / 100) self.focus_force() # window geometry self.update_idletasks() self.geometry(kwargs.get("geometry", '220x235')) self.save_geometry = kwargs.get("geometry", '220x235') self.update() self.rowconfigure(1, weight=1) self.minsize(10, 10) self.protocol("WM_DELETE_WINDOW", self.hide) # --- style self.style = Style(self) self.style.configure(self.id + ".TCheckbutton", selectbackground="red") self.style.map('TEntry', selectbackground=[('!focus', '#c3c3c3')]) selectbg = self.style.lookup('TEntry', 'selectbackground', ('focus', )) self.style.configure("sel.TCheckbutton", background=selectbg) self.style.map("sel.TCheckbutton", background=[("active", selectbg)]) # --- note elements # title font_title = "%s %s" % (CONFIG.get("Font", "title_family").replace( " ", "\ "), CONFIG.get("Font", "title_size")) style = CONFIG.get("Font", "title_style").split(",") if style: font_title += " " font_title += " ".join(style) self.title_var = StringVar(master=self, value=kwargs.get("title", _("Title"))) self.title_label = Label(self, textvariable=self.title_var, anchor="center", style=self.id + ".TLabel", font=font_title) self.title_entry = Entry(self, textvariable=self.title_var, exportselection=False, justify="center", font=font_title) # buttons/icons self.roll = Label(self, image="img_roll", style=self.id + ".TLabel") self.close = Label(self, image="img_close", style=self.id + ".TLabel") self.im_lock = PhotoImage(master=self, file=IM_LOCK) self.cadenas = Label(self, style=self.id + ".TLabel") # corner grip self.corner = Sizegrip(self, style=self.id + ".TSizegrip") # texte font_text = "%s %s" % (CONFIG.get("Font", "text_family").replace( " ", "\ "), CONFIG.get("Font", "text_size")) self.txt = Text(self, wrap='word', undo=True, selectforeground='white', inactiveselectbackground=selectbg, selectbackground=selectbg, tabs=(10, 'right', 21, 'left'), relief="flat", borderwidth=0, highlightthickness=0, font=font_text) # tags self.txt.tag_configure("bold", font="%s bold" % font_text) self.txt.tag_configure("italic", font="%s italic" % font_text) self.txt.tag_configure("bold-italic", font="%s bold italic" % font_text) self.txt.tag_configure("underline", underline=True, selectforeground="white") self.txt.tag_configure("overstrike", overstrike=True, selectforeground="white") self.txt.tag_configure("center", justify="center") self.txt.tag_configure("left", justify="left") self.txt.tag_configure("right", justify="right") self.txt.tag_configure("link", foreground="blue", underline=True, selectforeground="white") self.txt.tag_configure("list", lmargin1=0, lmargin2=21, tabs=(10, 'right', 21, 'left')) self.txt.tag_configure("todolist", lmargin1=0, lmargin2=21, tabs=(10, 'right', 21, 'left')) margin = 2 * Font(self, font=font_text).measure("m") self.txt.tag_configure("enum", lmargin1=0, lmargin2=margin + 5, tabs=(margin, 'right', margin + 5, 'left')) for coul in TEXT_COLORS.values(): self.txt.tag_configure(coul, foreground=coul, selectforeground="white") self.txt.tag_configure(coul + "-underline", foreground=coul, selectforeground="white", underline=True) self.txt.tag_configure(coul + "-overstrike", foreground=coul, overstrike=True, selectforeground="white") # --- menus # --- * menu on title self.menu = Menu(self, tearoff=False) # note color menu_note_color = Menu(self.menu, tearoff=False) colors = list(COLORS.keys()) colors.sort() for coul in colors: menu_note_color.add_command( label=coul, command=lambda key=coul: self.change_color(key)) # category self.category = StringVar( self, kwargs.get("category", CONFIG.get("General", "default_category"))) self.menu_categories = Menu(self.menu, tearoff=False) categories = CONFIG.options("Categories") categories.sort() for cat in categories: self.menu_categories.add_radiobutton(label=cat.capitalize(), value=cat, variable=self.category, command=self.change_category) # position: normal, always above, always below self.position = StringVar( self, kwargs.get("position", CONFIG.get("General", "position"))) menu_position = Menu(self.menu, tearoff=False) menu_position.add_radiobutton(label=_("Always above"), value="above", variable=self.position, command=self.set_position_above) menu_position.add_radiobutton(label=_("Always below"), value="below", variable=self.position, command=self.set_position_below) menu_position.add_radiobutton(label=_("Normal"), value="normal", variable=self.position, command=self.set_position_normal) # mode: note, list, todo list menu_mode = Menu(self.menu, tearoff=False) self.mode = StringVar(self, kwargs.get("mode", "note")) menu_mode.add_radiobutton(label=_("Note"), value="note", variable=self.mode, command=self.set_mode_note) menu_mode.add_radiobutton(label=_("List"), value="list", variable=self.mode, command=self.set_mode_list) menu_mode.add_radiobutton(label=_("ToDo List"), value="todolist", variable=self.mode, command=self.set_mode_todolist) menu_mode.add_radiobutton(label=_("Enumeration"), value="enum", variable=self.mode, command=self.set_mode_enum) self.menu.add_command(label=_("Delete"), command=self.delete) self.menu.add_cascade(label=_("Category"), menu=self.menu_categories) self.menu.add_cascade(label=_("Color"), menu=menu_note_color) self.menu.add_command(label=_("Lock"), command=self.lock) self.menu.add_cascade(label=_("Position"), menu=menu_position) self.menu.add_cascade(label=_("Mode"), menu=menu_mode) # --- * menu on main text self.menu_txt = Menu(self.txt, tearoff=False) # style menu_style = Menu(self.menu_txt, tearoff=False) menu_style.add_command(label=_("Bold"), command=lambda: self.toggle_text_style("bold")) menu_style.add_command( label=_("Italic"), command=lambda: self.toggle_text_style("italic")) menu_style.add_command(label=_("Underline"), command=self.toggle_underline) menu_style.add_command(label=_("Overstrike"), command=self.toggle_overstrike) # text alignment menu_align = Menu(self.menu_txt, tearoff=False) menu_align.add_command(label=_("Left"), command=lambda: self.set_align("left")) menu_align.add_command(label=_("Right"), command=lambda: self.set_align("right")) menu_align.add_command(label=_("Center"), command=lambda: self.set_align("center")) # text color menu_colors = Menu(self.menu_txt, tearoff=False) colors = list(TEXT_COLORS.keys()) colors.sort() for coul in colors: menu_colors.add_command(label=coul, command=lambda key=coul: self. change_sel_color(TEXT_COLORS[key])) # insert menu_insert = Menu(self.menu_txt, tearoff=False) menu_insert.add_command(label=_("Symbols"), command=self.add_symbols) menu_insert.add_command(label=_("Checkbox"), command=self.add_checkbox) menu_insert.add_command(label=_("Image"), command=self.add_image) menu_insert.add_command(label=_("Date"), command=self.add_date) menu_insert.add_command(label=_("Link"), command=self.add_link) if LATEX: menu_insert.add_command(label="LaTex", command=self.add_latex) self.menu_txt.add_cascade(label=_("Style"), menu=menu_style) self.menu_txt.add_cascade(label=_("Alignment"), menu=menu_align) self.menu_txt.add_cascade(label=_("Color"), menu=menu_colors) self.menu_txt.add_cascade(label=_("Insert"), menu=menu_insert) # --- restore note content/appearence self.color = kwargs.get("color", CONFIG.get("Categories", self.category.get())) self.txt.insert('1.0', kwargs.get("txt", "")) self.txt.edit_reset() # clear undo stack # restore inserted objects (images and checkboxes) # we need to restore objects with increasing index to avoid placment errors indexes = list(kwargs.get("inserted_objects", {}).keys()) indexes.sort(key=sorting) for index in indexes: kind, val = kwargs["inserted_objects"][index] if kind == "checkbox": ch = Checkbutton(self.txt, takefocus=False, style=self.id + ".TCheckbutton") if val: ch.state(("selected", )) self.txt.window_create(index, window=ch) elif kind == "image": if os.path.exists(val): self.images.append(PhotoImage(master=self.txt, file=val)) self.txt.image_create(index, image=self.images[-1], name=val) # restore tags for tag, indices in kwargs.get("tags", {}).items(): if indices: self.txt.tag_add(tag, *indices) for link in kwargs.get("links", {}).values(): self.nb_links += 1 self.links[self.nb_links] = link self.txt.tag_bind("link#%i" % self.nb_links, "<Button-1>", lambda e, l=link: open_url(l)) for img, latex in kwargs.get("latex", {}).items(): self.latex[img] = latex if LATEX: self.txt.tag_bind(img, '<Double-Button-1>', lambda e, im=img: self.add_latex(im)) mode = self.mode.get() if mode != "note": self.txt.tag_add(mode, "1.0", "end") self.txt.focus_set() self.lock() if kwargs.get("rolled", False): self.rollnote() if self.position.get() == "above": self.set_position_above() elif self.position.get() == "below": self.set_position_below() # --- placement # titlebar if CONFIG.get("General", "buttons_position") == "right": # right = lock icon - title - roll - close self.columnconfigure(1, weight=1) self.roll.grid(row=0, column=2, sticky="e") self.close.grid(row=0, column=3, sticky="e", padx=(0, 2)) self.cadenas.grid(row=0, column=0, sticky="w") self.title_label.grid(row=0, column=1, sticky="ew", pady=(1, 0)) else: # left = close - roll - title - lock icon self.columnconfigure(2, weight=1) self.roll.grid(row=0, column=1, sticky="w") self.close.grid(row=0, column=0, sticky="w", padx=(2, 0)) self.cadenas.grid(row=0, column=3, sticky="e") self.title_label.grid(row=0, column=2, sticky="ew", pady=(1, 0)) # body self.txt.grid(row=1, columnspan=4, column=0, sticky="ewsn", pady=(1, 4), padx=4) self.corner.lift(self.txt) self.corner.place(relx=1.0, rely=1.0, anchor="se") # --- bindings self.bind("<FocusOut>", self.save_note) self.bind('<Configure>', self.bouge) self.bind('<Button-1>', self.change_focus, True) self.close.bind("<Button-1>", self.hide) self.close.bind("<Enter>", self.enter_close) self.close.bind("<Leave>", self.leave_close) self.roll.bind("<Button-1>", self.rollnote) self.roll.bind("<Enter>", self.enter_roll) self.roll.bind("<Leave >", self.leave_roll) self.title_label.bind("<Double-Button-1>", self.edit_title) self.title_label.bind("<ButtonPress-1>", self.start_move) self.title_label.bind("<ButtonRelease-1>", self.stop_move) self.title_label.bind("<B1-Motion>", self.move) self.title_label.bind('<Button-3>', self.show_menu) self.title_entry.bind("<Return>", lambda e: self.title_entry.place_forget()) self.title_entry.bind("<FocusOut>", lambda e: self.title_entry.place_forget()) self.title_entry.bind("<Escape>", lambda e: self.title_entry.place_forget()) self.txt.tag_bind("link", "<Enter>", lambda event: self.txt.configure(cursor="hand1")) self.txt.tag_bind("link", "<Leave>", lambda event: self.txt.configure(cursor="")) self.txt.bind("<FocusOut>", self.save_note) self.txt.bind('<Button-3>', self.show_menu_txt) # add binding to the existing class binding so that the selected text # is erased on pasting self.txt.bind("<Control-v>", self.paste) self.corner.bind('<ButtonRelease-1>', self.resize) # --- keyboard shortcuts self.txt.bind('<Control-b>', lambda e: self.toggle_text_style('bold')) self.txt.bind('<Control-i>', lambda e: self.toggle_text_style('italic')) self.txt.bind('<Control-u>', lambda e: self.toggle_underline()) self.txt.bind('<Control-r>', lambda e: self.set_align('right')) self.txt.bind('<Control-l>', lambda e: self.set_align('left')) def __setattr__(self, name, value): object.__setattr__(self, name, value) if name == "color": self.style.configure(self.id + ".TSizegrip", background=self.color) self.style.configure(self.id + ".TLabel", background=self.color) self.style.configure("close" + self.id + ".TLabel", background=self.color) self.style.configure("roll" + self.id + ".TLabel", background=self.color) self.style.map(self.id + ".TLabel", background=[("active", self.color)]) self.style.configure(self.id + ".TCheckbutton", background=self.color) self.style.map(self.id + ".TCheckbutton", background=[("active", self.color), ("disabled", self.color)]) self.style.map("close" + self.id + ".TLabel", background=[("active", self.color)]) self.style.map("roll" + self.id + ".TLabel", background=[("active", self.color)]) self.configure(bg=self.color) self.txt.configure(bg=self.color) def paste(self, event): """ delete selected text before pasting """ if self.txt.tag_ranges("sel"): self.txt.delete("sel.first", "sel.last") def delete(self, confirmation=True): """ Delete this note """ if confirmation: rep = askokcancel(_("Confirmation"), _("Delete the note?")) else: rep = True if rep: del (self.master.note_data[self.id]) del (self.master.notes[self.id]) self.master.save() self.destroy() def lock(self): """ Put note in read-only mode to avoid unwanted text insertion """ if self.is_locked: selectbg = self.style.lookup('TEntry', 'selectbackground', ('focus', )) self.txt.configure(state="normal", selectforeground='white', selectbackground=selectbg, inactiveselectbackground=selectbg) self.style.configure("sel.TCheckbutton", background=selectbg) self.style.map("sel.TCheckbutton", background=[("active", selectbg)]) self.is_locked = False for checkbox in self.txt.window_names(): ch = self.txt.children[checkbox.split(".")[-1]] ch.configure(state="normal") self.cadenas.configure(image="") self.menu.entryconfigure(3, label=_("Lock")) self.title_label.bind("<Double-Button-1>", self.edit_title) self.txt.bind('<Button-3>', self.show_menu_txt) else: self.txt.configure(state="disabled", selectforeground='black', inactiveselectbackground='#c3c3c3', selectbackground='#c3c3c3') self.style.configure("sel.TCheckbutton", background='#c3c3c3') self.style.map("sel.TCheckbutton", background=[("active", '#c3c3c3')]) self.cadenas.configure(image=self.im_lock) for checkbox in self.txt.window_names(): ch = self.txt.children[checkbox.split(".")[-1]] ch.configure(state="disabled") self.is_locked = True self.menu.entryconfigure(3, label=_("Unlock")) self.title_label.unbind("<Double-Button-1>") self.txt.unbind('<Button-3>') self.save_note() def save_info(self): """ Return the dictionnary containing all the note data """ data = {} data["txt"] = self.txt.get("1.0", "end")[:-1] data["tags"] = {} for tag in self.txt.tag_names(): if tag not in ["sel", "todolist", "list", "enum"]: data["tags"][tag] = [ index.string for index in self.txt.tag_ranges(tag) ] data["title"] = self.title_var.get() data["geometry"] = self.save_geometry data["category"] = self.category.get() data["color"] = self.color data["locked"] = self.is_locked data["mode"] = self.mode.get() data["inserted_objects"] = {} data["rolled"] = not self.txt.winfo_ismapped() data["position"] = self.position.get() data["links"] = {} for i, link in self.links.items(): if self.txt.tag_ranges("link#%i" % i): data["links"][i] = link data["latex"] = {} for img, latex in self.latex.items(): if self.txt.tag_ranges(img): data["latex"][img] = latex for image in self.txt.image_names(): data["inserted_objects"][self.txt.index(image)] = ( "image", image.split('#')[0]) for checkbox in self.txt.window_names(): ch = self.txt.children[checkbox.split(".")[-1]] data["inserted_objects"][self.txt.index(checkbox)] = ( "checkbox", "selected" in ch.state()) return data def change_color(self, key): self.color = COLORS[key] self.save_note() def change_category(self, category=None): if category: self.category.set(category) self.color = CONFIG.get("Categories", self.category.get()) self.save_note() def set_position_above(self): e = ewmh.EWMH() for w in e.getClientList(): if w.get_wm_name() == 'mynotes%s' % self.id: e.setWmState(w, 1, '_NET_WM_STATE_ABOVE') e.setWmState(w, 0, '_NET_WM_STATE_BELOW') e.display.flush() self.save_note() def set_position_below(self): e = ewmh.EWMH() for w in e.getClientList(): if w.get_wm_name() == 'mynotes%s' % self.id: e.setWmState(w, 0, '_NET_WM_STATE_ABOVE') e.setWmState(w, 1, '_NET_WM_STATE_BELOW') e.display.flush() self.save_note() def set_position_normal(self): e = ewmh.EWMH() for w in e.getClientList(): if w.get_wm_name() == 'mynotes%s' % self.id: e.setWmState(w, 0, '_NET_WM_STATE_BELOW') e.setWmState(w, 0, '_NET_WM_STATE_ABOVE') e.display.flush() self.save_note() def set_mode_note(self): self.txt.tag_remove("list", "1.0", "end") self.txt.tag_remove("todolist", "1.0", "end") self.txt.tag_remove("enum", "1.0", "end") self.save_note() def set_mode_list(self): end = int(self.txt.index("end").split(".")[0]) lines = self.txt.get("1.0", "end").splitlines() for i, l in zip(range(1, end), lines): # remove checkboxes try: ch = self.txt.window_cget("%i.0" % i, "window") self.txt.children[ch.split('.')[-1]].destroy() self.txt.delete("%i.0" % i) except TclError: # there is no checkbox # remove enumeration res = re.match('^\t[0-9]+\.\t', l) if res: self.txt.delete("%i.0" % i, "%i.%i" % (i, res.end())) if self.txt.get("%i.0" % i, "%i.3" % i) != "\t•\t": self.txt.insert("%i.0" % i, "\t•\t") self.txt.tag_add("list", "1.0", "end") self.txt.tag_remove("todolist", "1.0", "end") self.txt.tag_remove("enum", "1.0", "end") self.save_note() def set_mode_enum(self): self.txt.configure(autoseparators=False) self.txt.edit_separator() end = int(self.txt.index("end").split(".")[0]) lines = self.txt.get("1.0", "end").splitlines() for i, l in zip(range(1, end), lines): # remove checkboxes try: ch = self.txt.window_cget("%i.0" % i, "window") self.txt.children[ch.split('.')[-1]].destroy() self.txt.delete("%i.0" % i) except TclError: # there is no checkbox # remove bullets if self.txt.get("%i.0" % i, "%i.3" % i) == "\t•\t": self.txt.delete("%i.0" % i, "%i.3" % i) if not re.match('^\t[0-9]+\.', l): self.txt.insert("%i.0" % i, "\t0.\t") self.txt.tag_add("enum", "1.0", "end") self.txt.tag_remove("todolist", "1.0", "end") self.txt.tag_remove("list", "1.0", "end") self.update_enum() self.txt.configure(autoseparators=True) self.txt.edit_separator() self.save_note() def set_mode_todolist(self): end = int(self.txt.index("end").split(".")[0]) lines = self.txt.get("1.0", "end").splitlines() for i, l in zip(range(1, end), lines): res = re.match('^\t[0-9]+\.\t', l) if res: self.txt.delete("%i.0" % i, "%i.%i" % (i, res.end())) elif self.txt.get("%i.0" % i, "%i.3" % i) == "\t•\t": self.txt.delete("%i.0" % i, "%i.3" % i) try: ch = self.txt.window_cget("%i.0" % i, "window") except TclError: ch = Checkbutton(self.txt, takefocus=False, style=self.id + ".TCheckbutton") self.txt.window_create("%i.0" % i, window=ch) self.txt.tag_remove("enum", "1.0", "end") self.txt.tag_remove("list", "1.0", "end") self.txt.tag_add("todolist", "1.0", "end") self.save_note() # --- bindings def enter_roll(self, event): """ mouse is over the roll icon """ self.roll.configure(image="img_rollactive") def leave_roll(self, event): """ mouse leaves the roll icon """ self.roll.configure(image="img_roll") def enter_close(self, event): """ mouse is over the close icon """ self.close.configure(image="img_closeactive") def leave_close(self, event): """ mouse leaves the close icon """ self.close.configure(image="img_close") def change_focus(self, event): if not self.is_locked: event.widget.focus_force() def show_menu(self, event): self.menu.tk_popup(event.x_root, event.y_root) def show_menu_txt(self, event): self.menu_txt.tk_popup(event.x_root, event.y_root) def resize(self, event): self.save_geometry = self.geometry() def bouge(self, event): geo = self.geometry().split("+")[1:] self.save_geometry = self.save_geometry.split("+")[0] \ + "+%s+%s" % tuple(geo) def edit_title(self, event): self.title_entry.place(x=self.title_label.winfo_x() + 5, y=self.title_label.winfo_y(), anchor="nw", width=self.title_label.winfo_width() - 10) def start_move(self, event): self.x = event.x self.y = event.y self.configure(cursor='fleur') def stop_move(self, event): self.x = None self.y = None self.configure(cursor='') def move(self, event): if self.x is not None and self.y is not None: deltax = event.x - self.x deltay = event.y - self.y x = self.winfo_x() + deltax y = self.winfo_y() + deltay self.geometry("+%s+%s" % (x, y)) def save_note(self, event=None): data = self.save_info() data["visible"] = True self.master.note_data[self.id] = data self.master.save() def rollnote(self, event=None): if self.txt.winfo_ismapped(): self.txt.grid_forget() self.corner.place_forget() self.geometry("%sx22" % self.winfo_width()) else: self.txt.grid(row=1, columnspan=4, column=0, sticky="ewsn", pady=(1, 4), padx=4) self.corner.place(relx=1.0, rely=1.0, anchor="se") self.geometry(self.save_geometry) self.save_note() def hide(self, event=None): """ Hide note (can be displayed again via app menu) """ cat = self.category.get() self.master.add_note_to_menu(self.id, self.title_var.get().strip(), cat) data = self.save_info() data["visible"] = False self.master.note_data[self.id] = data del (self.master.notes[self.id]) self.master.save() self.destroy() # --- Settings update def update_title_font(self): font = "%s %s" % (CONFIG.get("Font", "title_family").replace( " ", "\ "), CONFIG.get("Font", "title_size")) style = CONFIG.get("Font", "title_style").split(",") if style: font += " " font += " ".join(style) self.title_label.configure(font=font) def update_text_font(self): font = "%s %s" % (CONFIG.get("Font", "text_family").replace( " ", "\ "), CONFIG.get("Font", "text_size")) self.txt.configure(font=font) self.txt.tag_configure("bold", font="%s bold" % font) self.txt.tag_configure("italic", font="%s italic" % font) self.txt.tag_configure("bold-italic", font="%s bold italic" % font) margin = 2 * Font(self, font=font).measure("m") self.txt.tag_configure("enum", lmargin1=0, lmargin2=margin + 5, tabs=(margin, 'right', margin + 5, 'left')) def update_menu_cat(self, categories): """ Update the category submenu """ self.menu_categories.delete(0, "end") for cat in categories: self.menu_categories.add_radiobutton(label=cat.capitalize(), value=cat, variable=self.category, command=self.change_category) def update_titlebar(self): if CONFIG.get("General", "buttons_position") == "right": # right = lock icon - title - roll - close self.columnconfigure(1, weight=1) self.columnconfigure(2, weight=0) self.roll.grid_configure(row=0, column=2, sticky="e") self.close.grid_configure(row=0, column=3, sticky="e", padx=(0, 2)) self.cadenas.grid_configure(row=0, column=0, sticky="w") self.title_label.grid_configure(row=0, column=1, sticky="ew", pady=(1, 0)) else: # left = close - roll - title - lock icon self.columnconfigure(2, weight=1) self.columnconfigure(1, weight=0) self.roll.grid_configure(row=0, column=1, sticky="w") self.close.grid_configure(row=0, column=0, sticky="w", padx=(2, 0)) self.cadenas.grid_configure(row=0, column=3, sticky="e") self.title_label.grid_configure(row=0, column=2, sticky="ew", pady=(1, 0)) # --- Text edition def add_link(self): def ok(eveny=None): lien = link.get() txt = text.get() if lien: if not txt: txt = lien self.nb_links += 1 if self.txt.tag_ranges("sel"): index = self.txt.index("sel.first") self.txt.delete('sel.first', 'sel.last') else: index = "current" tags = self.txt.tag_names(index) + ("link", "link#%i" % self.nb_links) self.txt.insert("current", txt, tags) if not lien[:4] == "http": lien = "http://" + lien self.links[self.nb_links] = lien self.txt.tag_bind("link#%i" % self.nb_links, "<Button-1>", lambda e: open_url(lien)) top.destroy() top = Toplevel(self) top.transient(self) top.update_idletasks() top.geometry("+%i+%i" % top.winfo_pointerxy()) top.grab_set() top.resizable(True, False) top.title(_("Link")) top.columnconfigure(1, weight=1) text = Entry(top) link = Entry(top) if self.txt.tag_ranges('sel'): txt = self.txt.get('sel.first', 'sel.last') else: txt = '' text.insert(0, txt) text.icursor("end") Label(top, text=_("Text")).grid(row=0, column=0, sticky="e", padx=4, pady=4) Label(top, text=_("Link")).grid(row=1, column=0, sticky="e", padx=4, pady=4) text.grid(row=0, column=1, sticky="ew", padx=4, pady=4) link.grid(row=1, column=1, sticky="ew", padx=4, pady=4) Button(top, text="Ok", command=ok).grid(row=2, columnspan=2, padx=4, pady=4) text.focus_set() text.bind("<Return>", ok) link.bind("<Return>", ok) def add_checkbox(self): ch = Checkbutton(self.txt, takefocus=False, style=self.id + ".TCheckbutton") self.txt.window_create("current", window=ch) def add_date(self): self.txt.insert("current", strftime("%x")) def add_latex(self, img_name=None): def ok(event): latex = r'%s' % text.get() if latex: if img_name is None: l = [ int(os.path.splitext(f)[0]) for f in os.listdir(PATH_LATEX) ] l.sort() if l: i = l[-1] + 1 else: i = 0 img = "%i.png" % i self.txt.tag_bind(img, '<Double-Button-1>', lambda e: self.add_latex(img)) self.latex[img] = latex else: img = img_name im = os.path.join(PATH_LATEX, img) try: math_to_image(latex, im, fontsize=CONFIG.getint("Font", "text_size") - 2) self.images.append(PhotoImage(file=im, master=self)) if self.txt.tag_ranges("sel"): index = self.txt.index("sel.first") self.txt.delete('sel.first', 'sel.last') else: index = self.txt.index("current") self.txt.image_create(index, image=self.images[-1], name=im) self.txt.tag_add(img, index) top.destroy() except Exception as e: showerror(_("Error"), str(e)) top = Toplevel(self) top.transient(self) top.update_idletasks() top.geometry("+%i+%i" % top.winfo_pointerxy()) top.grab_set() top.resizable(True, False) top.title("LaTex") text = Entry(top, justify='center') if img_name is not None: text.insert(0, self.latex[img_name]) else: if self.txt.tag_ranges('sel'): text.insert(0, self.txt.get('sel.first', 'sel.last')) else: text.insert(0, '$$') text.icursor(1) text.pack(fill='x', expand=True) text.bind('<Return>', ok) text.focus_set() def add_image(self): fichier = askopenfilename(defaultextension=".png", filetypes=[("PNG", "*.png")], initialdir="", initialfile="", title=_('Select PNG image')) if os.path.exists(fichier): self.images.append(PhotoImage(master=self.txt, file=fichier)) self.txt.image_create("current", image=self.images[-1], name=fichier) elif fichier: showerror("Erreur", "L'image %s n'existe pas" % fichier) def add_symbols(self): symbols = pick_symbol( self, CONFIG.get("Font", "text_family").replace(" ", "\ "), CONFIG.get("General", "symbols")) self.txt.insert("current", symbols) def toggle_text_style(self, style): '''Toggle the style of the selected text''' if self.txt.tag_ranges("sel"): current_tags = self.txt.tag_names("sel.first") if style in current_tags: # first char is in style so 'unstyle' the range self.txt.tag_remove(style, "sel.first", "sel.last") elif style == "bold" and "bold-italic" in current_tags: self.txt.tag_remove("bold-italic", "sel.first", "sel.last") self.txt.tag_add("italic", "sel.first", "sel.last") elif style == "italic" and "bold-italic" in current_tags: self.txt.tag_remove("bold-italic", "sel.first", "sel.last") self.txt.tag_add("bold", "sel.first", "sel.last") elif style == "bold" and "italic" in current_tags: self.txt.tag_remove("italic", "sel.first", "sel.last") self.txt.tag_add("bold-italic", "sel.first", "sel.last") elif style == "italic" and "bold" in current_tags: self.txt.tag_remove("bold", "sel.first", "sel.last") self.txt.tag_add("bold-italic", "sel.first", "sel.last") else: # first char is normal, so apply style to the whole selection self.txt.tag_add(style, "sel.first", "sel.last") def toggle_underline(self): if self.txt.tag_ranges("sel"): current_tags = self.txt.tag_names("sel.first") if "underline" in current_tags: # first char is in style so 'unstyle' the range self.txt.tag_remove("underline", "sel.first", "sel.last") for coul in TEXT_COLORS.values(): self.txt.tag_remove(coul + "-underline", "sel.first", "sel.last") else: self.txt.tag_add("underline", "sel.first", "sel.last") for coul in TEXT_COLORS.values(): r = text_ranges(self.txt, coul, "sel.first", "sel.last") if r: for deb, fin in zip(r[::2], r[1::2]): self.txt.tag_add(coul + "-underline", "sel.first", "sel.last") def toggle_overstrike(self): if self.txt.tag_ranges("sel"): current_tags = self.txt.tag_names("sel.first") if "overstrike" in current_tags: # first char is in style so 'unstyle' the range self.txt.tag_remove("overstrike", "sel.first", "sel.last") for coul in TEXT_COLORS.values(): self.txt.tag_remove(coul + "-overstrike", "sel.first", "sel.last") else: self.txt.tag_add("overstrike", "sel.first", "sel.last") for coul in TEXT_COLORS.values(): r = text_ranges(self.txt, coul, "sel.first", "sel.last") if r: for deb, fin in zip(r[::2], r[1::2]): self.txt.tag_add(coul + "-overstrike", "sel.first", "sel.last") def change_sel_color(self, color): """ change the color of the selection """ if self.txt.tag_ranges("sel"): for coul in TEXT_COLORS.values(): self.txt.tag_remove(coul, "sel.first", "sel.last") self.txt.tag_remove(coul + "-overstrike", "sel.first", "sel.last") self.txt.tag_remove(coul + "-underline", "sel.first", "sel.last") if not color == "black": self.txt.tag_add(color, "sel.first", "sel.last") underline = text_ranges(self.txt, "underline", "sel.first", "sel.last") overstrike = text_ranges(self.txt, "overstrike", "sel.first", "sel.last") for deb, fin in zip(underline[::2], underline[1::2]): self.txt.tag_add(color + "-underline", deb, fin) for deb, fin in zip(overstrike[::2], overstrike[1::2]): self.txt.tag_add(color + "-overstrike", deb, fin) def set_align(self, alignment): """ Align the text according to alignment (left, right, center) """ if self.txt.tag_ranges("sel"): line = self.txt.index("sel.first").split(".")[0] line2 = self.txt.index("sel.last").split(".")[0] deb, fin = line + ".0", line2 + ".end" if not "\t" in self.txt.get(deb, fin): # tabulations don't support right/center alignment # remove old alignment tag self.txt.tag_remove("left", deb, fin) self.txt.tag_remove("right", deb, fin) self.txt.tag_remove("center", deb, fin) # set new alignment tag self.txt.tag_add(alignment, deb, fin) def update_enum(self): """ update enumeration numbers """ lines = self.txt.get("1.0", "end").splitlines() indexes = [] for i, l in enumerate(lines): res = re.match('^\t[0-9]+\.\t', l) res2 = re.match('^\t[0-9]+\.', l) if res: indexes.append((i, res.end())) elif res2: indexes.append((i, res2.end())) for j, (i, end) in enumerate(indexes): self.txt.delete("%i.0" % (i + 1), "%i.%i" % (i + 1, end)) self.txt.insert("%i.0" % (i + 1), "\t%i.\t" % (j + 1)) self.txt.tag_add("enum", "1.0", "end")
def init_window(self): # master window self.master.title("Scraper") # labels l1 = Label( self.master, text= 'Lets do some scraping, fill in the fields and hit search. Then enjoy!', fg="red") l1.grid(column=0, row=0, columnspan=4) l2 = Label(self.master, text="url or list of url's to search:") l2.grid(column=0, row=4) l3 = Label(self.master, text="location to save to:") l3.grid(column=0, row=5) l4 = Label(self.master, text="String to search for:", fg="blue") l4.grid(column=0, row=3) # buttons #b1 = Button(self.master, text="secret", command=self.secret, bg="green", activebackground="orange") #b1.grid(column=0, row=0, sticky=W) b2 = Button(self.master, text="quit", command=self.quit, fg="red", activebackground="red") b2.grid(column=0, row=6, sticky=W, padx=5) b3 = Button(self.master, text="Browse", command=self.browse1, bg="lime", activebackground="tan") b3.grid(column=3, row=4, sticky=E) b4 = Button(self.master, text="Browse", command=self.browse2, bg="lime", activebackground="tan") b4.grid(column=3, row=5, sticky=E) b5 = Button(self.master, text="Search", command=self.search, bg="orange", activebackground="pink") b5.grid(column=3, row=6, sticky=W) # Data Entry self.e1 = Entry(self.master) # default relief is SUNKEN self.e1.grid(column=1, row=4, sticky=(W, E), pady=5, ipady=3) self.e2 = Entry(self.master) self.e2.grid(column=1, row=5, sticky=(W, E), pady=5, ipady=3) self.e3 = Entry(self.master) self.e3.grid(column=1, row=3, sticky=(W, E), pady=5, ipady=3) # Sizegrip self.sg = Sizegrip(self.master).grid(column=999, row=999, sticky=(S, E)) self.master.rowconfigure(0, weight=1) self.master.columnconfigure(0, weight=1) # these make sizegrip work # Progressbar self.pb = Progressbar(self.master, orient=HORIZONTAL, length=200, mode='indeterminate') self.pb.grid(column=1, row=6, sticky=W)
context_menu.add_command(label='Cut', command=cut_text) context_menu.add_command(label='Copy', command=copy_text) context_menu.add_command(label='Paste', command=paste_text) context_menu.add_separator() context_menu.add_command(label='Delete', command=delete_text) root.bind('<3>', lambda e: context_menu.post(e.x_root, e.y_root)) # ===================================== # **** status bar **** # status bar is drawn before the text entry box 'text_entry' in order to keep it higher in priority of display. status_bar = tk.Label(root, bd=5, anchor=tk.W) status_bar.pack(side=BOTTOM, fill=tk.X) Sizegrip(status_bar).pack(side='right') # 'Sizegrip' displays a lower right corner, making it easier to resize the editor window. # ===================================== # **** Text area **** text_entry = ScrolledText(root, relief='flat', bd=2, wrap='word', undo=1, padx=3, pady=3, font=("Tahoma", "12")) text_entry.pack(expand=True, fill='both') text_entry.focus()
def create_content(self, **kw): self.minsize(190, 190) self.on = False # is the timer on? if not CONFIG.options("Tasks"): CONFIG.set("Tasks", _("Work"), CMAP[0]) self._stats = None # --- colors self.background = { _("Work"): CONFIG.get("Pomodoro", "work_bg"), _("Break"): CONFIG.get("Pomodoro", "break_bg"), _("Rest"): CONFIG.get("Pomodoro", "rest_bg") } self.foreground = { _("Work"): CONFIG.get("Pomodoro", "work_fg"), _("Break"): CONFIG.get("Pomodoro", "break_fg"), _("Rest"): CONFIG.get("Pomodoro", "rest_fg") } self.rowconfigure(1, weight=1) self.columnconfigure(0, weight=1) self.columnconfigure(1, weight=1) # nombre de séquence de travail effectuées d'affilée (pour # faire des pauses plus longues tous les 4 cycles) self.nb_cycles = 0 self.pomodori = IntVar(self, 0) # --- images self.im_go = PhotoImage(master=self, file=IM_START) self.im_stop = PhotoImage(master=self, file=IM_STOP) self.im_tomate = PhotoImage(master=self, file=IM_POMODORO) self.im_graph = PhotoImage(master=self, file=IM_GRAPH) # --- tasks list tasks_frame = Frame(self, style='pomodoro.TFrame') tasks_frame.grid(row=3, column=0, columnspan=3, sticky="wnse") tasks = [t.capitalize() for t in CONFIG.options("PomodoroTasks")] tasks.sort() self.task = StringVar(self, tasks[0]) self.menu_tasks = Menu(tasks_frame, relief='sunken', activeborderwidth=0) self.choose_task = Menubutton(tasks_frame, textvariable=self.task, menu=self.menu_tasks, style='pomodoro.TMenubutton') Label(tasks_frame, text=_("Task: "), style='pomodoro.TLabel', font="TkDefaultFont 12", width=6, anchor="e").pack(side="left", padx=4) self.choose_task.pack(side="right", fill="x", pady=4) # --- display self.tps = [CONFIG.getint("Pomodoro", "work_time"), 0] # time: min, sec self.activite = StringVar(self, _("Work")) self.titre = Label(self, textvariable=self.activite, font='TkDefaultFont 14', style='timer.pomodoro.TLabel', anchor="center") self.titre.grid(row=0, column=0, columnspan=2, sticky="we", pady=(4, 0), padx=4) self.temps = Label(self, text="{0:02}:{1:02}".format(self.tps[0], self.tps[1]), style='timer.pomodoro.TLabel', anchor="center") self.temps.grid(row=1, column=0, columnspan=2, sticky="nswe", padx=4) self.aff_pomodori = Label(self, textvariable=self.pomodori, anchor='e', padding=(20, 4, 20, 4), image=self.im_tomate, compound="left", style='timer.pomodoro.TLabel', font='TkDefaultFont 14') self.aff_pomodori.grid(row=2, columnspan=2, sticky="ew", padx=4) # --- buttons self.b_go = Button(self, image=self.im_go, command=self.go, style='pomodoro.TButton') self.b_go.grid(row=4, column=0, sticky="ew") self.b_stats = Button(self, image=self.im_graph, command=self.display_stats, style='pomodoro.TButton') self.b_stats.grid(row=4, column=1, sticky="ew") self._corner = Sizegrip(self, style="pomodoro.TSizegrip") self._corner.place(relx=1, rely=1, anchor='se') # --- bindings self.bind('<3>', lambda e: self.menu.tk_popup(e.x_root, e.y_root)) tasks_frame.bind('<ButtonPress-1>', self._start_move) tasks_frame.bind('<ButtonRelease-1>', self._stop_move) tasks_frame.bind('<B1-Motion>', self._move) self.titre.bind('<ButtonPress-1>', self._start_move) self.titre.bind('<ButtonRelease-1>', self._stop_move) self.titre.bind('<B1-Motion>', self._move) self.temps.bind('<ButtonPress-1>', self._start_move) self.temps.bind('<ButtonRelease-1>', self._stop_move) self.temps.bind('<B1-Motion>', self._move) self.b_stats.bind('<Enter>', self._on_enter) self.b_stats.bind('<Leave>', self._on_leave)
class Pomodoro(BaseWidget): """ Chronometre de temps de travail pour plus d'efficacité """ def __init__(self, master): BaseWidget.__init__(self, 'Pomodoro', master) def create_content(self, **kw): self.minsize(190, 190) self.on = False # is the timer on? if not CONFIG.options("Tasks"): CONFIG.set("Tasks", _("Work"), CMAP[0]) self._stats = None # --- colors self.background = { _("Work"): CONFIG.get("Pomodoro", "work_bg"), _("Break"): CONFIG.get("Pomodoro", "break_bg"), _("Rest"): CONFIG.get("Pomodoro", "rest_bg") } self.foreground = { _("Work"): CONFIG.get("Pomodoro", "work_fg"), _("Break"): CONFIG.get("Pomodoro", "break_fg"), _("Rest"): CONFIG.get("Pomodoro", "rest_fg") } self.rowconfigure(1, weight=1) self.columnconfigure(0, weight=1) self.columnconfigure(1, weight=1) # nombre de séquence de travail effectuées d'affilée (pour # faire des pauses plus longues tous les 4 cycles) self.nb_cycles = 0 self.pomodori = IntVar(self, 0) # --- images self.im_go = PhotoImage(master=self, file=IM_START) self.im_stop = PhotoImage(master=self, file=IM_STOP) self.im_tomate = PhotoImage(master=self, file=IM_POMODORO) self.im_graph = PhotoImage(master=self, file=IM_GRAPH) # --- tasks list tasks_frame = Frame(self, style='pomodoro.TFrame') tasks_frame.grid(row=3, column=0, columnspan=3, sticky="wnse") tasks = [t.capitalize() for t in CONFIG.options("PomodoroTasks")] tasks.sort() self.task = StringVar(self, tasks[0]) self.menu_tasks = Menu(tasks_frame, relief='sunken', activeborderwidth=0) self.choose_task = Menubutton(tasks_frame, textvariable=self.task, menu=self.menu_tasks, style='pomodoro.TMenubutton') Label(tasks_frame, text=_("Task: "), style='pomodoro.TLabel', font="TkDefaultFont 12", width=6, anchor="e").pack(side="left", padx=4) self.choose_task.pack(side="right", fill="x", pady=4) # --- display self.tps = [CONFIG.getint("Pomodoro", "work_time"), 0] # time: min, sec self.activite = StringVar(self, _("Work")) self.titre = Label(self, textvariable=self.activite, font='TkDefaultFont 14', style='timer.pomodoro.TLabel', anchor="center") self.titre.grid(row=0, column=0, columnspan=2, sticky="we", pady=(4, 0), padx=4) self.temps = Label(self, text="{0:02}:{1:02}".format(self.tps[0], self.tps[1]), style='timer.pomodoro.TLabel', anchor="center") self.temps.grid(row=1, column=0, columnspan=2, sticky="nswe", padx=4) self.aff_pomodori = Label(self, textvariable=self.pomodori, anchor='e', padding=(20, 4, 20, 4), image=self.im_tomate, compound="left", style='timer.pomodoro.TLabel', font='TkDefaultFont 14') self.aff_pomodori.grid(row=2, columnspan=2, sticky="ew", padx=4) # --- buttons self.b_go = Button(self, image=self.im_go, command=self.go, style='pomodoro.TButton') self.b_go.grid(row=4, column=0, sticky="ew") self.b_stats = Button(self, image=self.im_graph, command=self.display_stats, style='pomodoro.TButton') self.b_stats.grid(row=4, column=1, sticky="ew") self._corner = Sizegrip(self, style="pomodoro.TSizegrip") self._corner.place(relx=1, rely=1, anchor='se') # --- bindings self.bind('<3>', lambda e: self.menu.tk_popup(e.x_root, e.y_root)) tasks_frame.bind('<ButtonPress-1>', self._start_move) tasks_frame.bind('<ButtonRelease-1>', self._stop_move) tasks_frame.bind('<B1-Motion>', self._move) self.titre.bind('<ButtonPress-1>', self._start_move) self.titre.bind('<ButtonRelease-1>', self._stop_move) self.titre.bind('<B1-Motion>', self._move) self.temps.bind('<ButtonPress-1>', self._start_move) self.temps.bind('<ButtonRelease-1>', self._stop_move) self.temps.bind('<B1-Motion>', self._move) self.b_stats.bind('<Enter>', self._on_enter) self.b_stats.bind('<Leave>', self._on_leave) def update_style(self): self.menu_tasks.delete(0, 'end') tasks = [t.capitalize() for t in CONFIG.options('PomodoroTasks')] tasks.sort() for task in tasks: self.menu_tasks.add_radiobutton(label=task, value=task, variable=self.task) if self.task.get() not in tasks: self.stop(False) self.task.set(tasks[0]) self.attributes('-alpha', CONFIG.get(self.name, 'alpha', fallback=0.85)) bg = CONFIG.get('Pomodoro', 'background') fg = CONFIG.get('Pomodoro', 'foreground') active_bg = active_color(*self.winfo_rgb(bg)) self.style.configure('pomodoro.TMenubutton', background=bg, relief='flat', foreground=fg, borderwidth=0, arrowcolor=fg) self.style.configure('pomodoro.TButton', background=bg, relief='flat', foreground=fg, borderwidth=0) self.style.configure('pomodoro.TLabel', background=bg, foreground=fg) self.style.configure('pomodoro.TFrame', background=bg) self.style.configure('pomodoro.TSizegrip', background=bg) self.style.map('pomodoro.TSizegrip', background=[('active', active_bg)]) self.style.map('pomodoro.TButton', background=[('disabled', bg), ('!disabled', 'active', active_bg)]) self.style.map('pomodoro.TMenubutton', background=[('disabled', bg), ('!disabled', 'active', active_bg)]) self.configure(bg=bg) self.menu.configure(bg=bg, fg=fg, selectcolor=fg, activeforeground=fg, activebackground=active_bg) self.menu_pos.configure(bg=bg, fg=fg, selectcolor=fg, activeforeground=fg, activebackground=active_bg) self.menu_tasks.configure(bg=bg, activebackground=active_bg, fg=fg, selectcolor=fg, activeforeground=fg) self.background = { _("Work"): CONFIG.get("Pomodoro", "work_bg"), _("Break"): CONFIG.get("Pomodoro", "break_bg"), _("Rest"): CONFIG.get("Pomodoro", "rest_bg") } self.foreground = { _("Work"): CONFIG.get("Pomodoro", "work_fg"), _("Break"): CONFIG.get("Pomodoro", "break_fg"), _("Rest"): CONFIG.get("Pomodoro", "rest_fg") } act = self.activite.get() self.style.configure('timer.pomodoro.TLabel', font=CONFIG.get("Pomodoro", "font"), foreground=self.foreground[act], background=self.background[act]) def _on_enter(self, event=None): self._corner.state(('active', )) def _on_leave(self, event=None): self._corner.state(('!active', )) def _start_move(self, event): self.x = event.x self.y = event.y def _stop_move(self, event): self.x = None self.y = None self.configure(cursor='arrow') def _move(self, event): if self.x is not None and self.y is not None: self.configure(cursor='fleur') deltax = event.x - self.x deltay = event.y - self.y x = self.winfo_x() + deltax y = self.winfo_y() + deltay self.geometry("+%s+%s" % (x, y)) def hide(self): if self._stats is not None: self._stats.destroy() BaseWidget.hide(self) def stats(self, time=None): """Save stats.""" if time is None: time = CONFIG.getint("Pomodoro", "work_time") today = dt.date.today().toordinal() task = self.task.get().lower().replace(' ', '_') db = sqlite3.connect(PATH_STATS) cursor = db.cursor() try: cursor.execute('SELECT * FROM {} ORDER BY id DESC LIMIT 1'.format( scrub(task))) key, date, work = cursor.fetchone() except sqlite3.OperationalError: cursor.execute('''CREATE TABLE {} (id INTEGER PRIMARY KEY, date INTEGER, work INTEGER)'''.format( scrub(task))) cursor.execute( 'INSERT INTO {}(date, work) VALUES (?, ?)'.format(scrub(task)), (today, time)) else: if today != date: cursor.execute( 'INSERT INTO {}(date, work) VALUES (?, ?)'.format( scrub(task)), (today, time)) else: # update day's value cursor.execute( 'UPDATE {} SET work=? WHERE id=?'.format(scrub(task)), (work + time, key)) finally: db.commit() db.close() def display_stats(self): """ affiche les statistiques """ if self._stats is None: self._stats = Stats(self) self._stats.bind('<Destroy>', self._on_close_stats) else: self._stats.lift() def _on_close_stats(self, event): self._stats = None def go(self): if self.on: self.stop() else: self.on = True self.choose_task.state(["disabled"]) self.b_go.configure(image=self.im_stop) self.after(1000, self.affiche) logging.info('Start work cycle for task ' + self.task.get()) def stop(self, confirmation=True): """ Arrête le décompte du temps et le réinitialise, demande une confirmation avant de le faire si confirmation=True """ tps = int( CONFIG.getint("Pomodoro", "work_time") - self.tps[0] - self.tps[1] / 60) self.on = False rep = True if confirmation: rep = askyesno( title=_("Confirmation"), message=_( "Are you sure you want to give up the current session?")) if rep: self.choose_task.state(["!disabled"]) self.b_go.configure(image=self.im_go) if self.activite.get() == _("Work"): self.stats(tps) self.pomodori.set(0) self.nb_cycles = 0 self.tps = [CONFIG.getint("Pomodoro", "work_time"), 0] self.temps.configure( text="{0:02}:{1:02}".format(self.tps[0], self.tps[1])) act = _("Work") self.activite.set(act) self.style.configure('timer.pomodoro.TLabel', background=self.background[act], foreground=self.foreground[act]) logging.info('Pomodoro session interrupted.') else: self.on = True self.affiche() return rep @staticmethod def ting(): """ joue le son marquant le changement de période """ if not CONFIG.getboolean("Pomodoro", "mute", fallback=False): Popen([ CONFIG.get("General", "soundplayer"), CONFIG.get("Pomodoro", "beep") ]) def affiche(self): if self.on: self.tps[1] -= 1 if self.tps[1] == 0: if self.tps[0] == 0: self.ting() if self.activite.get() == _("Work"): self.pomodori.set(self.pomodori.get() + 1) self.nb_cycles += 1 self.choose_task.state(["!disabled"]) logging.info( 'Pomodoro: completed work session for task ' + self.task.get()) if self.nb_cycles % 4 == 0: # pause longue self.stats() self.activite.set(_("Rest")) self.tps = [ CONFIG.getint("Pomodoro", "rest_time"), 0 ] else: # pause courte self.stats() self.activite.set(_("Break")) self.tps = [ CONFIG.getint("Pomodoro", "break_time"), 0 ] else: self.choose_task.state(["disabled"]) self.activite.set(_("Work")) self.tps = [CONFIG.getint("Pomodoro", "work_time"), 0] act = self.activite.get() self.style.configure('timer.pomodoro.TLabel', background=self.background[act], foreground=self.foreground[act]) elif self.tps[1] == -1: self.tps[0] -= 1 self.tps[1] = 59 self.temps.configure(text="{0:02}:{1:02}".format(*self.tps)) self.after(1000, self.affiche)
def __init__(self): """ Constructor for the GUI. """ # Establish tkinter object self.root = Tk() self.root.title("ProPlanG") self.process_data = [] self.prev_visited = [] # data of the process that is currently being moved # x and y are the coordinates, item is the tag of the object group self.drag_x = 0 self.drag_y = 0 self.drag_item = None # Create Tkinter Menu (menubar on top, below window title) menubar = Menu(self.root) # Create pulldown menu (set tearoff=0 to disable detaching the menu) filemenu = Menu(menubar, tearoff=0) debugmenu = Menu(menubar, tearoff=0) # Menu content menubar.add_cascade(label="File", menu=filemenu) menubar.add_cascade(label="Settings", menu=debugmenu) # Filemenu content filemenu.add_command(label="Open...", command=self.read_config_file) # Debugmenu content self.detailed_arrow = BooleanVar(self.root, False) debugmenu.add_checkbutton(label="Show detailed arrows", variable=self.detailed_arrow, command=self.handle_arrow_drawing) self.arrow_split = BooleanVar(self.root, False) debugmenu.add_checkbutton(label="Directly connect arrows", variable=self.arrow_split, command=self.handle_arrow_drawing) # scrollbar x_scrollbar = Scrollbar(self.root, orient="horizontal") y_scrollbar = Scrollbar(self.root, orient="vertical") # sizegrip sizegrip = Sizegrip(self.root) # display the menu self.root.config(menu=menubar) # Prepare the main canvas self.main_canvas = Canvas(self.root, width=1000, height=500, borderwidth=1, scrollregion=(0, 0, 2000, 1000), xscrollcommand=x_scrollbar.set, yscrollcommand=y_scrollbar.set) # enable actual scrolling x_scrollbar.config(command=self.main_canvas.xview) y_scrollbar.config(command=self.main_canvas.yview) # bind mouse buttons to the drag functions self.main_canvas.bind("<ButtonPress-1>", self.drag_start) self.main_canvas.bind("<ButtonRelease-1>", self.drag_stop) self.main_canvas.bind("<B1-Motion>", self.drag) # place widgets in grid x_scrollbar.pack(fill="x", side="bottom", expand=False) y_scrollbar.pack(fill="y", side="right", expand=False) sizegrip.pack(in_=x_scrollbar, side="bottom", anchor="se") self.main_canvas.pack(fill="both", side="left", expand=True) # start the gui self.root.mainloop()
class MainWindow(object): def __init__(self, root, options): ''' ----------------------------------------------------- | main button toolbar | ----------------------------------------------------- | < ma | in content area > | | | | | File list | File name | | | | ----------------------------------------------------- | status bar area | ----------------------------------------------------- ''' # Obtain and expand the current working directory. if options.path: base_path = os.path.abspath(options.path) else: base_path = os.path.abspath(os.getcwd()) self.base_path = os.path.normcase(base_path) # Create a filename normalizer based on the CWD. self.filename_normalizer = filename_normalizer(self.base_path) # Set up dummy coverage data self.coverage_data = {'lines': {}, 'total_coverage': None} # Root window self.root = root self.root.title('Duvet') self.root.geometry('1024x768') # Prevent the menus from having the empty tearoff entry self.root.option_add('*tearOff', tk.FALSE) # Catch the close button self.root.protocol("WM_DELETE_WINDOW", self.cmd_quit) # Catch the "quit" event. self.root.createcommand('exit', self.cmd_quit) # Setup the menu self._setup_menubar() # Set up the main content for the window. self._setup_button_toolbar() self._setup_main_content() self._setup_status_bar() # Now configure the weights for the root frame self.root.columnconfigure(0, weight=1) self.root.rowconfigure(0, weight=0) self.root.rowconfigure(1, weight=1) self.root.rowconfigure(2, weight=0) ###################################################### # Internal GUI layout methods. ###################################################### def _setup_menubar(self): # Menubar self.menubar = tk.Menu(self.root) # self.menu_Apple = Menu(self.menubar, name='Apple') # self.menubar.add_cascade(menu=self.menu_Apple) self.menu_file = tk.Menu(self.menubar) self.menubar.add_cascade(menu=self.menu_file, label='File') self.menu_help = tk.Menu(self.menubar) self.menubar.add_cascade(menu=self.menu_help, label='Help') # self.menu_Apple.add_command(label='Test', command=self.cmd_dummy) # self.menu_file.add_command(label='New', command=self.cmd_dummy, accelerator="Command-N") # self.menu_file.add_command(label='Close', command=self.cmd_dummy) self.menu_help.add_command(label='Open Documentation', command=self.cmd_duvet_docs) self.menu_help.add_command(label='Open Duvet project page', command=self.cmd_duvet_page) self.menu_help.add_command(label='Open Duvet on GitHub', command=self.cmd_duvet_github) self.menu_help.add_command(label='Open BeeWare project page', command=self.cmd_beeware_page) # last step - configure the menubar self.root['menu'] = self.menubar def _setup_button_toolbar(self): ''' The button toolbar runs as a horizontal area at the top of the GUI. It is a persistent GUI component ''' # Main toolbar self.toolbar = tk.Frame(self.root) self.toolbar.grid(column=0, row=0, sticky=(tk.W, tk.E)) # Buttons on the toolbar self.refresh_button = tk.Button(self.toolbar, text='Refresh', command=self.cmd_refresh) self.refresh_button.grid(column=0, row=0) # Coverage summary for currently selected file. self.coverage_total_summary = tk.StringVar() self.coverage_total_summary_label = Label( self.toolbar, textvariable=self.coverage_total_summary, anchor=tk.E, padding=(5, 0, 5, 0), font=('Helvetica', '20')) self.coverage_total_summary_label.grid(column=1, row=0, sticky=(tk.W, tk.E)) self.toolbar.columnconfigure(0, weight=0) self.toolbar.columnconfigure(1, weight=1) self.toolbar.rowconfigure(0, weight=0) def _setup_main_content(self): ''' Sets up the main content area. It is a persistent GUI component ''' # Main content area self.content = PanedWindow(self.root, orient=tk.HORIZONTAL) self.content.grid(column=0, row=1, sticky=(tk.N, tk.S, tk.E, tk.W)) # Create the tree/control area on the left frame self._setup_left_frame() self._setup_project_file_tree() self._setup_global_file_tree() # Create the output/viewer area on the right frame self._setup_code_area() # Set up weights for the left frame's content self.content.columnconfigure(0, weight=1) self.content.rowconfigure(0, weight=1) self.content.pane(0, weight=1) self.content.pane(1, weight=4) def _setup_left_frame(self): ''' The left frame mostly consists of the tree widget ''' # The left-hand side frame on the main content area # The tabs for the two trees self.tree_notebook = Notebook(self.content, padding=(0, 5, 0, 5)) self.content.add(self.tree_notebook) def _setup_project_file_tree(self): self.project_file_tree_frame = tk.Frame(self.content) self.project_file_tree_frame.grid(column=0, row=0, sticky=(tk.N, tk.S, tk.E, tk.W)) self.tree_notebook.add(self.project_file_tree_frame, text='Project') self.project_file_tree = FileView(self.project_file_tree_frame, normalizer=self.filename_normalizer, root=self.base_path) self.project_file_tree.grid(column=0, row=0, sticky=(tk.N, tk.S, tk.E, tk.W)) # # The tree's vertical scrollbar self.project_file_tree_scrollbar = tk.Scrollbar( self.project_file_tree_frame, orient=tk.VERTICAL) self.project_file_tree_scrollbar.grid(column=1, row=0, sticky=(tk.N, tk.S)) # # Tie the scrollbar to the text views, and the text views # # to each other. self.project_file_tree.config( yscrollcommand=self.project_file_tree_scrollbar.set) self.project_file_tree_scrollbar.config( command=self.project_file_tree.yview) # Setup weights for the "project_file_tree" tree self.project_file_tree_frame.columnconfigure(0, weight=1) self.project_file_tree_frame.columnconfigure(1, weight=0) self.project_file_tree_frame.rowconfigure(0, weight=1) # Handlers for GUI events self.project_file_tree.bind('<<TreeviewSelect>>', self.on_file_selected) def _setup_global_file_tree(self): self.global_file_tree_frame = tk.Frame(self.content) self.global_file_tree_frame.grid(column=0, row=0, sticky=(tk.N, tk.S, tk.E, tk.W)) self.tree_notebook.add(self.global_file_tree_frame, text='Global') self.global_file_tree = FileView(self.global_file_tree_frame, normalizer=self.filename_normalizer) self.global_file_tree.grid(column=0, row=0, sticky=(tk.N, tk.S, tk.E, tk.W)) # # The tree's vertical scrollbar self.global_file_tree_scrollbar = tk.Scrollbar( self.global_file_tree_frame, orient=tk.VERTICAL) self.global_file_tree_scrollbar.grid(column=1, row=0, sticky=(tk.N, tk.S)) # # Tie the scrollbar to the text views, and the text views # # to each other. self.global_file_tree.config( yscrollcommand=self.global_file_tree_scrollbar.set) self.global_file_tree_scrollbar.config( command=self.global_file_tree.yview) # Setup weights for the "global_file_tree" tree self.global_file_tree_frame.columnconfigure(0, weight=1) self.global_file_tree_frame.columnconfigure(1, weight=0) self.global_file_tree_frame.rowconfigure(0, weight=1) # Handlers for GUI events self.global_file_tree.bind('<<TreeviewSelect>>', self.on_file_selected) def _setup_code_area(self): self.code_frame = tk.Frame(self.content) self.code_frame.grid(column=1, row=0, sticky=(tk.N, tk.S, tk.E, tk.W)) # Label for current file self.current_file = tk.StringVar() self.current_file_label = Label(self.code_frame, textvariable=self.current_file) self.current_file_label.grid(column=0, row=0, sticky=(tk.W, tk.E)) # Code display area self.code = CodeView(self.code_frame) self.code.grid(column=0, row=1, sticky=(tk.N, tk.S, tk.E, tk.W)) # Set up weights for the code frame's content self.code_frame.columnconfigure(0, weight=1) self.code_frame.rowconfigure(0, weight=0) self.code_frame.rowconfigure(1, weight=1) self.content.add(self.code_frame) def _setup_status_bar(self): # Status bar self.statusbar = tk.Frame(self.root) self.statusbar.grid(column=0, row=2, sticky=(tk.W, tk.E)) # Coverage summary for currently selected file. self.coverage_file_summary = tk.StringVar() self.coverage_file_summary_label = Label( self.statusbar, textvariable=self.coverage_file_summary) self.coverage_file_summary_label.grid(column=0, row=0, sticky=(tk.W, tk.E)) self.coverage_file_summary.set('No file selected') # Main window resize handle self.grip = Sizegrip(self.statusbar) self.grip.grid(column=1, row=0, sticky=(tk.S, tk.E)) # Set up weights for status bar frame self.statusbar.columnconfigure(0, weight=1) self.statusbar.columnconfigure(1, weight=0) self.statusbar.rowconfigure(0, weight=0) ###################################################### # Utility methods for controlling content ###################################################### def show_file(self, filename, line=None, breakpoints=None): """Show the content of the nominated file. If specified, line is the current line number to highlight. If the line isn't currently visible, the window will be scrolled until it is. breakpoints is a list of line numbers that have current breakpoints. If refresh is true, the file will be reloaded and redrawn. """ # Set the filename label for the current file self.current_file.set(self.filename_normalizer(filename)) # Update the code view; this means changing the displayed file # if necessary, and updating the current line. if filename != self.code.filename: self.code.filename = filename missing = self.coverage_data['missing'].get( os.path.normcase(filename), []) executed = self.coverage_data['lines'].get( os.path.normcase(filename), []) n_executed = len(executed) n_missing = len(missing) self.code.highlight_missing(missing) self.coverage_file_summary.set( '%s/%s lines executed' % (n_executed, n_executed + n_missing)) self.code.line = line def load_coverage(self): "Load and display coverage data" # Store the old list of files that have coverage data. # We do this so we can identify stale data on the tree. old_files = set(self.coverage_data['lines'].keys()) old_total_coverage = self.coverage_data['total_coverage'] loaded = False retry = True while not loaded and retry: try: # Load the new coverage data cov = coverage.coverage() cov.load() # Override precision for coverage reporting. coverage.results.Numbers.set_precision(1) if cov.data.measured_files(): self.coverage_data = { 'lines': {}, 'missing': {}, } totals = coverage.results.Numbers() # Update the coverage display of every file mentioned in the file. for filename in cov.data.measured_files(): filename = os.path.normcase(filename) node = nodify(filename) dirname, basename = os.path.split(filename) # If the normalized version of the filename is the same as the # filename, then the file *isn't* under the project root. if filename == self.filename_normalizer(filename): file_tree = self.global_file_tree else: file_tree = self.project_file_tree try: # # Make sure the file exists on the tree. file_tree.insert_filename(dirname, basename) # Compute the coverage percentage analysis = cov._analyze(filename) self.coverage_data['lines'][ filename] = analysis.statements self.coverage_data['missing'][ filename] = analysis.missing file_coverage = analysis.numbers.pc_covered totals = totals + analysis.numbers file_tree.set(node, 'coverage', analysis.numbers.pc_covered_str) # file_tree.set(node, 'branch_coverage', str(len(lines))) # Set the color of the tree node based on coverage if file_coverage < 70.0: file_tree.item(node, tags=['file', 'code', 'bad']) elif file_coverage < 80.0: file_tree.item(node, tags=['file', 'code', 'poor']) elif file_coverage < 90.0: file_tree.item(node, tags=['file', 'code', 'ok']) elif file_coverage < 99.9: file_tree.item(node, tags=['file', 'code', 'good']) else: file_tree.item( node, tags=['file', 'code', 'perfect']) except coverage.misc.NoSource: # could mean the file was deleted after running coverage file_tree.item(node, tags=['bad']) # We've updated the file, so we know it isn't stale. try: old_files.remove(filename) except KeyError: # File wasn't loaded before; ignore this. pass # Clear out any stale coverage data for filename in old_files: node = nodify(filename) if file_tree.exists(node): file_tree.set(node, 'coverage', '') file_tree.item(node, tags=['file', 'code']) # Compute the overall coverage total_coverage = totals.pc_covered self.coverage_data['total_coverage'] = total_coverage coverage_text = u'%.1f%%' % total_coverage # Update the text with up/down arrows to reflect change if old_total_coverage is not None: if total_coverage > old_total_coverage: coverage_text = coverage_text + u' ⬆' elif total_coverage < old_total_coverage: coverage_text = coverage_text + u' ⬇' self.coverage_total_summary.set(coverage_text) # Set the color based on coverage level. if total_coverage < 70.0: self.coverage_total_summary_label.configure( foreground='red') elif total_coverage < 80.0: self.coverage_total_summary_label.configure( foreground='orange') elif total_coverage < 90.0: self.coverage_total_summary_label.configure( foreground='blue') elif total_coverage < 99.9: self.coverage_total_summary_label.configure( foreground='cyan') else: self.coverage_total_summary_label.configure( foreground='green') # Refresh the file display current_file = self.code._filename if current_file: self.code._filename = None self.show_file(current_file) loaded = True else: retry = tkMessageBox.askretrycancel( message= "Couldn't find coverage data file. Have you generated coverage data? Is the .coverage in your current working directory", title='No coverage data found') except Exception as e: retry = tkMessageBox.askretrycancel( message= "Couldn't load coverage data -- data file may be corrupted (Error was: %s)" % e, title='Problem loading coverage data') return loaded ###################################################### # TK Main loop ###################################################### def mainloop(self): self.root.mainloop() ###################################################### # TK Command handlers ###################################################### def cmd_quit(self): "Quit the program" self.root.quit() def cmd_refresh(self, event=None): "Refresh the coverage data" self.load_coverage() def cmd_duvet_page(self): "Show the Duvet project page" webbrowser.open_new('http://pybee.org/duvet') def cmd_duvet_github(self): "Show the Duvet GitHub repo" webbrowser.open_new('http://github.com/pybee/duvet') def cmd_duvet_docs(self): "Show the Duvet documentation" # If this is a formal release, show the docs for that # version. otherwise, just show the head docs. if len(NUM_VERSION) == 3: webbrowser.open_new('https://duvet.readthedocs.io/en/v%s/' % VERSION) else: webbrowser.open_new('https://duvet.readthedocs.io/') def cmd_beeware_page(self): "Show the BeeWare project page" webbrowser.open_new('http://pybee.org/') ###################################################### # Handlers for GUI actions ###################################################### def on_file_selected(self, event): "When a file is selected, highlight the file and line" if event.widget.selection(): filename = event.widget.selection()[0] # Display the file in the code view if os.path.isfile(filename): self.show_file(filename=filename) else: self.code.filename = None
def __init__(self, master, key, **kwargs): """ Create a new sticky note. master: main app key: key identifying this note in master.note_data kwargs: dictionnary of the other arguments (title, txt, category, color, tags, geometry, locked, checkboxes, images, rolled) """ Toplevel.__init__(self, master) # --- window properties self.id = key self.is_locked = not (kwargs.get("locked", False)) self.images = [] self.links = {} self.latex = {} self.nb_links = 0 self.title('mynotes%s' % key) self.attributes("-type", "splash") self.attributes("-alpha", CONFIG.getint("General", "opacity") / 100) self.focus_force() # window geometry self.update_idletasks() self.geometry(kwargs.get("geometry", '220x235')) self.save_geometry = kwargs.get("geometry", '220x235') self.update() self.rowconfigure(1, weight=1) self.minsize(10, 10) self.protocol("WM_DELETE_WINDOW", self.hide) # --- style self.style = Style(self) self.style.configure(self.id + ".TCheckbutton", selectbackground="red") self.style.map('TEntry', selectbackground=[('!focus', '#c3c3c3')]) selectbg = self.style.lookup('TEntry', 'selectbackground', ('focus', )) self.style.configure("sel.TCheckbutton", background=selectbg) self.style.map("sel.TCheckbutton", background=[("active", selectbg)]) # --- note elements # title font_title = "%s %s" % (CONFIG.get("Font", "title_family").replace( " ", "\ "), CONFIG.get("Font", "title_size")) style = CONFIG.get("Font", "title_style").split(",") if style: font_title += " " font_title += " ".join(style) self.title_var = StringVar(master=self, value=kwargs.get("title", _("Title"))) self.title_label = Label(self, textvariable=self.title_var, anchor="center", style=self.id + ".TLabel", font=font_title) self.title_entry = Entry(self, textvariable=self.title_var, exportselection=False, justify="center", font=font_title) # buttons/icons self.roll = Label(self, image="img_roll", style=self.id + ".TLabel") self.close = Label(self, image="img_close", style=self.id + ".TLabel") self.im_lock = PhotoImage(master=self, file=IM_LOCK) self.cadenas = Label(self, style=self.id + ".TLabel") # corner grip self.corner = Sizegrip(self, style=self.id + ".TSizegrip") # texte font_text = "%s %s" % (CONFIG.get("Font", "text_family").replace( " ", "\ "), CONFIG.get("Font", "text_size")) self.txt = Text(self, wrap='word', undo=True, selectforeground='white', inactiveselectbackground=selectbg, selectbackground=selectbg, tabs=(10, 'right', 21, 'left'), relief="flat", borderwidth=0, highlightthickness=0, font=font_text) # tags self.txt.tag_configure("bold", font="%s bold" % font_text) self.txt.tag_configure("italic", font="%s italic" % font_text) self.txt.tag_configure("bold-italic", font="%s bold italic" % font_text) self.txt.tag_configure("underline", underline=True, selectforeground="white") self.txt.tag_configure("overstrike", overstrike=True, selectforeground="white") self.txt.tag_configure("center", justify="center") self.txt.tag_configure("left", justify="left") self.txt.tag_configure("right", justify="right") self.txt.tag_configure("link", foreground="blue", underline=True, selectforeground="white") self.txt.tag_configure("list", lmargin1=0, lmargin2=21, tabs=(10, 'right', 21, 'left')) self.txt.tag_configure("todolist", lmargin1=0, lmargin2=21, tabs=(10, 'right', 21, 'left')) margin = 2 * Font(self, font=font_text).measure("m") self.txt.tag_configure("enum", lmargin1=0, lmargin2=margin + 5, tabs=(margin, 'right', margin + 5, 'left')) for coul in TEXT_COLORS.values(): self.txt.tag_configure(coul, foreground=coul, selectforeground="white") self.txt.tag_configure(coul + "-underline", foreground=coul, selectforeground="white", underline=True) self.txt.tag_configure(coul + "-overstrike", foreground=coul, overstrike=True, selectforeground="white") # --- menus # --- * menu on title self.menu = Menu(self, tearoff=False) # note color menu_note_color = Menu(self.menu, tearoff=False) colors = list(COLORS.keys()) colors.sort() for coul in colors: menu_note_color.add_command( label=coul, command=lambda key=coul: self.change_color(key)) # category self.category = StringVar( self, kwargs.get("category", CONFIG.get("General", "default_category"))) self.menu_categories = Menu(self.menu, tearoff=False) categories = CONFIG.options("Categories") categories.sort() for cat in categories: self.menu_categories.add_radiobutton(label=cat.capitalize(), value=cat, variable=self.category, command=self.change_category) # position: normal, always above, always below self.position = StringVar( self, kwargs.get("position", CONFIG.get("General", "position"))) menu_position = Menu(self.menu, tearoff=False) menu_position.add_radiobutton(label=_("Always above"), value="above", variable=self.position, command=self.set_position_above) menu_position.add_radiobutton(label=_("Always below"), value="below", variable=self.position, command=self.set_position_below) menu_position.add_radiobutton(label=_("Normal"), value="normal", variable=self.position, command=self.set_position_normal) # mode: note, list, todo list menu_mode = Menu(self.menu, tearoff=False) self.mode = StringVar(self, kwargs.get("mode", "note")) menu_mode.add_radiobutton(label=_("Note"), value="note", variable=self.mode, command=self.set_mode_note) menu_mode.add_radiobutton(label=_("List"), value="list", variable=self.mode, command=self.set_mode_list) menu_mode.add_radiobutton(label=_("ToDo List"), value="todolist", variable=self.mode, command=self.set_mode_todolist) menu_mode.add_radiobutton(label=_("Enumeration"), value="enum", variable=self.mode, command=self.set_mode_enum) self.menu.add_command(label=_("Delete"), command=self.delete) self.menu.add_cascade(label=_("Category"), menu=self.menu_categories) self.menu.add_cascade(label=_("Color"), menu=menu_note_color) self.menu.add_command(label=_("Lock"), command=self.lock) self.menu.add_cascade(label=_("Position"), menu=menu_position) self.menu.add_cascade(label=_("Mode"), menu=menu_mode) # --- * menu on main text self.menu_txt = Menu(self.txt, tearoff=False) # style menu_style = Menu(self.menu_txt, tearoff=False) menu_style.add_command(label=_("Bold"), command=lambda: self.toggle_text_style("bold")) menu_style.add_command( label=_("Italic"), command=lambda: self.toggle_text_style("italic")) menu_style.add_command(label=_("Underline"), command=self.toggle_underline) menu_style.add_command(label=_("Overstrike"), command=self.toggle_overstrike) # text alignment menu_align = Menu(self.menu_txt, tearoff=False) menu_align.add_command(label=_("Left"), command=lambda: self.set_align("left")) menu_align.add_command(label=_("Right"), command=lambda: self.set_align("right")) menu_align.add_command(label=_("Center"), command=lambda: self.set_align("center")) # text color menu_colors = Menu(self.menu_txt, tearoff=False) colors = list(TEXT_COLORS.keys()) colors.sort() for coul in colors: menu_colors.add_command(label=coul, command=lambda key=coul: self. change_sel_color(TEXT_COLORS[key])) # insert menu_insert = Menu(self.menu_txt, tearoff=False) menu_insert.add_command(label=_("Symbols"), command=self.add_symbols) menu_insert.add_command(label=_("Checkbox"), command=self.add_checkbox) menu_insert.add_command(label=_("Image"), command=self.add_image) menu_insert.add_command(label=_("Date"), command=self.add_date) menu_insert.add_command(label=_("Link"), command=self.add_link) if LATEX: menu_insert.add_command(label="LaTex", command=self.add_latex) self.menu_txt.add_cascade(label=_("Style"), menu=menu_style) self.menu_txt.add_cascade(label=_("Alignment"), menu=menu_align) self.menu_txt.add_cascade(label=_("Color"), menu=menu_colors) self.menu_txt.add_cascade(label=_("Insert"), menu=menu_insert) # --- restore note content/appearence self.color = kwargs.get("color", CONFIG.get("Categories", self.category.get())) self.txt.insert('1.0', kwargs.get("txt", "")) self.txt.edit_reset() # clear undo stack # restore inserted objects (images and checkboxes) # we need to restore objects with increasing index to avoid placment errors indexes = list(kwargs.get("inserted_objects", {}).keys()) indexes.sort(key=sorting) for index in indexes: kind, val = kwargs["inserted_objects"][index] if kind == "checkbox": ch = Checkbutton(self.txt, takefocus=False, style=self.id + ".TCheckbutton") if val: ch.state(("selected", )) self.txt.window_create(index, window=ch) elif kind == "image": if os.path.exists(val): self.images.append(PhotoImage(master=self.txt, file=val)) self.txt.image_create(index, image=self.images[-1], name=val) # restore tags for tag, indices in kwargs.get("tags", {}).items(): if indices: self.txt.tag_add(tag, *indices) for link in kwargs.get("links", {}).values(): self.nb_links += 1 self.links[self.nb_links] = link self.txt.tag_bind("link#%i" % self.nb_links, "<Button-1>", lambda e, l=link: open_url(l)) for img, latex in kwargs.get("latex", {}).items(): self.latex[img] = latex if LATEX: self.txt.tag_bind(img, '<Double-Button-1>', lambda e, im=img: self.add_latex(im)) mode = self.mode.get() if mode != "note": self.txt.tag_add(mode, "1.0", "end") self.txt.focus_set() self.lock() if kwargs.get("rolled", False): self.rollnote() if self.position.get() == "above": self.set_position_above() elif self.position.get() == "below": self.set_position_below() # --- placement # titlebar if CONFIG.get("General", "buttons_position") == "right": # right = lock icon - title - roll - close self.columnconfigure(1, weight=1) self.roll.grid(row=0, column=2, sticky="e") self.close.grid(row=0, column=3, sticky="e", padx=(0, 2)) self.cadenas.grid(row=0, column=0, sticky="w") self.title_label.grid(row=0, column=1, sticky="ew", pady=(1, 0)) else: # left = close - roll - title - lock icon self.columnconfigure(2, weight=1) self.roll.grid(row=0, column=1, sticky="w") self.close.grid(row=0, column=0, sticky="w", padx=(2, 0)) self.cadenas.grid(row=0, column=3, sticky="e") self.title_label.grid(row=0, column=2, sticky="ew", pady=(1, 0)) # body self.txt.grid(row=1, columnspan=4, column=0, sticky="ewsn", pady=(1, 4), padx=4) self.corner.lift(self.txt) self.corner.place(relx=1.0, rely=1.0, anchor="se") # --- bindings self.bind("<FocusOut>", self.save_note) self.bind('<Configure>', self.bouge) self.bind('<Button-1>', self.change_focus, True) self.close.bind("<Button-1>", self.hide) self.close.bind("<Enter>", self.enter_close) self.close.bind("<Leave>", self.leave_close) self.roll.bind("<Button-1>", self.rollnote) self.roll.bind("<Enter>", self.enter_roll) self.roll.bind("<Leave >", self.leave_roll) self.title_label.bind("<Double-Button-1>", self.edit_title) self.title_label.bind("<ButtonPress-1>", self.start_move) self.title_label.bind("<ButtonRelease-1>", self.stop_move) self.title_label.bind("<B1-Motion>", self.move) self.title_label.bind('<Button-3>', self.show_menu) self.title_entry.bind("<Return>", lambda e: self.title_entry.place_forget()) self.title_entry.bind("<FocusOut>", lambda e: self.title_entry.place_forget()) self.title_entry.bind("<Escape>", lambda e: self.title_entry.place_forget()) self.txt.tag_bind("link", "<Enter>", lambda event: self.txt.configure(cursor="hand1")) self.txt.tag_bind("link", "<Leave>", lambda event: self.txt.configure(cursor="")) self.txt.bind("<FocusOut>", self.save_note) self.txt.bind('<Button-3>', self.show_menu_txt) # add binding to the existing class binding so that the selected text # is erased on pasting self.txt.bind("<Control-v>", self.paste) self.corner.bind('<ButtonRelease-1>', self.resize) # --- keyboard shortcuts self.txt.bind('<Control-b>', lambda e: self.toggle_text_style('bold')) self.txt.bind('<Control-i>', lambda e: self.toggle_text_style('italic')) self.txt.bind('<Control-u>', lambda e: self.toggle_underline()) self.txt.bind('<Control-r>', lambda e: self.set_align('right')) self.txt.bind('<Control-l>', lambda e: self.set_align('left'))
def _create_text_tab(self, nb): self.dir0 = 1 self.dir1 = 1 # populate the third frame with other widgets fr = Frame(nb, name='fr') lF = LabelFrame(fr, text="Slider") fr1 = Frame(lF) fr1.grid(row=0, column=0, sticky='nsew') from_ = 100 to = 0 value = 0 step = 10 fontSize = 9 self.scvar = IntVar() scRange = self.any_number_range(from_, to, step) scLen = len(scRange[1]) * (fontSize + 10) self.sc = Scale(fr1, from_=from_, to=to, variable=self.scvar, orient='vertical', length=scLen, command=self.v_scale) self.sc.set(value) l1 = Label(fr1, textvariable=self.scvar, width=5) l1.grid(row=0, column=0, padx=5, pady=5) self.sc.grid(row=0, column=1, padx=5, pady=5) fr4 = Frame(fr1) fr4.grid(row=0, column=2) sc_split = '\n'.join(scRange[0].split()) lb = Label(fr1, text=sc_split, font=('Courier New', str(fontSize))) lb.grid(row=0, column=2, padx=5, pady=5) fr2 = Frame(lF, name='fr2') fr2.grid(row=0, column=1, sticky='nsew') self.schvar = IntVar() a = 0 b = 100 schRange = self.any_number_range(a, b, s=10) schLen = Font().measure(schRange[0]) self.sch = Scale(fr2, from_=a, to=b, length=schLen, variable=self.schvar, orient='horizontal', command=self.h_scale) self.sch.set(0) l2 = Label(fr2, textvariable=self.schvar) l2.grid(row=1, column=1, pady=2) self.sch.grid(row=2, column=1, padx=5, pady=5, sticky='nsew') l3 = Label(fr2, text=schRange[0], font=('Courier New', str(fontSize))) l3.grid(row=3, column=1, padx=5, pady=5) lF.grid(row=0, column=0, sticky='nesw', pady=5, padx=5) lF1 = LabelFrame(fr, text="Progress", name='lf') pb1var = IntVar() pb2var = IntVar() self.pbar = Progressbar(lF1, variable=pb1var, length=150, mode="indeterminate", name='pb1', orient='horizontal') self.pb2 = Progressbar(lF1, variable=pb2var, length=150, mode='indeterminate', name='pb2', orient='vertical') self.pbar["value"] = 25 self.h_progress() self.v_progress() self.pbar.grid(row=1, column=0, padx=5, pady=5, sticky='nw') self.pb2.grid(row=1, column=1, padx=5, pady=5, sticky='nw') l3 = Label(lF1, textvariable=pb1var) l3.grid(row=0, column=0, pady=2, sticky='nw') l4 = Label(lF1, textvariable=pb2var) l4.grid(row=0, column=1, pady=2, sticky='nw') sg1 = Sizegrip(fr) sg1.grid(row=2, column=2, sticky='e') lF1.grid(row=1, column=0, sticky='nesw', pady=5, padx=5) # add to notebook (underline = index for short-cut character) nb.add(fr, text='Sliders & Others', underline=0)
class MainWindow(object): def __init__(self, root, options): ''' ----------------------------------------------------- | main button toolbar | ----------------------------------------------------- | < ma | in content area > | | | | | File list | File name | | | | ----------------------------------------------------- | status bar area | ----------------------------------------------------- ''' # Obtain and expand the current working directory. if options.path: base_path = os.path.abspath(options.path) else: base_path = os.path.abspath(os.getcwd()) self.base_path = os.path.normcase(base_path) # Create a filename normalizer based on the CWD. self.filename_normalizer = filename_normalizer(self.base_path) # Set up dummy coverage data self.coverage_data = {'lines': {}, 'total_coverage': None} # Root window self.root = root self.root.title('Duvet') self.root.geometry('1024x768') # Prevent the menus from having the empty tearoff entry self.root.option_add('*tearOff', tk.FALSE) # Catch the close button self.root.protocol("WM_DELETE_WINDOW", self.cmd_quit) # Catch the "quit" event. self.root.createcommand('exit', self.cmd_quit) # Setup the menu self._setup_menubar() # Set up the main content for the window. self._setup_button_toolbar() self._setup_main_content() self._setup_status_bar() # Now configure the weights for the root frame self.root.columnconfigure(0, weight=1) self.root.rowconfigure(0, weight=0) self.root.rowconfigure(1, weight=1) self.root.rowconfigure(2, weight=0) ###################################################### # Internal GUI layout methods. ###################################################### def _setup_menubar(self): # Menubar self.menubar = tk.Menu(self.root) # self.menu_Apple = Menu(self.menubar, name='Apple') # self.menubar.add_cascade(menu=self.menu_Apple) self.menu_file = tk.Menu(self.menubar) self.menubar.add_cascade(menu=self.menu_file, label='File') self.menu_help = tk.Menu(self.menubar) self.menubar.add_cascade(menu=self.menu_help, label='Help') # self.menu_Apple.add_command(label='Test', command=self.cmd_dummy) # self.menu_file.add_command(label='New', command=self.cmd_dummy, accelerator="Command-N") # self.menu_file.add_command(label='Close', command=self.cmd_dummy) self.menu_help.add_command(label='Open Documentation', command=self.cmd_duvet_docs) self.menu_help.add_command(label='Open Duvet project page', command=self.cmd_duvet_page) self.menu_help.add_command(label='Open Duvet on GitHub', command=self.cmd_duvet_github) self.menu_help.add_command(label='Open BeeWare project page', command=self.cmd_beeware_page) # last step - configure the menubar self.root['menu'] = self.menubar def _setup_button_toolbar(self): ''' The button toolbar runs as a horizontal area at the top of the GUI. It is a persistent GUI component ''' # Main toolbar self.toolbar = tk.Frame(self.root) self.toolbar.grid(column=0, row=0, sticky=(tk.W, tk.E)) # Buttons on the toolbar self.refresh_button = tk.Button(self.toolbar, text='Refresh', command=self.cmd_refresh) self.refresh_button.grid(column=0, row=0) # Coverage summary for currently selected file. self.coverage_total_summary = tk.StringVar() self.coverage_total_summary_label = Label( self.toolbar, textvariable=self.coverage_total_summary, anchor=tk.E, padding=(5, 0, 5, 0), font=('Helvetica','20') ) self.coverage_total_summary_label.grid(column=1, row=0, sticky=(tk.W, tk.E)) self.toolbar.columnconfigure(0, weight=0) self.toolbar.columnconfigure(1, weight=1) self.toolbar.rowconfigure(0, weight=0) def _setup_main_content(self): ''' Sets up the main content area. It is a persistent GUI component ''' # Main content area self.content = PanedWindow(self.root, orient=tk.HORIZONTAL) self.content.grid(column=0, row=1, sticky=(tk.N, tk.S, tk.E, tk.W)) # Create the tree/control area on the left frame self._setup_left_frame() self._setup_project_file_tree() self._setup_global_file_tree() # Create the output/viewer area on the right frame self._setup_code_area() # Set up weights for the left frame's content self.content.columnconfigure(0, weight=1) self.content.rowconfigure(0, weight=1) self.content.pane(0, weight=1) self.content.pane(1, weight=4) def _setup_left_frame(self): ''' The left frame mostly consists of the tree widget ''' # The left-hand side frame on the main content area # The tabs for the two trees self.tree_notebook = Notebook( self.content, padding=(0, 5, 0, 5) ) self.content.add(self.tree_notebook) def _setup_project_file_tree(self): self.project_file_tree_frame = tk.Frame(self.content) self.project_file_tree_frame.grid(column=0, row=0, sticky=(tk.N, tk.S, tk.E, tk.W)) self.tree_notebook.add(self.project_file_tree_frame, text='Project') self.project_file_tree = FileView(self.project_file_tree_frame, normalizer=self.filename_normalizer, root=self.base_path) self.project_file_tree.grid(column=0, row=0, sticky=(tk.N, tk.S, tk.E, tk.W)) # # The tree's vertical scrollbar self.project_file_tree_scrollbar = tk.Scrollbar(self.project_file_tree_frame, orient=tk.VERTICAL) self.project_file_tree_scrollbar.grid(column=1, row=0, sticky=(tk.N, tk.S)) # # Tie the scrollbar to the text views, and the text views # # to each other. self.project_file_tree.config(yscrollcommand=self.project_file_tree_scrollbar.set) self.project_file_tree_scrollbar.config(command=self.project_file_tree.yview) # Setup weights for the "project_file_tree" tree self.project_file_tree_frame.columnconfigure(0, weight=1) self.project_file_tree_frame.columnconfigure(1, weight=0) self.project_file_tree_frame.rowconfigure(0, weight=1) # Handlers for GUI events self.project_file_tree.bind('<<TreeviewSelect>>', self.on_file_selected) def _setup_global_file_tree(self): self.global_file_tree_frame = tk.Frame(self.content) self.global_file_tree_frame.grid(column=0, row=0, sticky=(tk.N, tk.S, tk.E, tk.W)) self.tree_notebook.add(self.global_file_tree_frame, text='Global') self.global_file_tree = FileView(self.global_file_tree_frame, normalizer=self.filename_normalizer) self.global_file_tree.grid(column=0, row=0, sticky=(tk.N, tk.S, tk.E, tk.W)) # # The tree's vertical scrollbar self.global_file_tree_scrollbar = tk.Scrollbar(self.global_file_tree_frame, orient=tk.VERTICAL) self.global_file_tree_scrollbar.grid(column=1, row=0, sticky=(tk.N, tk.S)) # # Tie the scrollbar to the text views, and the text views # # to each other. self.global_file_tree.config(yscrollcommand=self.global_file_tree_scrollbar.set) self.global_file_tree_scrollbar.config(command=self.global_file_tree.yview) # Setup weights for the "global_file_tree" tree self.global_file_tree_frame.columnconfigure(0, weight=1) self.global_file_tree_frame.columnconfigure(1, weight=0) self.global_file_tree_frame.rowconfigure(0, weight=1) # Handlers for GUI events self.global_file_tree.bind('<<TreeviewSelect>>', self.on_file_selected) def _setup_code_area(self): self.code_frame = tk.Frame(self.content) self.code_frame.grid(column=1, row=0, sticky=(tk.N, tk.S, tk.E, tk.W)) # Label for current file self.current_file = tk.StringVar() self.current_file_label = Label(self.code_frame, textvariable=self.current_file) self.current_file_label.grid(column=0, row=0, sticky=(tk.W, tk.E)) # Code display area self.code = CodeView(self.code_frame) self.code.grid(column=0, row=1, sticky=(tk.N, tk.S, tk.E, tk.W)) # Set up weights for the code frame's content self.code_frame.columnconfigure(0, weight=1) self.code_frame.rowconfigure(0, weight=0) self.code_frame.rowconfigure(1, weight=1) self.content.add(self.code_frame) def _setup_status_bar(self): # Status bar self.statusbar = tk.Frame(self.root) self.statusbar.grid(column=0, row=2, sticky=(tk.W, tk.E)) # Coverage summary for currently selected file. self.coverage_file_summary = tk.StringVar() self.coverage_file_summary_label = Label(self.statusbar, textvariable=self.coverage_file_summary) self.coverage_file_summary_label.grid(column=0, row=0, sticky=(tk.W, tk.E)) self.coverage_file_summary.set('No file selected') # Main window resize handle self.grip = Sizegrip(self.statusbar) self.grip.grid(column=1, row=0, sticky=(tk.S, tk.E)) # Set up weights for status bar frame self.statusbar.columnconfigure(0, weight=1) self.statusbar.columnconfigure(1, weight=0) self.statusbar.rowconfigure(0, weight=0) ###################################################### # Utility methods for controlling content ###################################################### def show_file(self, filename, line=None, breakpoints=None): """Show the content of the nominated file. If specified, line is the current line number to highlight. If the line isn't currently visible, the window will be scrolled until it is. breakpoints is a list of line numbers that have current breakpoints. If refresh is true, the file will be reloaded and redrawn. """ # Set the filename label for the current file self.current_file.set(self.filename_normalizer(filename)) # Update the code view; this means changing the displayed file # if necessary, and updating the current line. if filename != self.code.filename: self.code.filename = filename missing = self.coverage_data['missing'].get(os.path.normcase(filename), []) executed = self.coverage_data['lines'].get(os.path.normcase(filename), []) n_executed = len(executed) n_missing = len(missing) self.code.highlight_missing(missing) self.coverage_file_summary.set('%s/%s lines executed' % (n_executed, n_executed + n_missing)) self.code.line = line def load_coverage(self): "Load and display coverage data" # Store the old list of files that have coverage data. # We do this so we can identify stale data on the tree. old_files = set(self.coverage_data['lines'].keys()) old_total_coverage = self.coverage_data['total_coverage'] loaded = False retry = True while not loaded and retry: try: # Load the new coverage data cov = coverage.coverage() cov.load() # Override precision for coverage reporting. coverage.results.Numbers.set_precision(1) if cov.data.measured_files(): self.coverage_data = { 'lines': {}, 'missing': {}, } totals = coverage.results.Numbers() # Update the coverage display of every file mentioned in the file. for filename in cov.data.measured_files(): filename = os.path.normcase(filename) node = nodify(filename) dirname, basename = os.path.split(filename) # If the normalized version of the filename is the same as the # filename, then the file *isn't* under the project root. if filename == self.filename_normalizer(filename): file_tree = self.global_file_tree else: file_tree = self.project_file_tree try: # # Make sure the file exists on the tree. file_tree.insert_filename(dirname, basename) # Compute the coverage percentage analysis = cov._analyze(filename) self.coverage_data['lines'][filename] = analysis.statements self.coverage_data['missing'][filename] = analysis.missing file_coverage = analysis.numbers.pc_covered totals = totals + analysis.numbers file_tree.set(node, 'coverage', analysis.numbers.pc_covered_str) # file_tree.set(node, 'branch_coverage', str(len(lines))) # Set the color of the tree node based on coverage if file_coverage < 70.0: file_tree.item(node, tags=['file', 'code', 'bad']) elif file_coverage < 80.0: file_tree.item(node, tags=['file', 'code', 'poor']) elif file_coverage < 90.0: file_tree.item(node, tags=['file', 'code', 'ok']) elif file_coverage < 99.9: file_tree.item(node, tags=['file', 'code', 'good']) else: file_tree.item(node, tags=['file', 'code', 'perfect']) except coverage.misc.NoSource: # could mean the file was deleted after running coverage file_tree.item(node, tags=['bad']) # We've updated the file, so we know it isn't stale. try: old_files.remove(filename) except KeyError: # File wasn't loaded before; ignore this. pass # Clear out any stale coverage data for filename in old_files: node = nodify(filename) if file_tree.exists(node): file_tree.set(node, 'coverage', '') file_tree.item(node, tags=['file', 'code']) # Compute the overall coverage total_coverage = totals.pc_covered self.coverage_data['total_coverage'] = total_coverage coverage_text = u'%.1f%%' % total_coverage # Update the text with up/down arrows to reflect change if old_total_coverage is not None: if total_coverage > old_total_coverage: coverage_text = coverage_text + u' ⬆' elif total_coverage < old_total_coverage: coverage_text = coverage_text + u' ⬇' self.coverage_total_summary.set(coverage_text) # Set the color based on coverage level. if total_coverage < 70.0: self.coverage_total_summary_label.configure(foreground='red') elif total_coverage < 80.0: self.coverage_total_summary_label.configure(foreground='orange') elif total_coverage < 90.0: self.coverage_total_summary_label.configure(foreground='blue') elif total_coverage < 99.9: self.coverage_total_summary_label.configure(foreground='cyan') else: self.coverage_total_summary_label.configure(foreground='green') # Refresh the file display current_file = self.code._filename if current_file: self.code._filename = None self.show_file(current_file) loaded = True else: retry = tkMessageBox.askretrycancel( message="Couldn't find coverage data file. Have you generated coverage data? Is the .coverage in your current working directory", title='No coverage data found' ) except Exception as e: retry = tkMessageBox.askretrycancel( message="Couldn't load coverage data -- data file may be corrupted (Error was: %s)" % e, title='Problem loading coverage data' ) return loaded ###################################################### # TK Main loop ###################################################### def mainloop(self): self.root.mainloop() ###################################################### # TK Command handlers ###################################################### def cmd_quit(self): "Quit the program" self.root.quit() def cmd_refresh(self, event=None): "Refresh the coverage data" self.load_coverage() def cmd_duvet_page(self): "Show the Duvet project page" webbrowser.open_new('http://pybee.org/duvet') def cmd_duvet_github(self): "Show the Duvet GitHub repo" webbrowser.open_new('http://github.com/pybee/duvet') def cmd_duvet_docs(self): "Show the Duvet documentation" # If this is a formal release, show the docs for that # version. otherwise, just show the head docs. if len(NUM_VERSION) == 3: webbrowser.open_new('https://duvet.readthedocs.io/en/v%s/' % VERSION) else: webbrowser.open_new('https://duvet.readthedocs.io/') def cmd_beeware_page(self): "Show the BeeWare project page" webbrowser.open_new('http://pybee.org/') ###################################################### # Handlers for GUI actions ###################################################### def on_file_selected(self, event): "When a file is selected, highlight the file and line" if event.widget.selection(): filename = event.widget.selection()[0] # Display the file in the code view if os.path.isfile(filename): self.show_file(filename=filename) else: self.code.filename = None
def _create_text_tab(self, nb): self.dir0 = 1 self.dir1 = 1 # populate the third frame with other widgets fr = Frame(nb, name='fr1') lF = LabelFrame(fr, text="Scale") fr3 = Frame(lF) fr3.grid(row=0, column=0, sticky='nsew') scl = Label( fr3, text= 'Position mouse to cursor ,left mouse click and move\nor use spinbox arrows' ) scl.grid(row=0, column=0, sticky='nw') fr1 = Frame(lF) fr1.grid(row=1, column=0, sticky='nsew') from_ = 0 to = 100 value = 0 step = 10 fontSize = 9 self.scvar = IntVar() scRange = self.any_number_range(from_, to, step) #scLen = len(scRange[1]) * (fontSize + 10) #print('scLen',scLen) self.sc = TtkScale(fr1, from_=from_, to=to, variable=self.scvar, orient='vertical', length=200, showvalue=False, tickinterval=5, resolution=5, sliderlength=16) #self.sc = Scale(fr1, from_=from_, to=to, variable=self.scvar, #orient='vertical', length=scLen) self.sc.set(value) #l1 = Label(fr1,textvariable=self.scvar,width=5) l1 = Spinbox(fr1, from_=from_, to=to, textvariable=self.scvar, width=4) l1.grid(row=1, column=0, padx=5, pady=5) self.sc.grid(row=1, column=1, padx=40, pady=5) fr4 = Frame(fr1) fr4.grid(row=1, column=2) sc_split = '\n'.join(scRange[0].split()) #lb = Label(fr1, text=sc_split, font=('Courier New', str(fontSize)), #width=4*len(scRange[1])) #lb.grid(row=1, column=2,padx=5,pady=5) #sep = Separator(lF, orient="vertical") #sep.grid(row=1,column=2,sticky='ns') fr2 = Frame(lF, name='fr2') fr2.grid(row=1, column=3, sticky='nsew') self.schvar = IntVar() a = 0 b = 100 #schRange = self.any_number_range(a,b,s=10) #schLen = Font().measure(schRange[0]) #print('schLen',schLen) #self.sch = Scale(fr2, from_=a, to=b, length=schLen, variable=self.schvar, #orient='horizontal') self.sch = TtkScale(lF, from_=a, to=b, length=200, variable=self.schvar, orient='horizontal', showvalue=False, tickinterval=5, resolution=5, sliderlength=16) self.sch.set(0) #l2 = Label(fr2,textvariable=self.schvar,width=5) l2 = Spinbox(lF, from_=a, to=b, textvariable=self.schvar, width=4) l2.grid(row=1, column=1, pady=2, sticky='s') self.sch.grid(row=2, column=1, padx=5, pady=40, sticky='nsew') #l3 = Label(fr2,text=schRange[0], font=('Courier New', str(fontSize)), #width=4*len(schRange[1])) #l3.grid(row=3,column=1,padx=5,pady=5) lF.grid(row=0, column=0, sticky='nesw', pady=5, padx=5) lF1 = LabelFrame(fr, text="Progress", name='lf') pb1var = IntVar() pb2var = IntVar() self.pbar = Progressbar(lF1, variable=pb1var, length=150, mode="indeterminate", name='pb1', orient='horizontal') self.pb2 = Progressbar(lF1, variable=pb2var, length=150, mode='indeterminate', name='pb2', orient='vertical') self.pbar["value"] = 25 self.pbar.grid(row=1, column=0, padx=5, pady=5, sticky='nw') sep = Separator(lF1, orient="vertical") self.pb2.grid(row=1, column=2, padx=5, pady=5, sticky='nw') l3 = Label(lF1, textvariable=pb1var) l3.grid(row=0, column=0, pady=2, sticky='nw') sep.grid(row=0, column=1, rowspan=2, sticky='ns') l4 = Label(lF1, textvariable=pb2var) l4.grid(row=0, column=2, pady=2, sticky='nw') sg1 = Sizegrip(fr) sg1.grid(row=2, column=2, sticky='e') lF1.grid(row=1, column=0, sticky='nesw', pady=5, padx=5) start = Button(lF1, text='Start Progress', command=lambda: self._do_bars('start')) stop = Button(lF1, text='Stop Progress', command=lambda: self._do_bars('stop')) start.grid(row=2, column=0, padx=5, pady=5, sticky='nw') stop.grid(row=3, column=0, padx=5, pady=5, sticky='nw') # add to notebook (underline = index for short-cut character) nb.add(fr, text='Scale & Others', underline=0)