예제 #1
0
    def _create_menu(self):
        BaseWidget._create_menu(self)

        self._sort_order = StringVar(
            self, LATESTS.get(self.name, 'sort_order', fallback='A-Z'))
        add_trace(self._sort_order, 'write', self._order_trace)
        self.menu_sort.add_radiobutton(
            label='A-Z',
            variable=self._sort_order,
            value='A-Z',
            command=lambda: self._sort_by_name(reverse=False))
        self.menu_sort.add_radiobutton(
            label='Z-A',
            variable=self._sort_order,
            value='Z-A',
            command=lambda: self._sort_by_name(reverse=True))
        self.menu_sort.add_radiobutton(
            label=_('Oldest first'),
            variable=self._sort_order,
            value='oldest',
            command=lambda: self._sort_by_date(reverse=False))
        self.menu_sort.add_radiobutton(
            label=_('Most recent first'),
            variable=self._sort_order,
            value='latest',
            command=lambda: self._sort_by_date(reverse=True))
        if self.name != 'All':
            self.menu.add_command(label=_('Remove category'),
                                  command=self.remove_cat)
예제 #2
0
 def feed_change_cat(self, title, old_cat, new_cat):
     if old_cat != new_cat:
         FEEDS.set(title, 'category', new_cat)
         if old_cat != '':
             self.cat_widgets[old_cat].remove_feed(title)
         if new_cat != '':
             if new_cat not in LATESTS.sections():
                 LATESTS.add_section(new_cat)
                 LATESTS.set(new_cat, 'visible', 'True')
                 LATESTS.set(new_cat, 'geometry', '')
                 LATESTS.set(new_cat, 'position', 'normal')
                 LATESTS.set(new_cat, 'sort_order', 'A-Z')
                 self.cat_widgets[new_cat] = CatWidget(self, new_cat)
                 self.cat_widgets[new_cat].event_generate('<Configure>')
                 self.menu_categories.add_checkbutton(
                     label=new_cat,
                     command=lambda: self.toggle_category_widget(new_cat))
                 cst.add_trace(self.cat_widgets[new_cat].variable, 'write',
                               lambda *args: self.cat_widget_trace(new_cat))
                 self.cat_widgets[new_cat].variable.set(True)
             else:
                 try:
                     filename = FEEDS.get(title, 'data')
                     latest = cst.feed_get_latest(filename)
                 except (configparser.NoOptionError,
                         pickle.UnpicklingError):
                     latest = ''
                 self.cat_widgets[new_cat].entry_add(
                     title, FEEDS.get(title, 'updated'), latest,
                     FEEDS.get(title, 'url'))
예제 #3
0
    def feed_rename(self, old_name, new_name):
        options = {
            opt: FEEDS.get(old_name, opt)
            for opt in FEEDS.options(old_name)
        }
        FEEDS.remove_section(old_name)
        try:
            # check if feed's title already exists
            FEEDS.add_section(new_name)
        except configparser.DuplicateSectionError:
            i = 2
            duplicate = True
            while duplicate:
                # increment i until new_name~#i does not already exist
                try:
                    FEEDS.add_section("{}~#{}".format(new_name, i))
                except configparser.DuplicateSectionError:
                    i += 1
                else:
                    duplicate = False
                    name = "{}~#{}".format(new_name, i)
        else:
            name = new_name
        logging.info("Renamed feed '%s' to '%s'", old_name, name)
        for opt, val in options.items():
            FEEDS.set(name, opt, val)
        self._check_result_init_id[name] = self._check_result_init_id.pop(
            old_name, '')
        self._check_result_update_id[name] = self._check_result_update_id.pop(
            old_name, '')
        self.threads[name] = self.threads.pop(old_name, None)
        self.queues[name] = self.queues.pop(old_name)
        self.feed_widgets[name] = self.feed_widgets.pop(old_name)
        self.feed_widgets[name].rename_feed(name)
        self.cat_widgets['All'].rename_feed(old_name, name)
        category = FEEDS.get(name, 'category', fallback='')
        if category != '':
            self.cat_widgets[category].rename_feed(old_name, name)
        self.menu_feeds.delete(old_name)
        self.menu_feeds.add_checkbutton(
            label=name, command=lambda: self.toggle_feed_widget(name))
        trace_info = cst.info_trace(self.feed_widgets[name].variable)
        if trace_info:
            cst.remove_trace(self.feed_widgets[name].variable, 'write',
                             trace_info[0][1])
        cst.add_trace(self.feed_widgets[name].variable, 'write',
                      lambda *args: self.feed_widget_trace(name))
        self.menu_feeds.set_item_value(name,
                                       self.feed_widgets[name].variable.get())

        cst.save_feeds()
        return name
예제 #4
0
파일: font.py 프로젝트: j4321/FeedAgregator
    def __init__(self,
                 master,
                 font,
                 style=False,
                 sample_text=_("Sample text")):
        ttk.Frame.__init__(self, master)

        # entry validation
        self._validate = self.register(self._validate_entry_nb)
        self._validate_size = self.register(self._validate_font_size)

        # chooser
        self.font = tkfont.Font(self, font=font)
        sample = ttk.Label(self,
                           text=sample_text,
                           anchor="center",
                           font=self.font,
                           style="white.TLabel",
                           relief="groove")

        sample.grid(row=2,
                    columnspan=2,
                    padx=4,
                    pady=6,
                    ipadx=4,
                    ipady=4,
                    sticky="eswn")
        self.fonts = list(set(tkfont.families()))
        self.fonts.append("TkDefaultFont")
        self.fonts.sort()

        prop = self.font.actual()
        self.font_family = tk.StringVar(self, value=prop['family'])
        add_trace(
            self.font_family, 'write',
            lambda *args: self.font.configure(family=self.font_family.get()))
        self.font_size = tk.StringVar(self, value=prop['size'])
        add_trace(self.font_size, 'write',
                  lambda *args: self._config_size(self.font_size, self.font))

        w = max([len(f) for f in self.fonts])
        sizes = list(range(6, 17)) + list(range(18, 32, 2))
        if not prop['size'] in sizes:
            sizes.append(prop['size'])
        sizes.sort()
        self.sizes = ["%i" % i for i in sizes]

        self.choose_family = AutoCompleteCombobox(
            self,
            values=self.fonts,
            width=(w * 2) // 3,
            textvariable=self.font_family,
            exportselection=False)
        self.choose_family.current(self.fonts.index(prop['family']))
        self.choose_family.grid(row=0, column=0, padx=4, pady=4)
        self.choose_size = ttk.Combobox(self,
                                        values=self.sizes,
                                        width=5,
                                        exportselection=False,
                                        textvariable=self.font_size,
                                        validate="key",
                                        validatecommand=(self._validate_size,
                                                         "%d", "%P", "%V"))
        self.choose_size.current(self.sizes.index(str(prop['size'])))
        self.choose_size.grid(row=0, column=1, padx=4, pady=4)

        if style:
            frame_style = ttk.Frame(self)
            frame_style.grid(row=1, columnspan=2, pady=6)
            self.bold = tk.StringVar(self, value=prop['weight'])
            add_trace(
                self.bold, 'write',
                lambda *args: self.font.configure(weight=self.bold.get()))
            self.italic = tk.StringVar(self, value=prop['slant'])
            add_trace(
                self.italic, 'write',
                lambda *args: self.font.configure(slant=self.italic.get()))
            self.underline = tk.BooleanVar(self, value=prop['underline'])
            add_trace(
                self.underline, 'write',
                lambda *args: self.font.configure(underline=self.underline.get(
                )))
            ttk.Checkbutton(frame_style,
                            text=_("Bold"),
                            onvalue='bold',
                            offvalue='normal',
                            variable=self.bold).pack(side='left', padx=4)
            ttk.Checkbutton(frame_style,
                            text=_("Italic"),
                            onvalue='italic',
                            offvalue='roman',
                            variable=self.italic).pack(side='left', padx=4)
            ttk.Checkbutton(frame_style,
                            text=_("Underline"),
                            variable=self.underline).pack(side='left', padx=4)
예제 #5
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)
예제 #6
0
 def _check_result_add(self, thread, queue, url, manager_queue=None):
     if thread.is_alive():
         self._check_add_id = self.after(1000, self._check_result_add,
                                         thread, queue, url, manager_queue)
     else:
         title, latest, date, data = queue.get(False)
         if title:
             try:
                 # check if feed's title already exists
                 FEEDS.add_section(title)
             except configparser.DuplicateSectionError:
                 i = 2
                 duplicate = True
                 while duplicate:
                     # increment i until title~#i does not already exist
                     try:
                         FEEDS.add_section("{}~#{}".format(title, i))
                     except configparser.DuplicateSectionError:
                         i += 1
                     else:
                         duplicate = False
                         name = "{}~#{}".format(title, i)
             else:
                 name = title
             if manager_queue is not None:
                 manager_queue.put(name)
             logging.info("Added feed '%s' %s", name, url)
             if CONFIG.getboolean("General", "notifications",
                                  fallback=True):
                 run([
                     "notify-send", "-i", cst.IM_ICON_SVG, name,
                     cst.html2text(latest)
                 ])
             self.cat_widgets['All'].entry_add(name, date, latest, url)
             filename = cst.new_data_file()
             cst.save_data(filename, latest, data)
             FEEDS.set(name, 'url', url)
             FEEDS.set(name, 'updated', date)
             FEEDS.set(name, 'data', filename)
             FEEDS.set(name, 'visible', 'True')
             FEEDS.set(name, 'geometry', '')
             FEEDS.set(name, 'position', 'normal')
             FEEDS.set(name, 'category', '')
             FEEDS.set(name, 'sort_is_reversed', 'False')
             FEEDS.set(name, 'active', 'True')
             cst.save_feeds()
             self.queues[name] = queue
             self.feed_widgets[name] = FeedWidget(self, name)
             self.menu_feeds.add_checkbutton(
                 label=name, command=lambda: self.toggle_feed_widget(name))
             cst.add_trace(self.feed_widgets[name].variable, 'write',
                           lambda *args: self.feed_widget_trace(name))
             self.feed_widgets[name].variable.set(True)
             for entry_title, date, summary, link in data:
                 self.feed_widgets[name].entry_add(entry_title, date,
                                                   summary, link, -1)
         else:
             if manager_queue is not None:
                 manager_queue.put('')
             if cst.internet_on():
                 logging.error('%s is not a valid feed.', url)
                 showerror(_('Error'),
                           _('{url} is not a valid feed.').format(url=url))
             else:
                 logging.warning('No Internet connection.')
                 showerror(_('Error'), _('No Internet connection.'))
예제 #7
0
    def __init__(self):
        Tk.__init__(self, className=cst.APP_NAME)
        self.protocol("WM_DELETE_WINDOW", self.quit)
        self.withdraw()

        logging.info('Starting %s', cst.APP_NAME)

        self.im_icon = PhotoImage(master=self, file=cst.IM_ICON_48)
        self.iconphoto(True, self.im_icon)

        # --- style
        self.style = Style(self)
        self.style.theme_use("clam")
        self.style.configure("TScale", sliderlength=20)
        self.style.map("TCombobox",
                       fieldbackground=[('readonly', 'white')],
                       selectbackground=[('readonly', 'white')],
                       selectforeground=[('readonly', 'black')])
        self.style.configure("title.TLabel", font="TkDefaultFont 9 bold")
        self.style.configure("white.TLabel", background="white")
        self.style.map("white.TLabel", background=[("active", "white")])
        self.style.configure('heading.TLabel',
                             relief='ridge',
                             borderwidth=1,
                             padding=(10, 4))
        self.style.configure('manager.TButton', padding=0)
        self.style.map('manager.Treeview', background=[], foreground=[])
        self.style.layout(
            'no_edit.TEntry',
            [('Entry.padding', {
                'children': [('Entry.textarea', {
                    'sticky': 'nswe'
                })],
                'sticky': 'nswe'
            })])
        self.style.configure('no_edit.TEntry',
                             background='white',
                             padding=[4, 0])
        self.style.configure('manager.TEntry', padding=[2, 1])
        self.style.layout('manager.Treeview.Row', [('Treeitem.row', {
            'sticky': 'nswe'
        }), ('Treeitem.image', {
            'side': 'right',
            'sticky': 'e'
        })])
        self.style.layout('manager.Treeview.Item', [('Treeitem.padding', {
            'children': [('Checkbutton.indicator', {
                'side': 'left',
                'sticky': ''
            }), ('Treeitem.text', {
                'side': 'left',
                'sticky': ''
            })],
            'sticky':
            'nswe'
        })])

        self._im_trough = tkPhotoImage(name='trough-scrollbar-vert',
                                       width=15,
                                       height=15,
                                       master=self)
        bg = CONFIG.get("Widget", 'background', fallback='gray10')
        widget_bg = (0, 0, 0)
        widget_fg = (255, 255, 255)
        vmax = self.winfo_rgb('white')[0]
        color = tuple(int(val / vmax * 255) for val in widget_bg)
        active_bg = cst.active_color(color)
        active_bg2 = cst.active_color(cst.active_color(color, 'RGB'))
        slider_vert_insens = Image.new('RGBA', (13, 28), widget_bg)
        slider_vert = Image.new('RGBA', (13, 28), active_bg)
        slider_vert_active = Image.new('RGBA', (13, 28), widget_fg)
        slider_vert_prelight = Image.new('RGBA', (13, 28), active_bg2)
        self._im_trough.put(" ".join(["{" + " ".join([bg] * 15) + "}"] * 15))
        self._im_slider_vert_active = PhotoImage(slider_vert_active,
                                                 name='slider-vert-active',
                                                 master=self)
        self._im_slider_vert = PhotoImage(slider_vert,
                                          name='slider-vert',
                                          master=self)
        self._im_slider_vert_prelight = PhotoImage(slider_vert_prelight,
                                                   name='slider-vert-prelight',
                                                   master=self)
        self._im_slider_vert_insens = PhotoImage(slider_vert_insens,
                                                 name='slider-vert-insens',
                                                 master=self)
        self.style.element_create('widget.Vertical.Scrollbar.trough', 'image',
                                  'trough-scrollbar-vert')
        self.style.element_create(
            'widget.Vertical.Scrollbar.thumb',
            'image',
            'slider-vert', ('pressed', '!disabled', 'slider-vert-active'),
            ('active', '!disabled', 'slider-vert-prelight'),
            ('disabled', 'slider-vert-insens'),
            border=6,
            sticky='ns')
        self.style.layout('widget.Vertical.TScrollbar', [
            ('widget.Vertical.Scrollbar.trough', {
                'children': [('widget.Vertical.Scrollbar.thumb', {
                    'expand': '1'
                })],
                'sticky': 'ns'
            })
        ])

        hide = Image.new('RGBA', (12, 12), active_bg2)
        hide_active = Image.new('RGBA', (12, 12), widget_fg)
        hide_pressed = Image.new('RGBA', (12, 12), (150, 0, 0))
        toggle_open = Image.new('RGBA', (9, 9), widget_fg)
        toggle_open_active = Image.new('RGBA', (9, 9), active_bg2)
        toggle_close = Image.new('RGBA', (9, 9), widget_fg)
        toggle_close_active = Image.new('RGBA', (9, 9), active_bg2)
        self._im_hide = PhotoImage(hide, master=self)
        self._im_hide_active = PhotoImage(hide_active, master=self)
        self._im_hide_pressed = PhotoImage(hide_pressed, master=self)
        self._im_open = PhotoImage(toggle_open, master=self)
        self._im_open_active = PhotoImage(toggle_open_active, master=self)
        self._im_close = PhotoImage(toggle_close, master=self)
        self._im_close_active = PhotoImage(toggle_close_active, master=self)

        self.style.element_create(
            "toggle",
            "image",
            self._im_close, ("!hover", "selected", "!disabled", self._im_open),
            ("hover", "!selected", "!disabled", self._im_close_active),
            ("hover", "selected", "!disabled", self._im_open_active),
            border=2,
            sticky='')
        self.style.layout('Toggle', [('Toggle.border', {
            'children': [('Toggle.padding', {
                'children': [('Toggle.toggle', {
                    'sticky': 'nswe'
                })],
                'sticky': 'nswe'
            })],
            'sticky':
            'nswe'
        })])
        self.style.configure('widget.close.TButton',
                             background=bg,
                             relief='flat',
                             image=self._im_hide,
                             padding=0)
        self.style.map('widget.close.TButton',
                       background=[],
                       relief=[],
                       image=[('active', '!pressed', self._im_hide_active),
                              ('active', 'pressed', self._im_hide_pressed)])
        self.option_add('*Toplevel.background',
                        self.style.lookup('TFrame', 'background'))
        self.option_add('*{app_name}.background'.format(app_name=cst.APP_NAME),
                        self.style.lookup('TFrame', 'background'))
        self.widget_style_init()

        # --- tray icon menu
        self.icon = TrayIcon(cst.ICON)
        self.menu_widgets = SubMenu(parent=self.icon.menu)

        self.menu_categories = SubMenu(parent=self.menu_widgets)
        self.menu_categories.add_command(label=_('Hide all'),
                                         command=self.hide_all_cats)
        self.menu_categories.add_command(label=_('Show all'),
                                         command=self.hide_all_cats)
        self.menu_categories.add_separator()

        self.menu_feeds = SubMenu(parent=self.menu_widgets)
        self.menu_feeds.add_command(label=_('Hide all'),
                                    command=self.hide_all_feeds)
        self.menu_feeds.add_command(label=_('Show all'),
                                    command=self.show_all_feeds)
        self.menu_feeds.add_separator()

        self.menu_widgets.add_command(label=_('Hide all'),
                                      command=self.hide_all)
        self.menu_widgets.add_command(label=_('Show all'),
                                      command=self.show_all)
        self.menu_widgets.add_separator()
        self.menu_widgets.add_cascade(label=_('Categories'),
                                      menu=self.menu_categories)
        self.menu_widgets.add_cascade(label=_('Feeds'), menu=self.menu_feeds)

        self.icon.menu.add_cascade(label=_('Widgets'), menu=self.menu_widgets)
        self.icon.menu.add_command(label=_('Add feed'), command=self.add)
        self.icon.menu.add_command(label=_('Update feeds'),
                                   command=self.feed_update)
        self.icon.menu.add_command(label=_('Manage feeds'),
                                   command=self.feed_manage)
        self.icon.menu.add_command(label=_("Suspend"), command=self.start_stop)
        self.icon.menu.add_separator()
        self.icon.menu.add_command(label=_('Settings'), command=self.settings)
        self.icon.menu.add_command(label=_("Check for updates"),
                                   command=lambda: UpdateChecker(self, True))
        self.icon.menu.add_command(label=_("Help"), command=lambda: Help(self))
        self.icon.menu.add_command(label=_("About"),
                                   command=lambda: About(self))
        self.icon.menu.add_command(label=_('Quit'), command=self.quit)
        self.icon.loop(self)

        self._notify_no_internet = True

        self._internet_id = ""
        self._update_id = ""
        self._check_add_id = ""
        self._check_end_update_id = ""
        self._check_result_update_id = {}
        self._check_result_init_id = {}
        self.queues = {}
        self.threads = {}

        # --- category widgets
        self.cat_widgets = {}
        self.cat_widgets['All'] = CatWidget(self, 'All')
        self.cat_widgets['All'].event_generate('<Configure>')
        self.menu_widgets.add_checkbutton(label=_('Latests'),
                                          command=self.toggle_latests_widget)
        cst.add_trace(self.cat_widgets['All'].variable, 'write',
                      self.latests_widget_trace)
        self.cat_widgets['All'].variable.set(
            LATESTS.getboolean('All', 'visible'))
        cats = LATESTS.sections()

        cats.remove('All')
        for category in cats:
            self.cat_widgets[category] = CatWidget(self, category)
            self.cat_widgets[category].event_generate('<Configure>')
            self.menu_categories.add_checkbutton(
                label=category,
                command=lambda c=category: self.toggle_category_widget(c))
            cst.add_trace(self.cat_widgets[category].variable,
                          'write',
                          lambda *args, c=category: self.cat_widget_trace(c))
            self.cat_widgets[category].variable.set(
                LATESTS.getboolean(category, 'visible'))

        # --- feed widgets
        self.feed_widgets = {}
        for title in FEEDS.sections():
            self._check_result_update_id[title] = ''
            self._check_result_init_id[title] = ''
            self.queues[title] = Queue(1)
            self.threads[title] = None
            self.menu_feeds.add_checkbutton(
                label=title,
                command=lambda t=title: self.toggle_feed_widget(t))
            self.feed_widgets[title] = FeedWidget(self, title)
            cst.add_trace(self.feed_widgets[title].variable,
                          'write',
                          lambda *args, t=title: self.feed_widget_trace(t))
            self.feed_widgets[title].variable.set(
                FEEDS.getboolean(title, 'visible', fallback=True))
        self.feed_init()

        # --- check for updates
        if CONFIG.getboolean("General", "check_update"):
            UpdateChecker(self)

        self.bind_class('TEntry', '<Control-a>', self.entry_select_all)