Example #1
0
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))
Example #2
0
    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)
Example #3
0
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))
Example #4
0
    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)
Example #5
0
    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)
Example #6
0
 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")
Example #7
0
    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'))
Example #8
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)
Example #9
0
File: view.py Project: pybee/duvet
    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)
Example #12
0
    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),
    )
Example #14
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()
Example #15
0
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))
Example #16
0
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")
Example #17
0
    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)
Example #18
0
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()
Example #19
0
    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)
Example #20
0
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)
Example #21
0
    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()
Example #22
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
Example #23
0
    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'))
Example #24
0
    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)
Example #25
0
File: view.py Project: pybee/duvet
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)