Beispiel #1
0
    def __init__(self, master, app, **kwargs):
        Frame.__init__(self, master, padding=4, **kwargs)
        self.rowconfigure(2, weight=1)
        self.columnconfigure(0, weight=1)
        self.columnconfigure(1, weight=1)

        self.tree = Treeview(self,
                             columns=('replace', 'by'),
                             show='',
                             selectmode='browse')
        scroll_x = AutoScrollbar(self,
                                 orient='horizontal',
                                 command=self.tree.xview)
        scroll_y = AutoScrollbar(self,
                                 orient='vertical',
                                 command=self.tree.yview)
        self.tree.configure(xscrollcommand=scroll_x.set,
                            yscrollcommand=scroll_y.set)

        self.reset()

        self.replace = StringVar(self)
        self.by = StringVar(self)

        add_trace(self.replace, 'write', self._trace_replace)
        add_trace(self.by, 'write', self._trace_by)

        b_frame = Frame(self)
        self.b_add = Button(b_frame, text=_('New'), command=self.add)
        self.b_rem = Button(b_frame, text=_('Delete'), command=self.remove)
        self.b_add.state(('disabled', ))
        self.b_rem.state(('disabled', ))
        self.b_add.pack(pady=4, fill='x')
        self.b_rem.pack(pady=4, fill='x')
        Button(b_frame, text=_('Reset'), command=self.reset).pack(pady=8,
                                                                  fill='x')

        Label(self, text=_('Replace')).grid(row=0,
                                            column=0,
                                            sticky='w',
                                            pady=4)
        Label(self, text=_('By')).grid(row=0, column=1, sticky='w', pady=4)
        Entry(self, textvariable=self.replace).grid(row=1,
                                                    column=0,
                                                    sticky='ew',
                                                    pady=4,
                                                    padx=(0, 4))
        Entry(self, textvariable=self.by).grid(row=1,
                                               column=1,
                                               sticky='ew',
                                               pady=4)
        self.tree.grid(row=2, columnspan=2, sticky='ewsn', pady=(4, 0))
        scroll_x.grid(row=3, columnspan=2, sticky='ew', pady=(0, 4))
        scroll_y.grid(row=2, column=2, sticky='ns', pady=(4, 0))
        b_frame.grid(row=1, rowspan=2, padx=(4, 0), sticky='nw', column=3)

        self.tree.bind('<<TreeviewSelect>>', self._on_treeview_select)
Beispiel #2
0
    def __init__(self, master):
        """Create note manager to easily delete multiple notes."""
        Toplevel.__init__(self, master, class_='MyNotes')
        self.title(_("Note Manager"))
        self.minsize(width=546, height=200)
        self.grab_set()
        categories = CONFIG.options("Categories")
        categories.sort()

        self.im_del = PhotoImage(file=IM_DELETE, master=self)
        self.im_change = PhotoImage(file=IM_CHANGE, master=self)
        self.im_visible = PhotoImage(file=IM_VISIBLE_24, master=self)
        self.im_hidden = PhotoImage(file=IM_HIDDEN_24, master=self)

        tooltipwrapper = TooltipWrapper(self)

        self.notebook = Notebook(self)
        self.notebook.pack(fill='both', expand=True)

        self.texts = {}
        self.frames = {}
        self.notes = {}

        # to change notes category
        menu_cat = Menu(self, tearoff=False)
        self.category = StringVar(self)

        # create one tab per category
        for cat in categories:
            menu_cat.add_radiobutton(label=cat.capitalize(),
                                     value=cat,
                                     variable=self.category,
                                     command=self.change_cat_selection)
            self.notes[cat] = {}
            frame = Frame(self.notebook, padding=2)
            self.texts[cat] = Text(frame,
                                   width=1,
                                   height=1,
                                   bg=self.cget('bg'),
                                   relief='flat',
                                   highlightthickness=0,
                                   padx=0,
                                   pady=0,
                                   cursor='arrow')
            frame.columnconfigure(0, weight=1)
            frame.rowconfigure(1, weight=1)

            self.texts[cat].grid(row=1, column=0, sticky='ewsn')
            scrolly = Scrollbar(frame,
                                orient='vertical',
                                command=self.texts[cat].yview)
            scrolly.grid(row=1, column=1, sticky='ns', pady=(2, 0))
            scrollx = Scrollbar(frame,
                                orient='horizontal',
                                command=self.texts[cat].xview)
            scrollx.grid(row=2, column=0, sticky='ew')
            self.texts[cat].configure(xscrollcommand=scrollx.set,
                                      yscrollcommand=scrolly.set)
            self.frames[cat] = Frame(self.texts[cat],
                                     style='bg.TFrame',
                                     padding=1,
                                     height=29,
                                     width=523)
            self.frames[cat].columnconfigure(0, weight=1, minsize=170)
            headings = Frame(frame, padding=(1, 0, 1, 0))
            headings.columnconfigure(0, weight=0, minsize=20)
            headings.columnconfigure(1, weight=1, minsize=198)
            headings.columnconfigure(2, weight=1, minsize=198)
            headings.columnconfigure(3, weight=0, minsize=84)
            headings.columnconfigure(4, weight=0, minsize=22)
            Heading(headings,
                    cat,
                    'title',
                    command=self.sort_column,
                    text=_('Title')).grid(row=0, column=1, sticky='ew')
            Heading(headings,
                    cat,
                    'text',
                    command=self.sort_column,
                    text=_('Text')).grid(row=0, column=2, sticky='ew')
            Heading(headings,
                    cat,
                    'date',
                    command=self.sort_column,
                    text=_('Date')).grid(row=0, column=3, sticky='ew')
            Heading(headings,
                    cat,
                    'select_all',
                    style='select.heading.TLabel',
                    padding=0,
                    command=self.toggle_selectall).place(x=0,
                                                         y=0,
                                                         anchor='nw',
                                                         relheight=1,
                                                         width=20)
            Heading(headings, cat, 'visibility',
                    command=self.sort_column).place(relx=1,
                                                    y=0,
                                                    anchor='ne',
                                                    bordermode='outside',
                                                    width=23,
                                                    relheight=1)
            headings.place(x=0, y=2, anchor='nw')
            self.update_idletasks()
            frame.rowconfigure(0, minsize=headings.winfo_reqheight())
            self.texts[cat].window_create('1.0', window=self.frames[cat])
            b_frame = Frame(frame)
            b_frame.grid(row=3, columnspan=2)
            m = Menubutton(b_frame,
                           image=self.im_change,
                           text=_('Change category'),
                           compound='right',
                           menu=menu_cat,
                           padding=1)
            m.pack(side='left', padx=4, pady=4, fill='y')
            tooltipwrapper.add_tooltip(m,
                                       _('Change category of selected notes'))
            b_show = Button(b_frame,
                            image=self.im_visible,
                            padding=1,
                            command=self.show_selection)
            b_show.pack(side='left', padx=4, pady=4)
            tooltipwrapper.add_tooltip(b_show, _('Show selected notes'))
            b_hide = Button(b_frame,
                            image=self.im_hidden,
                            padding=1,
                            command=self.hide_selection)
            b_hide.pack(side='left', padx=4, pady=4)
            tooltipwrapper.add_tooltip(b_hide, _('Hide selected notes'))
            b_del = Button(b_frame,
                           image=self.im_del,
                           command=self.del_selection,
                           padding=1)
            b_del.pack(side='left', padx=4, pady=4, fill='y')
            tooltipwrapper.add_tooltip(b_del, _('Delete selected notes'))

            self.notebook.add(frame,
                              text=cat.capitalize(),
                              sticky="ewsn",
                              padding=0)
        # display notes by category
        for key, note_data in self.master.note_data.items():
            self.display_note(key, note_data)

        for txt in self.texts.values():
            txt.configure(state='disabled')
        self.geometry('410x450')
        self.bind("<Button-4>", lambda e: self.scroll(-1))
        self.bind("<Button-5>", lambda e: self.scroll(1))
        self.notebook.bind('<<NotebookTabChanged>>', self.on_change_tab)
Beispiel #3
0
    def __init__(self, master):
        Toplevel.__init__(self, master)
        self.title(_("Delete"))
        self.grab_set()
        categories = CONFIG.options("Categories")
        categories.sort()

        self.im_moins = PhotoImage(file=IM_MOINS, master=self)

        self.notebook = Notebook(self)
        self.notebook.pack(fill='both', expand=True)

        self.texts = {}
        frames = {}
        self.notes = {}
        for cat in categories:
            frame = Frame(self.notebook)
            self.texts[cat] = Text(frame,
                                   width=1,
                                   height=1,
                                   bg=self.cget('bg'),
                                   relief='flat',
                                   highlightthickness=0,
                                   padx=4,
                                   pady=4,
                                   cursor='arrow')
            frame.columnconfigure(0, weight=1)
            frame.rowconfigure(0, weight=1)

            self.texts[cat].grid(row=0, column=0, sticky='ewsn', padx=(0, 2))
            scrolly = Scrollbar(frame,
                                orient='vertical',
                                command=self.texts[cat].yview)
            scrolly.grid(row=0, column=1, sticky='ns')
            scrollx = Scrollbar(frame,
                                orient='horizontal',
                                command=self.texts[cat].xview)
            scrollx.grid(row=1, column=0, sticky='ew')
            self.texts[cat].configure(xscrollcommand=scrollx.set,
                                      yscrollcommand=scrolly.set)
            frames[cat] = Frame(self.texts[cat])
            frames[cat].columnconfigure(0, weight=1, minsize=170)
            frames[cat].columnconfigure(1, weight=1, minsize=170)
            frames[cat].columnconfigure(2, minsize=20)
            self.texts[cat].window_create('1.0', window=frames[cat])

            self.notebook.add(frame,
                              text=cat.capitalize(),
                              sticky="ewsn",
                              padding=0)
        for key, note_data in self.master.note_data.items():
            cat = note_data["category"]
            c, r = frames[cat].grid_size()
            self.notes[key] = []
            title = note_data['title'][:20]
            title = title.replace('\t', ' ') + ' ' * (20 - len(title))
            self.notes[key].append(
                Label(frames[cat], text=title, font='TkDefaultFont 10 bold'))
            txt = note_data['txt'].splitlines()
            if txt:
                txt = txt[0][:17] + '...'
            else:
                txt = ''
            txt = txt.replace('\t', ' ') + ' ' * (20 - len(txt))
            self.notes[key].append(Label(frames[cat], text=txt))
            self.notes[key].append(
                Button(frames[cat],
                       image=self.im_moins,
                       command=lambda iid=key: self.delete_note(iid)))
            for i, widget in enumerate(self.notes[key]):
                widget.grid(row=r, column=i, sticky='w', padx=4, pady=4)

        for txt in self.texts.values():
            txt.configure(state='disabled')
        self.geometry('410x450')
        self.bind_all("<Button-4>", lambda e: self.scroll(-1))
        self.bind_all("<Button-5>", lambda e: self.scroll(1))
Beispiel #4
0
    def __init__(self,
                 parent=None,
                 title="",
                 message="",
                 traceback="",
                 report_msg=False,
                 button="Ok",
                 image="error"):
        """
        Create a message box with one button:
            parent: parent of the toplevel window
            title: message box title
            message: message box text (that can be selected)
            button: message displayed on the button
            image: image displayed at the left of the message, either a PhotoImage or a string
        """
        Toplevel.__init__(self, parent)
        self.transient(parent)
        self.resizable(False, False)
        self.title(title)
        self.result = ""
        self.button = button

        style = Style(self)
        style.configure("url.TLabel", foreground="blue")
        style.configure("txt.TFrame", background='white')
        if not parent:
            style.theme_use('clam')

        if isinstance(image, str):
            data = ICONS.get(image)
            if data:
                self.img = PhotoImage(master=self, data=data)
            else:
                self.img = PhotoImage(master=self, file=image)
            image = self.img
        frame = Frame(self)
        frame.rowconfigure(0, weight=1)
        frame.columnconfigure(1, weight=1)
        l = len(message)
        l2 = len(traceback)
        w = max(1, min(max(l, l2), 50))
        h = 0
        for line in message.splitlines():
            h += 1 + len(line) // w
        h2 = 0
        for line in traceback.splitlines():
            h2 += 1 + len(line) // w
        if h + h2 < 3:
            w = min(l, 35)
            h = 0
            for line in message.splitlines():
                h += 1 + len(line) // w
            h2 = 0
            for line in traceback.splitlines():
                h2 += 1 + len(line) // w

        display = Text(frame,
                       relief='flat',
                       highlightthickness=0,
                       font="TkDefaultFont 10 bold",
                       bg=self.cget('bg'),
                       height=h,
                       width=w,
                       wrap="word")
        display.configure(
            inactiveselectbackground=display.cget("selectbackground"))
        display.insert("1.0", message)
        display.configure(state="disabled")
        display.grid(row=0, column=1, pady=(10, 4), padx=4, sticky="ewns")
        display.bind("<Button-1>", lambda event: display.focus_set())
        if image:
            Label(frame, image=image).grid(row=0,
                                           column=0,
                                           padx=4,
                                           pady=(10, 4))

        frame2 = Frame(self)
        frame2.columnconfigure(0, weight=1)
        frame2.rowconfigure(0, weight=1)
        report_frame = Frame(self)
        if traceback:
            txt_frame = Frame(frame2,
                              style='txt.TFrame',
                              relief='sunken',
                              borderwidth=1)
            error_msg = Text(txt_frame,
                             width=w,
                             wrap='word',
                             font="TkDefaultFont 10",
                             bg='white',
                             height=8,
                             highlightthickness=0)
            error_msg.bind("<Button-1>", lambda event: error_msg.focus_set())
            error_msg.insert('1.0', traceback)
            error_msg.configure(state="disabled")
            scrolly = Scrollbar(frame2,
                                orient='vertical',
                                command=error_msg.yview)
            scrolly.grid(row=0, column=1, sticky='ns')
            scrollx = Scrollbar(frame2,
                                orient='horizontal',
                                command=error_msg.xview)
            scrollx.grid(row=1, column=0, sticky='ew')
            error_msg.configure(yscrollcommand=scrolly.set,
                                xscrollcommand=scrollx.set)
            error_msg.pack(side='left', fill='both', expand=True)
            txt_frame.grid(row=0, column=0, sticky='ewsn')
        if report_msg:
            Label(report_frame,
                  text=_("Please report this bug on ")).pack(side="left")
            url = Label(report_frame,
                        style="url.TLabel",
                        cursor="hand1",
                        font="TkDefaultFont 10 underline",
                        text="https://github.com/j4321/MyNotes/issues")
            url.pack(side="left")
            url.bind(
                "<Button-1>",
                lambda e: url_open("https://github.com/j4321/MyNotes/issues"))
        b = Button(self, text=button, command=self.validate)
        frame.pack(fill='x')
        frame2.pack(fill='both', padx=4, pady=(4, 4))
        report_frame.pack(fill="x", padx=4, pady=(4, 0))
        b.pack(padx=10, pady=10)
        self.grab_set()
        b.focus_set()
Beispiel #5
0
    def __init__(self, master):
        """Create Config dialog."""
        Toplevel.__init__(self, master, class_='MyNotes')
        self.title(_("Preferences"))
        self.grab_set()
        self.protocol("WM_DELETE_WINDOW", self.quit)
        self.changes = {}, {}, False, False
        self.minsize(width=430, height=450)

        # --- style
        style = Style(self)
        style.theme_use("clam")
        style.configure("TScale", sliderlength=20)
        style.map("TCombobox",
                  fieldbackground=[('readonly', 'white')],
                  selectbackground=[('readonly', 'white')],
                  selectforeground=[('readonly', 'black')])
        style.configure("prev.TLabel", background="white")
        style.map("prev.TLabel", background=[("active", "white")])
        color = CONFIG.get("Categories",
                           CONFIG.get("General", "default_category"))
        style.configure("titlebar.TFrame", background=color)
        style.configure("titlebar.TLabel", background=color)
        style.configure("text.TFrame", background="white")

        # --- body
        self.notebook = Notebook(self)
        okcancel_frame = Frame(self)
        okcancel_frame.columnconfigure(0, weight=1)
        okcancel_frame.columnconfigure(1, weight=1)
        okcancel_frame.pack(fill="x", side='bottom')
        self.notebook.pack(expand=True, fill="both")

        # --- * General settings
        self._init_general()

        # --- * Font settings
        self._init_font()

        # --- * Categories
        self.category_settings = CategoryManager(self.notebook, master)
        self.notebook.add(self.category_settings,
                          text=_("Categories"),
                          sticky="ewsn",
                          padding=4)
        # --- * Symbols
        size = CONFIG.get("Font", "text_size")
        family = CONFIG.get("Font", "text_family")
        symbols_settings = Frame(self.notebook, padding=4)
        self.notebook.add(symbols_settings,
                          text=_("Symbols"),
                          sticky="ewsn",
                          padding=4)
        txt_frame = Frame(symbols_settings,
                          relief="sunken",
                          borderwidth=1,
                          style="text.TFrame")
        txt_frame.rowconfigure(0, weight=1)
        txt_frame.columnconfigure(0, weight=1)
        self.symbols = Text(txt_frame,
                            width=1,
                            height=1,
                            highlightthickness=0,
                            spacing2=5,
                            spacing1=5,
                            relief="flat",
                            padx=4,
                            pady=4,
                            font="%s %s" % (family.replace(" ", "\ "), size))
        scroll_y = AutoScrollbar(txt_frame,
                                 orient='vertical',
                                 command=self.symbols.yview)
        self.symbols.configure(yscrollcommand=scroll_y.set)

        self.symbols.insert("1.0", CONFIG.get("General", "symbols"))
        Label(symbols_settings, text=_("Available symbols")).pack(padx=4,
                                                                  pady=4)
        txt_frame.pack(fill="both", expand=True, padx=4, pady=4)
        self.symbols.grid(sticky='ewns')
        scroll_y.grid(row=0, column=1, sticky='ns')
        Button(symbols_settings, text=_('Reset'),
               command=self.reset_symbols).pack(padx=4, pady=4)

        # --- * AutoCorrect
        self.autocorrect_settings = AutoCorrectConfig(self.notebook, master)
        self.notebook.add(self.autocorrect_settings,
                          text=_("AutoCorrect"),
                          sticky="ewsn",
                          padding=4)

        # --- Ok/Cancel buttons
        Button(okcancel_frame, text="Ok", command=self.ok).grid(row=1,
                                                                column=0,
                                                                padx=4,
                                                                pady=10,
                                                                sticky="e")
        Button(okcancel_frame, text=_("Cancel"),
               command=self.destroy).grid(row=1,
                                          column=1,
                                          padx=4,
                                          pady=10,
                                          sticky="w")
Beispiel #6
0
    def __init__(self, master, note_data):
        """Create export dialog."""
        Toplevel.__init__(self, master, class_='MyNotes')
        self.title(_("Export"))
        self.minsize(350, 250)
        self.grab_set()
        self.columnconfigure(0, weight=1)
        self.rowconfigure(3, weight=1)

        self.note_data = note_data
        self.categories = CONFIG.options("Categories")
        self.categories.sort()
        self.notes_to_export = []
        self.export_type = None
        self.export_data = False

        # export type
        self.type = StringVar(self, _("Notes (.notes)"))

        type_frame = Frame(self)
        menu_type = Menu(self, tearoff=False)
        for etype in EXT_DICT:
            menu_type.add_radiobutton(label=etype, value=etype, variable=self.type)
        mb = Menubutton(type_frame, menu=menu_type, textvariable=self.type, width=max([int(len(key) * 0.8) for key in EXT_DICT]))
        Label(type_frame, text=_('Export to')).pack(side='left', padx=4)
        mb.pack(side='left', padx=4)
        type_frame.grid(row=0, columnspan=2, sticky='w', pady=4)

        Separator(self).grid(columnspan=2, sticky="ew", padx=4, pady=4)

        # export only visible notes checkbutton
        self.ch_only_visible = Checkbutton(self, text=_("Only visible notes"),
                                           command=self.select_only_visible)
        self.ch_only_visible.grid(columnspan=2, sticky="w", padx=4, pady=4)

        # note selection
        self.tree = CheckboxTreeview(self, show='tree')
        self.tree.grid(row=3, sticky="nsew", padx=4, pady=4)
        scroll = Scrollbar(self, orient='vertical', command=self.tree.yview)
        self.tree.configure(yscrollcommand=scroll.set)
        scroll.grid(row=3, column=1, sticky='ns')

        self.tree.insert('', 'end', 'root', text=_('Categories'))
        for cat in self.categories:
            self.tree.insert('root', 'end', cat, text=cat.capitalize())
        for key, data in self.note_data.items():
            self.tree.insert(data['category'], 'end', key,
                             text='{} - {}'.format(data['title'], data.get('date', '??')),
                             tags=['visible'] if data['visible'] else [])
        for cat in self.categories:
            if not self.tree.get_children(cat):
                self.tree.detach(cat)
        self.tree.bind('<<Checked>>', self.toggle_select_visible)
        self.tree.bind('<<Unchecked>>', self.toggle_select_visible)

        Separator(self).grid(sticky="ew", columnspan=2, padx=4, pady=4)
        self.ch_export_data = Checkbutton(self, text=_('Export data (pictures and linked files)'))
        self.ch_export_data.grid(sticky="w", columnspan=2, padx=4, pady=4)

        frame = Frame(self)
        frame.grid(columnspan=2)

        Button(frame, text="Ok",
               command=self.ok).grid(row=0, column=0, sticky="w", padx=4, pady=4)
        Button(frame, text=_("Cancel"),
               command=self.destroy).grid(row=0, column=1, sticky="e", padx=4, pady=4)
        self.tree.check_item('root')
        self.tree.expand_all()
        self.toggle_select_visible()
Beispiel #7
0
    def __init__(self, master, app, **kwargs):
        """Create category manager."""
        Frame.__init__(self, master, padding=4, **kwargs)
        self.columnconfigure(0, weight=1)
        self.rowconfigure(1, weight=1)

        self.app = app

        self.style = Style(self)
        self.style.theme_use("clam")

        self.im_plus = PhotoImage(file=IM_PLUS)
        self.im_delete = PhotoImage(file=IM_DELETE)

        # --- Default category
        self.frame_def_cat = Frame(self)
        self.default_category = StringVar(
            self.frame_def_cat,
            CONFIG.get("General", "default_category").capitalize())
        Label(self.frame_def_cat,
              text=_("Default category ")).grid(row=0,
                                                column=0,
                                                sticky="e",
                                                padx=(4, 0))
        self.categories = CONFIG.options("Categories")
        self.categories.sort()
        categories = [cat.capitalize() for cat in self.categories]
        self.def_cat_menu = OptionMenu(
            self.frame_def_cat, self.default_category,
            CONFIG.get("General", "default_category").capitalize(),
            *categories)
        optionmenu_patch(self.def_cat_menu, self.default_category)
        self.def_cat_menu.grid(row=0, column=1, sticky="w", padx=4, pady=4)

        # --- Category colors, names ...
        style = Style(self)
        style.configure('txt.TFrame', relief='ridge', border=2)
        bg = style.lookup('TFrame', 'background')
        frame = Frame(self, style='txt.TFrame', padding=1)
        frame.columnconfigure(0, weight=1)
        frame.rowconfigure(0, weight=1)
        txt = Text(frame,
                   width=1,
                   height=1,
                   bg=bg,
                   relief='flat',
                   highlightthickness=0,
                   padx=6,
                   pady=6,
                   cursor='arrow')
        scroll_x = AutoScrollbar(frame, orient='horizontal', command=txt.xview)
        scroll_y = AutoScrollbar(frame, orient='vertical', command=txt.yview)
        txt.configure(xscrollcommand=scroll_x.set, yscrollcommand=scroll_y.set)

        txt.grid(row=0, column=0, sticky='ewns')
        scroll_x.grid(row=1, column=0, sticky='ew')
        scroll_y.grid(row=0, column=1, sticky='ns')

        self.frame_cat = Frame(txt)
        txt.window_create('1.0', window=self.frame_cat)
        txt.configure(state='disabled')
        self.colors = list(COLORS.keys())
        self.colors.sort()
        self.images = []
        self.cat_colors = {}
        self.cat_labels = {}
        self.cat_menus = {}
        self.cat_buttons = {}
        for i, cat in enumerate(self.categories):
            self.cat_labels[cat] = Label(self.frame_cat,
                                         text="%s" % cat.capitalize(),
                                         anchor='e')
            self.cat_labels[cat].grid(row=i + 2, column=0, sticky="ew", padx=2)
            self.cat_labels[cat].bind('<Double-Button-1>', self.change_name)
            self.cat_colors[cat] = StringVar(self)
            color = CONFIG.get("Categories", cat)
            self.cat_menus[cat] = OptionMenu(self.frame_cat,
                                             self.cat_colors[cat],
                                             INV_COLORS[color],
                                             *self.colors,
                                             command=lambda color, c=cat: self.
                                             change_menubutton_color(color, c),
                                             style="%s.TMenubutton" % cat)
            optionmenu_patch(self.cat_menus[cat], self.cat_colors[cat])
            self.style.configure("%s.TMenubutton" % cat, background=color)
            self.cat_menus[cat].grid(row=i + 2,
                                     column=1,
                                     sticky="w",
                                     padx=4,
                                     pady=4)
            self.cat_buttons[cat] = Button(
                self.frame_cat,
                image=self.im_delete,
                padding=0,
                command=lambda c=cat: self.del_cat(c))
            self.cat_buttons[cat].grid(row=i + 2,
                                       column=2,
                                       padx=4,
                                       pady=4,
                                       sticky='ns')

        if len(self.categories) == 1:
            self.cat_buttons[self.categories[0]].configure(state="disabled")

        # --- placement
        self.frame_def_cat.grid(row=0, column=0, sticky="eswn", pady=4)
        frame.grid(row=1, column=0, sticky="eswn")
        Button(self, image=self.im_plus, command=self.add_cat).grid(row=2,
                                                                    column=0,
                                                                    sticky='w',
                                                                    pady=8)