예제 #1
0
    def del_task(self, task):
        """ Suppression de tâches """
        rep = askyesno(
            _("Confirmation"),
            _("Are you sure you want to delete the task {task}? This action cannot be undone."
              ).format(task=task.capitalize()))
        if rep:
            CONFIG.remove_option("PomodoroTasks", task)
            # remove stats
            db = sqlite3.connect(PATH_STATS)
            cursor = db.cursor()
            try:
                cursor.execute('DROP TABLE {}'.format(
                    scrub(task.lower().replace(' ', '_'))))
                db.commit()
            except sqlite3.OperationalError:
                pass  # no stats yet
            db.close()
            self.tasks[task].destroy()
            self._tasks_btns[task].destroy()
            del self.tasks[task]
            del self._tasks_btns[task]

            if len(CONFIG.options("PomodoroTasks")) == 1:
                CONFIG.set("PomodoroTasks", _("Work"), CMAP[0])
                self._tasks_btns[CONFIG.options("PomodoroTasks")[0]].state(
                    ['disabled'])
            save_config()
예제 #2
0
        def ajoute(event=None):
            task = nom.get().lower().strip()
            if task in self.tasks:
                showerror(
                    _("Error"),
                    _("The task {task} already exists.").format(task=task),
                    parent=self)

            elif task:
                coul = CMAP[(len(self.tasks) + 1) % len(CMAP)]
                i = self.task_frame.grid_size()[1] + 1
                self.tasks[task] = ColorFrame(self.task_frame, coul,
                                              task.capitalize())
                self.tasks[task].grid(row=i,
                                      column=0,
                                      sticky='e',
                                      padx=4,
                                      pady=4)
                b = Button(self.task_frame,
                           image=self.im_moins,
                           padding=2,
                           command=lambda t=task: self.del_task(t))
                b.grid(row=i, column=1, sticky='w', padx=4, pady=4)
                self._tasks_btns[task] = b
                self._tasks_btns[CONFIG.options("PomodoroTasks")[0]].state(
                    ['!disabled'])
            top.destroy()
예제 #3
0
 def update_style(self):
     bg = CONFIG.get(self.name, 'background', fallback='grey10')
     fg = CONFIG.get(self.name, 'foreground', fallback='white')
     active_bg = active_color(*self.winfo_rgb(bg))
     self.attributes('-alpha', CONFIG.get(self.name, 'alpha',
                                          fallback=0.85))
     self.configure(bg=bg)
     self._calendar.menu.configure(bg=bg,
                                   fg=fg,
                                   selectcolor=fg,
                                   activeforeground=fg,
                                   activebackground=active_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)
     keys = self._calendar.keys()
     opts = {
         opt: CONFIG.get('Calendar', opt)
         for opt in CONFIG.options('Calendar') if opt in keys
     }
     self._calendar.configure(**opts)
     self._calendar['font'] = CONFIG.get('Calendar', 'font')
     self._calendar.update_style()
예제 #4
0
 def __init__(self, scheduler, iid=None, **kw):
     d = datetime.now() + timedelta(minutes=5)
     d = d.replace(minute=(d.minute // 5) * 5)
     d = kw.pop('Start', d)
     self.scheduler = scheduler
     defaults = {
         'Summary': '',
         'Place': '',
         'Description': '',
         'Start': d,
         'End': d + timedelta(hours=1),
         'Task': False,
         'Repeat': {},
         'WholeDay': False,
         'Reminders': {},
         'Category': CONFIG.options('Categories')[0]
     }
     defaults.update(kw)
     self._properties = defaults
     self.iid = iid
예제 #5
0
파일: settings.py 프로젝트: j4321/Scheduler
    def _init_calendar(self):
        # --- general config
        general = ttk.Frame(self.frames[_('Calendar')], padding=4)
        general.columnconfigure(1, weight=1)
        self.frames[_('Calendar')].add(general, text=_('General'))
        # --- --- opacity
        self.cal_opacity = OpacityFrame(
            general, CONFIG.getfloat('Calendar', 'alpha', fallback=0.85))
        self.cal_opacity.grid(row=0, columnspan=2, sticky='w', padx=4)

        ttk.Separator(general, orient='horizontal').grid(row=1,
                                                         columnspan=2,
                                                         pady=10,
                                                         sticky='ew')
        # --- --- font
        ttk.Label(general, text=_('Font'),
                  style='title.TLabel').grid(row=2,
                                             sticky='nw',
                                             padx=4,
                                             pady=4)
        self.cal_font = FontFrame(general, CONFIG.get('Calendar', 'font'))
        self.cal_font.grid(row=2, column=1, sticky='w', padx=4, pady=4)

        # --- Colors
        frame_color = ttk.Frame(self.frames[_('Calendar')], padding=4)
        frame_color.columnconfigure(3, weight=1)
        self.frames[_('Calendar')].add(frame_color, text=_('Colors'))
        self.cal_colors = {}

        ttk.Label(frame_color, style='subtitle.TLabel',
                  text=_('General')).grid(row=0, column=0, sticky='w')
        self.cal_bg = ColorFrame(frame_color,
                                 CONFIG.get('Calendar', 'background'),
                                 _('Background'))
        self.cal_fg = ColorFrame(frame_color,
                                 CONFIG.get('Calendar', 'foreground'),
                                 _('Foreground'))
        self.cal_bd = ColorFrame(frame_color,
                                 CONFIG.get('Calendar', 'bordercolor'),
                                 _('Border'))
        self.cal_bg.grid(row=0, column=1, sticky='e', padx=8, pady=4)
        self.cal_fg.grid(row=0, column=2, sticky='e', padx=8, pady=4)
        self.cal_bd.grid(row=1, column=1, sticky='e', padx=8, pady=4)

        cal_colors = {
            'normal': _('Normal day'),
            'weekend': _('Weekend'),
            'othermonth': _('Other month day'),
            'othermonthwe': _('Other month weekend'),
            'select': _('Selected day'),
            'headers': _('Headers'),
            'tooltip': _('Tooltip')
        }

        for i, (name, label) in enumerate(cal_colors.items()):
            bg = ColorFrame(frame_color,
                            CONFIG.get('Calendar', name + 'background'),
                            _('Background'))
            fg = ColorFrame(frame_color,
                            CONFIG.get('Calendar', name + 'foreground'),
                            _('Foreground'))
            self.cal_colors[name + 'background'] = bg
            self.cal_colors[name + 'foreground'] = fg

            ttk.Separator(frame_color, orient='horizontal').grid(row=2 + 2 * i,
                                                                 columnspan=4,
                                                                 pady=10,
                                                                 sticky='ew')
            ttk.Label(frame_color,
                      style='subtitle.TLabel',
                      wraplength=110,
                      text=label).grid(row=3 + 2 * i, column=0, sticky='w')
            bg.grid(row=3 + 2 * i, column=1, sticky='e', padx=8, pady=4)
            fg.grid(row=3 + 2 * i, column=2, sticky='e', padx=8, pady=4)

        # --- Categories
        categories = ttk.Frame(self.frames[_('Calendar')], padding=4)
        categories.columnconfigure(0, weight=1)
        categories.rowconfigure(0, weight=1)
        self.frames[_('Calendar')].add(categories, text=_('Event categories'))

        can = tk.Canvas(categories,
                        bg=self['bg'],
                        highlightthickness=0,
                        width=1,
                        relief='flat')
        scroll = AutoScrollbar(categories,
                               orient='vertical',
                               command=can.yview)
        can.configure(yscrollcommand=scroll.set)
        can.grid(row=0, column=0, sticky='ewns')
        scroll.grid(row=0, column=1, sticky='ns')

        ttk.Button(categories, image=self._im_plus,
                   command=self.add_cat).grid(row=1,
                                              column=0,
                                              sticky='w',
                                              pady=4)

        self.cat_frame = ttk.Frame(can)
        can.create_window(0, 0, anchor='nw', window=self.cat_frame)

        self.cats = {}
        for i, cat in enumerate(CONFIG.options("Categories")):
            l = ttk.Label(self.cat_frame, text=cat, style='subtitle.TLabel')
            col = CONFIG.get('Categories', cat).split(', ')
            bg = ColorFrame(self.cat_frame, col[1].strip(), _('Background'))
            fg = ColorFrame(self.cat_frame, col[0].strip(), _('Foreground'))
            b = ttk.Button(self.cat_frame,
                           image=self._im_moins,
                           padding=2,
                           command=lambda c=cat: self.del_cat(c))
            self.cats[cat] = [l, bg, fg, b]
            l.grid(row=i, column=0, sticky='e', padx=4, pady=4)
            bg.grid(row=i, column=1, sticky='e', padx=4, pady=4)
            fg.grid(row=i, column=2, sticky='e', padx=4, pady=4)
            b.grid(row=i, column=3, sticky='e', padx=4, pady=4)
        self.update_idletasks()
        can.configure(width=self.cat_frame.winfo_reqwidth())
        can.configure(scrollregion=can.bbox('all'))
        can.bind('<4>', lambda e: self._scroll(e, -1))
        can.bind('<5>', lambda e: self._scroll(e, 1))
        self.cat_frame.bind(
            '<Configure>',
            lambda e: can.configure(scrollregion=can.bbox('all')))
예제 #6
0
    def __init__(self, parent, **options):
        """ créer le Toplevel permettant de modifier les paramètres """
        Frame.__init__(self, parent, **options)

        self.onglets = Notebook(self)
        self.onglets.pack(fill='both', expand=True)
        self.im_color = PhotoImage(master=self, file=IM_COLOR)
        self.im_plus = PhotoImage(master=self, file=IM_ADD)
        self.im_moins = PhotoImage(master=self, file=IM_DEL)

        self.okfct = self.register(only_nb)

        self.style = Style(self)

        self.nb_task = len(CONFIG.options("PomodoroTasks"))

        # --- Général (temps, police et langue)
        self.general = Frame(self.onglets, padding=10)
        self.general.columnconfigure(1, weight=1)
        self.onglets.add(self.general, text=_("General"))

        # --- --- Temps
        Label(self.general, text=_("Times (min)"),
              style='title.TLabel').grid(row=0,
                                         pady=4,
                                         padx=(2, 10),
                                         sticky="w")
        self.time_frame = Frame(self.general)
        self.time_frame.grid(row=0, column=1, sticky="w", padx=4)
        Label(self.time_frame, text=_("Work")).grid(row=0, padx=4, column=0)
        self.travail = Entry(self.time_frame,
                             width=4,
                             justify='center',
                             validatecommand=(self.okfct, '%P'),
                             validate='key')
        self.travail.insert(0, CONFIG.get("Pomodoro", "work_time"))
        self.travail.grid(row=0, column=1, padx=(0, 10))
        Label(self.time_frame, text=_("Break")).grid(row=0, column=2, padx=4)
        self.pause = Entry(self.time_frame,
                           width=4,
                           justify='center',
                           validatecommand=(self.okfct, '%P'),
                           validate='key')
        self.pause.insert(0, CONFIG.get("Pomodoro", "break_time"))
        self.pause.grid(row=0, column=3, padx=(0, 10))
        Label(self.time_frame, text=_("Rest")).grid(row=0, column=4, padx=4)
        self.rest = Entry(self.time_frame,
                          width=4,
                          justify='center',
                          validatecommand=(self.okfct, '%P'),
                          validate='key')
        self.rest.insert(0, CONFIG.get("Pomodoro", "rest_time"))
        self.rest.grid(row=0, column=5)

        Separator(self.general, orient='horizontal').grid(row=1,
                                                          columnspan=2,
                                                          sticky="ew",
                                                          pady=10)

        # --- --- Police
        Label(self.general, text=_("Font"),
              style='title.TLabel').grid(row=2, sticky='nw', padx=(2, 10))
        self.font = FontFrame(self.general,
                              font=CONFIG.get('Pomodoro', 'font'),
                              sample_text="02:17")
        self.font.grid(row=2, column=1, padx=4, sticky='w')

        Separator(self.general, orient='horizontal').grid(row=3,
                                                          columnspan=2,
                                                          sticky="ew",
                                                          pady=10)

        # --- --- Opacity
        self.opacity = OpacityFrame(self.general)
        self.opacity.grid(row=5, columnspan=2, sticky='w', padx=(2, 4), pady=4)

        Separator(self.general, orient='horizontal').grid(row=6,
                                                          columnspan=2,
                                                          sticky="ew",
                                                          pady=10)

        # --- --- Son
        self.sound = SoundFrame(self.general,
                                CONFIG.get("Pomodoro", "beep"),
                                mute=CONFIG.getboolean("Pomodoro", "mute"),
                                label=_("Sound"),
                                style='title.TLabel')
        self.sound.grid(row=7, columnspan=2, sticky='ew', pady=4)

        # --- Couleurs
        self.couleurs = Frame(self.onglets, padding=10)
        self.couleurs.columnconfigure(3, weight=1)
        self.onglets.add(self.couleurs, text=_("Colors"))

        self.bg = ColorFrame(self.couleurs, CONFIG.get("Pomodoro",
                                                       "background"),
                             _("Background"))
        self.work_bg = ColorFrame(self.couleurs,
                                  CONFIG.get("Pomodoro", "work_bg"),
                                  _("Background"))
        self.break_bg = ColorFrame(self.couleurs,
                                   CONFIG.get("Pomodoro", "break_bg"),
                                   _("Background"))
        self.rest_bg = ColorFrame(self.couleurs,
                                  CONFIG.get("Pomodoro", "rest_bg"),
                                  _("Background"))
        self.fg = ColorFrame(self.couleurs, CONFIG.get("Pomodoro",
                                                       "foreground"),
                             _("Foreground"))
        self.work_fg = ColorFrame(self.couleurs,
                                  CONFIG.get("Pomodoro", "work_fg"),
                                  _("Foreground"))
        self.break_fg = ColorFrame(self.couleurs,
                                   CONFIG.get("Pomodoro", "break_fg"),
                                   _("Foreground"))
        self.rest_fg = ColorFrame(self.couleurs,
                                  CONFIG.get("Pomodoro", "rest_fg"),
                                  _("Foreground"))

        Label(self.couleurs, text=_("General"),
              style='title.TLabel').grid(row=0,
                                         column=0,
                                         pady=4,
                                         padx=(2, 10),
                                         sticky="w")
        self.bg.grid(row=0, column=1, sticky='e', padx=8, pady=4)
        self.fg.grid(row=0, column=2, sticky='e', padx=8, pady=4)
        Separator(self.couleurs, orient='horizontal').grid(row=1,
                                                           sticky="ew",
                                                           pady=10,
                                                           columnspan=4)
        Label(self.couleurs, text=_("Work"),
              style='title.TLabel').grid(row=2,
                                         column=0,
                                         pady=4,
                                         padx=(2, 10),
                                         sticky="w")
        self.work_bg.grid(row=2, column=1, sticky='e', padx=8, pady=4)
        self.work_fg.grid(row=2, column=2, sticky='e', padx=8, pady=4)
        Separator(self.couleurs, orient='horizontal').grid(row=3,
                                                           sticky="ew",
                                                           pady=10,
                                                           columnspan=4)
        Label(self.couleurs, text=_("Break"),
              style='title.TLabel').grid(row=4,
                                         column=0,
                                         pady=4,
                                         padx=(2, 10),
                                         sticky="w")
        self.break_bg.grid(row=4, column=1, sticky='e', padx=8, pady=4)
        self.break_fg.grid(row=4, column=2, sticky='e', padx=8, pady=4)
        Separator(self.couleurs, orient='horizontal').grid(row=5,
                                                           sticky="ew",
                                                           pady=10,
                                                           columnspan=4)
        Label(self.couleurs, text=_("Rest"),
              style='title.TLabel').grid(row=6,
                                         column=0,
                                         pady=4,
                                         padx=(2, 10),
                                         sticky="w")
        self.rest_bg.grid(row=6, column=1, sticky='e', padx=8, pady=4)
        self.rest_fg.grid(row=6, column=2, sticky='e', padx=8, pady=4)

        # --- Tasks
        self.stats = Frame(self.onglets, padding=10)
        self.stats.columnconfigure(0, weight=1)
        self.stats.rowconfigure(2, weight=1)
        self.onglets.add(self.stats, text=_("Tasks"))
        # graph legend
        legend_frame = Frame(self.stats)
        Label(legend_frame,
              style='title.TLabel',
              text=_('Maximum number of rows in the legend')).pack(side='left')
        self.legend_row_nb = Entry(legend_frame,
                                   width=4,
                                   justify='center',
                                   validatecommand=(self.okfct, '%P'),
                                   validate='key')
        self.legend_row_nb.insert(
            0, CONFIG.get('Pomodoro', 'legend_max_height', fallback='6'))
        self.legend_row_nb.pack(side='left', padx=4)

        # task colors
        can = Canvas(self.stats,
                     bg=self.style.lookup('TFrame', 'background'),
                     highlightthickness=0,
                     width=1,
                     relief='flat')
        scroll = AutoScrollbar(self.stats,
                               orient='vertical',
                               command=can.yview)
        can.configure(yscrollcommand=scroll.set)
        self.task_frame = Frame(can)
        can.create_window(0, 0, anchor='nw', window=self.task_frame)

        tasks = CONFIG.options("PomodoroTasks")
        tasks.sort()
        cmap = [CONFIG.get("PomodoroTasks", task) for task in tasks]
        self.tasks = {}
        self._tasks_btns = {}
        for i, (coul, task) in enumerate(zip(cmap, tasks)):
            self.tasks[task] = ColorFrame(self.task_frame, coul,
                                          task.capitalize())
            self.tasks[task].grid(row=i, column=0, sticky='e', padx=4, pady=4)
            b = Button(self.task_frame,
                       image=self.im_moins,
                       padding=2,
                       command=lambda t=task: self.del_task(t))
            b.grid(row=i, column=1, sticky='w', padx=4, pady=4)
            self._tasks_btns[task] = b
        if len(tasks) == 1:
            self._tasks_btns[tasks[0]].state(['disabled'])

        legend_frame.grid(row=0, columnspan=2, sticky='w', pady=4)
        Label(self.stats,
              text=_('Colors in the statistic graph'),
              style='title.TLabel').grid(row=1, column=0, sticky='w', pady=4)
        can.grid(row=2, column=0, sticky='ewns')
        scroll.grid(row=2, column=1, sticky='ns')
        Button(self.stats, image=self.im_plus,
               command=self.add_task).grid(row=3, column=0, sticky='w')

        self.update_idletasks()
        can.configure(width=self.task_frame.winfo_reqwidth())
        can.configure(scrollregion=can.bbox('all'))
        can.bind('<4>', lambda e: self._scroll(e, -1))
        can.bind('<5>', lambda e: self._scroll(e, 1))
        self.task_frame.bind(
            '<Configure>',
            lambda e: can.configure(scrollregion=can.bbox('all')))
예제 #7
0
    def plot_stats(self):
        tasks = [t.capitalize() for t in CONFIG.options("PomodoroTasks")]
        tasks.sort()
        coul = [CONFIG.get("PomodoroTasks", task) for task in tasks]
        stats_x = []
        stats_y = []
        demain = dt.date.today().toordinal() + 1
        min_x = demain

        # récupération des données
        no_data = True
        db = sqlite3.connect(PATH_STATS)
        cursor = db.cursor()
        for i, task in enumerate(tasks):
            name = task.lower().replace(' ', '_')
            try:
                cursor.execute('SELECT date, work FROM {}'.format(scrub(name)))
                data = cursor.fetchall()
            except sqlite3.OperationalError:
                # task was never worked
                stats_x.append([demain - 1])
                stats_y.append(np.array([0]))
            else:
                no_data = False
                x = []
                y = []
                for date, work in data:
                    x.append(date)
                    y.append(work / 60)
                min_x = min(x[0], min_x)
                stats_x.append(x)
                stats_y.append(y)
        db.close()

        # plots
        xx = np.arange(min_x, demain, dtype=float)
        yy0 = np.zeros_like(xx)  # pour empiler les stats
        if not no_data:
            for (i, task), x, y in zip(enumerate(tasks), stats_x, stats_y):
                yy = np.array([], dtype=int)
                # comble les trous par des 0
                # ainsi, les jours où une tâche n'a pas été travaillée correspondent
                # à des 0 sur le graph
                xxx = np.arange(min_x, x[0])
                yy = np.concatenate((yy, np.zeros_like(xxx, dtype=int)))
                for j in range(len(x) - 1):
                    xxx = np.arange(x[j], x[j + 1])
                    yy = np.concatenate(
                        (yy, [y[j]], np.zeros(len(xxx) - 1, dtype=int)))
                xxx = np.arange(x[-1], demain)
                yy = np.concatenate(
                    (yy, [y[-1]], np.zeros(len(xxx) - 1, dtype=int)))
                self.ax.bar(xx,
                            yy,
                            bottom=yy0,
                            width=0.8,
                            label=task,
                            color=coul[i])
                yy0 += yy
            self.ax.xaxis.set_major_formatter(DateFormatter('%x'))
            self.ax.set_xlim(min_x - 0.5, demain - 0.5)
            self.ax.set_ylabel(_("time (h)"))
            self.ax.set_xlabel(_("date"))
            self.ax.xaxis_date()
            rows = CONFIG.getint("Pomodoro", "legend_max_height", fallback=6)
            ncol = int(np.ceil(len(tasks) / rows))

            lgd = self.ax.legend(fontsize=10,
                                 ncol=ncol,
                                 columnspacing=0.7,
                                 handlelength=0.9,
                                 handletextpad=0.5)
            try:
                lgd.set_draggable(True)
            except AttributeError:
                lgd.draggable(True)
            max_y = yy0.max()
            self.ax.set_ylim(0, max_y + 0.1 * max_y)
            self.ax.tick_params('x', rotation=70)
            self.update_idletasks()
            self.fig.tight_layout()
        self.figAgg.draw()
        self.toolbar.push_current()
        self.ax.set_xlim(max(demain - 30, min_x) - 0.5, demain - 0.5)
        self.toolbar.push_current()
        self.figAgg.draw()
예제 #8
0
 def update_style(self):
     cats = {cat: CONFIG.get('Categories', cat).split(', ') for cat in CONFIG.options('Categories')}
     for cat, (fg, bg) in cats.items():
         style = 'ev_%s.%s.TLabel' % (cat, self._style_prefixe)
         self.style.configure(style, background=bg, foreground=fg)
예제 #9
0
    def __init__(self, master, event, new=False):
        Toplevel.__init__(self, master)
        self.minsize(410, 402)
        if master.winfo_ismapped():
            self.transient(master)
        self.protocol('WM_DELETE_WINDOW', self.cancel)

        self._only_nb = self.register(only_nb)

        self.event = event
        if new:
            self.title(_('New Event'))
        else:
            self.title(_('Edit Event'))
        self._new = new
        self._task = BooleanVar(self, bool(event['Task']))
        self._whole_day = BooleanVar(self, event['WholeDay'])

        # --- style
        style = Style(self)
        active_bg = style.lookup('TEntry', 'selectbackground', ('focus', ))

        self.alarms = []

        notebook = Notebook(self)
        notebook.pack(fill='both', expand=True)
        Button(self, text=_('Ok'), command=self.ok).pack(pady=(10, 6), padx=4)

        # --- event settings
        frame_event = Frame(notebook)
        notebook.add(frame_event, text=_('Event'), sticky='eswn', padding=4)
        frame_event.columnconfigure(1, weight=1)
        frame_event.rowconfigure(5, weight=1)

        self.img_moins = PhotoImage(master=self, file=IM_DEL)
        self.img_bell = PhotoImage(master=self, file=IM_BELL)
        Label(frame_event, text=_('Summary')).grid(row=0,
                                                   column=0,
                                                   padx=4,
                                                   pady=6,
                                                   sticky='e')
        Label(frame_event, text=_('Place')).grid(row=1,
                                                 column=0,
                                                 padx=4,
                                                 pady=6,
                                                 sticky='e')
        Label(frame_event, text=_('Start')).grid(row=2,
                                                 column=0,
                                                 padx=4,
                                                 pady=6,
                                                 sticky='e')
        self._end_label = Label(frame_event, text=_('End'))
        self._end_label.grid(row=3, column=0, padx=4, pady=6, sticky='e')
        frame_task = Frame(frame_event)
        frame_task.grid(row=4, column=1, padx=4, pady=6, sticky='w')
        Label(frame_event, text=_('Description')).grid(row=5,
                                                       column=0,
                                                       padx=4,
                                                       pady=6,
                                                       sticky='e')
        Label(frame_event, text=_('Category')).grid(row=6,
                                                    column=0,
                                                    padx=4,
                                                    pady=6,
                                                    sticky='e')
        Button(frame_event,
               image=self.img_bell,
               command=self.add_reminder,
               padding=0).grid(row=7, column=0, padx=4, pady=6, sticky='en')

        self.summary = Entry(frame_event, width=35)
        self.summary.insert(0, self.event['Summary'])
        self.summary.grid(row=0, column=1, padx=4, pady=6, sticky='ew')
        self.place = Entry(frame_event, width=35)
        self.place.insert(0, self.event['Place'])
        self.place.grid(row=1, column=1, padx=4, pady=6, sticky='ew')
        frame_start = Frame(frame_event)
        frame_start.grid(row=2, column=1, padx=4, pady=6, sticky='w')
        frame_end = Frame(frame_event)
        frame_end.grid(row=3, column=1, padx=4, pady=6, sticky='w')
        txt_frame = Frame(frame_event,
                          style='txt.TFrame',
                          border=1,
                          relief='sunken')
        self.desc = Text(txt_frame,
                         width=35,
                         height=4,
                         highlightthickness=0,
                         relief='flat',
                         selectbackground=active_bg)
        self.desc.insert('1.0', self.event['Description'])
        self.desc.pack(fill='both', expand=True)
        txt_frame.grid(row=5, column=1, padx=4, pady=6, sticky='ewsn')
        cats = list(CONFIG.options('Categories'))
        width = max([len(cat) for cat in cats])
        self.category = Combobox(frame_event,
                                 width=width + 2,
                                 values=cats,
                                 state='readonly')
        self.category.set(event['Category'])
        self.category.grid(row=6, column=1, padx=4, pady=6, sticky='w')
        self.frame_alarms = Frame(frame_event)
        self.frame_alarms.grid(row=7, column=1, sticky='w')

        # --- *--- task
        Checkbutton(frame_task,
                    text=_('Task'),
                    command=self._change_label,
                    variable=self._task).pack(side='left')

        self.task_progress = Combobox(frame_task,
                                      state='readonly',
                                      width=9,
                                      values=(_('Pending'), _('In Progress'),
                                              _('Completed'), _('Cancelled')))
        self.task_progress.pack(side='left', padx=(8, 4))
        self.in_progress = Combobox(
            frame_task,
            state='readonly',
            width=5,
            values=['{}%'.format(i) for i in range(0, 110, 10)])
        self.in_progress.pack(side='left', padx=4)
        if not event['Task']:
            self.task_progress.set(_('Pending'))
            self.in_progress.set('0%')
        elif '%' in event['Task']:
            self.task_progress.set(_('In Progress'))
            self.in_progress.set(event['Task'])
        else:
            self.task_progress.set(_(event['Task']))
            self.in_progress.set('0%')

        # calendar settings
        prop = {
            op: CONFIG.get('Calendar', op)
            for op in CONFIG.options('Calendar')
        }
        prop['font'] = "Liberation\ Sans 9"
        prop.update(selectforeground='white', selectbackground=active_bg)
        locale = CONFIG.get('General', 'locale')

        # --- *--- start date
        self.start_date = self.event['Start']
        self.start_entry = DateEntry(frame_start,
                                     locale=locale,
                                     width=10,
                                     justify='center',
                                     year=self.start_date.year,
                                     month=self.start_date.month,
                                     day=self.start_date.day,
                                     **prop)

        self.start_hour = Combobox(frame_start,
                                   width=3,
                                   justify='center',
                                   state='readonly',
                                   exportselection=False,
                                   values=['%02d' % i for i in range(24)])
        self.start_hour.set('%02d' % self.start_date.hour)
        self.start_min = Combobox(frame_start,
                                  width=3,
                                  justify='center',
                                  state='readonly',
                                  exportselection=False,
                                  values=['%02d' % i for i in range(0, 60, 5)])
        self.start_min.set('%02d' % self.start_date.minute)
        self.start_entry.pack(side='left', padx=(0, 18))
        self.start_hour.pack(side='left', padx=(4, 0))
        self.start_date = self.start_date.date()
        Label(frame_start, text=':').pack(side='left')
        self.start_min.pack(side='left', padx=(0, 4))
        Checkbutton(frame_start,
                    text=_("whole day"),
                    variable=self._whole_day,
                    command=self._toggle_whole_day).pack(side='left', padx=4)

        # --- *--- end date
        self.end_date = self.event['End']
        self.end_entry = DateEntry(frame_end,
                                   justify='center',
                                   locale=locale,
                                   width=10,
                                   year=self.end_date.year,
                                   month=self.end_date.month,
                                   day=self.end_date.day,
                                   **prop)

        self.end_hour = Combobox(frame_end,
                                 width=3,
                                 justify='center',
                                 state='readonly',
                                 exportselection=False,
                                 values=['%02d' % i for i in range(24)])
        self.end_hour.set('%02d' % self.end_date.hour)
        self.end_min = Combobox(frame_end,
                                width=3,
                                justify='center',
                                state='readonly',
                                exportselection=False,
                                values=['%02d' % i for i in range(0, 60, 5)])
        self.end_min.set('%02d' % self.end_date.minute)
        self.end_entry.pack(side='left', padx=(0, 18))

        self.end_hour.pack(side='left', padx=(4, 0))
        Label(frame_end, text=':').pack(side='left')
        self.end_min.pack(side='left', padx=(0, 4))
        self.end_date = self.end_date.date()

        for date in self.event['Reminders'].values():
            self.add_reminder(date)

        self._toggle_whole_day()

        # --- repetition settings
        frame_rep = Frame(notebook)
        notebook.add(frame_rep, text=_('Repetition'), padding=4, sticky='eswn')
        frame_rep.columnconfigure(0, weight=1)
        frame_rep.columnconfigure(1, weight=1)
        frame_rep.rowconfigure(1, weight=1)
        self._repeat = BooleanVar(self, bool(self.event['Repeat']))
        repeat = {
            'Frequency': 'year',
            'Limit': 'always',
            'NbTimes': 1,
            'EndDate': (datetime.now() + timedelta(days=1)).date(),
            'WeekDays': [self.start_date.isocalendar()[2] - 1]
        }
        repeat.update(self.event['Repeat'])

        self._repeat_freq = StringVar(self, repeat['Frequency'])
        Checkbutton(frame_rep,
                    text=_('Repeat event'),
                    variable=self._repeat,
                    command=self._toggle_rep).grid(row=0,
                                                   column=0,
                                                   columnspan=2,
                                                   padx=4,
                                                   pady=6,
                                                   sticky='w')
        # --- *--- Frequency
        frame_freq = LabelFrame(frame_rep, text=_('Frequency'))
        frame_freq.grid(row=1, column=0, sticky='eswn', padx=(0, 3))
        self._lfreq = Label(frame_freq, text=_('Every:'))
        self._lfreq.grid(row=0, column=0, padx=4, pady=2, sticky='e')

        self._freqs = []
        for i, val in enumerate(['Year', 'Month', 'Week']):
            r = Radiobutton(frame_freq,
                            text=_(val),
                            variable=self._repeat_freq,
                            value=val.lower(),
                            command=self._toggle_wd)
            r.grid(row=i, column=1, padx=4, pady=2, sticky='nw')
            self._freqs.append(r)

        frame_days = Frame(frame_freq)
        frame_days.grid(row=2, column=2, padx=4, pady=2, sticky='nw')
        self._week_days = []
        days = get_day_names("wide", locale=locale)
        days = [days[i] for i in range(7)]
        for day in days:
            ch = Checkbutton(frame_days, text=day)
            ch.pack(anchor='w')
            self._week_days.append(ch)

        for d in repeat['WeekDays']:
            self._week_days[int(d)].state(('selected', ))

        # --- *--- Limit
        frame_lim = LabelFrame(frame_rep, text=_('Limit'))
        frame_lim.grid(row=1, column=1, sticky='eswn', padx=(3, 0))
        frame_lim.grid(row=1, column=1, sticky='eswn', padx=(3, 0))
        self._repeat_lim = StringVar(self, repeat['Limit'])

        # always
        r1 = Radiobutton(frame_lim,
                         text=_('Always'),
                         value='always',
                         variable=self._repeat_lim,
                         command=self._toggle_lim)
        r1.grid(row=0, column=0, sticky='w')
        # until
        r2 = Radiobutton(frame_lim,
                         text=_('Until'),
                         value='until',
                         variable=self._repeat_lim,
                         command=self._toggle_lim)
        r2.grid(row=1, column=0, sticky='w')
        until_date = repeat['EndDate']
        self.until_entry = DateEntry(frame_lim,
                                     width=10,
                                     justify='center',
                                     locale=locale,
                                     year=until_date.year,
                                     month=until_date.month,
                                     day=until_date.day,
                                     **prop)

        self.until_entry.grid(row=1,
                              column=1,
                              columnspan=2,
                              sticky='w',
                              padx=(4, 10),
                              pady=2)

        # after
        r3 = Radiobutton(frame_lim,
                         text=_('After'),
                         value='after',
                         variable=self._repeat_lim,
                         command=self._toggle_lim)
        r3.grid(row=2, column=0, sticky='w')
        frame_after = Frame(frame_lim,
                            style='txt.TFrame',
                            relief='sunken',
                            border=1)
        self.s_after = Spinbox(frame_after,
                               from_=0,
                               to=100,
                               width=3,
                               justify='center',
                               relief='flat',
                               highlightthickness=0,
                               validate='key',
                               validatecommand=(self._only_nb, '%P'),
                               disabledbackground='white')
        self.s_after.pack()
        self.s_after.delete(0, 'end')
        self.s_after.insert(0, str(repeat['NbTimes']))
        frame_after.grid(row=2, column=1, padx=4, pady=2, sticky='w')
        self._llim = Label(frame_lim, text=_('times'))
        self._llim.grid(row=2, column=2, padx=0, pady=2, sticky='w')

        self._rb_lim = [r1, r2, r3]

        self._toggle_rep()
        self._change_label()

        # --- bindings
        self.bind('<Configure>')
        self.task_progress.bind('<<ComboboxSelected>>',
                                self._toggle_in_progress)
        self.start_entry.bind('<<DateEntrySelected>>', self._select_start)
        self.end_entry.bind('<<DateEntrySelected>>', self._select_end)
        self.start_hour.bind("<<ComboboxSelected>>", self._select_start_hour)
        self.start_min.bind("<<ComboboxSelected>>", self._select_start_min)
        self.end_min.bind("<<ComboboxSelected>>", self._select_end_time)
        self.end_hour.bind("<<ComboboxSelected>>", self._select_end_time)
        self.bind_class("TCombobox",
                        "<<ComboboxSelected>>",
                        self.__clear_selection,
                        add=True)

        # self.wait_visibility(self)
        # self.grab_set()
        self.summary.focus_set()
예제 #10
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)
예제 #11
0
    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])
예제 #12
0
    def __init__(self):
        Tk.__init__(self, className='Scheduler')
        logging.info('Start')
        self.protocol("WM_DELETE_WINDOW", self.hide)
        self._visible = BooleanVar(self, False)
        self.withdraw()

        self.icon_img = PhotoImage(master=self, file=ICON48)
        self.iconphoto(True, self.icon_img)

        # --- systray icon
        self.icon = TrayIcon(ICON, fallback_icon_path=ICON_FALLBACK)

        # --- menu
        self.menu_widgets = SubMenu(parent=self.icon.menu)
        self.menu_eyes = Eyes(self.icon.menu, self)
        self.icon.menu.add_checkbutton(label=_('Manager'),
                                       command=self.display_hide)
        self.icon.menu.add_cascade(label=_('Widgets'), menu=self.menu_widgets)
        self.icon.menu.add_cascade(label=_("Eyes' rest"), menu=self.menu_eyes)
        self.icon.menu.add_command(label=_('Settings'), command=self.settings)
        self.icon.menu.add_separator()
        self.icon.menu.add_command(label=_('About'),
                                   command=lambda: About(self))
        self.icon.menu.add_command(label=_('Quit'), command=self.exit)
        self.icon.bind_left_click(lambda: self.display_hide(toggle=True))

        add_trace(self._visible, 'write', self._visibility_trace)

        self.menu = Menu(self, tearoff=False)
        self.menu.add_command(label=_('Edit'), command=self._edit_menu)
        self.menu.add_command(label=_('Delete'), command=self._delete_menu)
        self.right_click_iid = None

        self.menu_task = Menu(self.menu, tearoff=False)
        self._task_var = StringVar(self)
        menu_in_progress = Menu(self.menu_task, tearoff=False)
        for i in range(0, 110, 10):
            prog = '{}%'.format(i)
            menu_in_progress.add_radiobutton(label=prog,
                                             value=prog,
                                             variable=self._task_var,
                                             command=self._set_progress)
        for state in ['Pending', 'Completed', 'Cancelled']:
            self.menu_task.add_radiobutton(label=_(state),
                                           value=state,
                                           variable=self._task_var,
                                           command=self._set_progress)
        self._img_dot = tkPhotoImage(master=self)
        self.menu_task.insert_cascade(1,
                                      menu=menu_in_progress,
                                      compound='left',
                                      label=_('In Progress'),
                                      image=self._img_dot)
        self.title('Scheduler')
        self.rowconfigure(1, weight=1)
        self.columnconfigure(0, weight=1)

        self.scheduler = BackgroundScheduler(coalesce=False,
                                             misfire_grace_time=86400)
        self.scheduler.add_jobstore('sqlalchemy',
                                    url='sqlite:///%s' % JOBSTORE)
        self.scheduler.add_jobstore('memory', alias='memo')
        # --- style
        self.style = Style(self)
        self.style.theme_use("clam")
        self.style.configure('title.TLabel', font='TkdefaultFont 10 bold')
        self.style.configure('title.TCheckbutton',
                             font='TkdefaultFont 10 bold')
        self.style.configure('subtitle.TLabel', font='TkdefaultFont 9 bold')
        self.style.configure('white.TLabel', background='white')
        self.style.configure('border.TFrame',
                             background='white',
                             border=1,
                             relief='sunken')
        self.style.configure("Treeview.Heading", font="TkDefaultFont")
        bgc = self.style.lookup("TButton", "background")
        fgc = self.style.lookup("TButton", "foreground")
        bga = self.style.lookup("TButton", "background", ("active", ))
        self.style.map('TCombobox',
                       fieldbackground=[('readonly', 'white'),
                                        ('readonly', 'focus', 'white')],
                       background=[("disabled", "active", "readonly", bgc),
                                   ("!disabled", "active", "readonly", bga)],
                       foreground=[('readonly', '!disabled', fgc),
                                   ('readonly', '!disabled', 'focus', fgc),
                                   ('readonly', 'disabled', 'gray40'),
                                   ('readonly', 'disabled', 'focus', 'gray40')
                                   ],
                       arrowcolor=[("disabled", "gray40")])
        self.style.configure('menu.TCombobox',
                             foreground=fgc,
                             background=bgc,
                             fieldbackground=bgc)
        self.style.map('menu.TCombobox',
                       fieldbackground=[('readonly', bgc),
                                        ('readonly', 'focus', bgc)],
                       background=[("disabled", "active", "readonly", bgc),
                                   ("!disabled", "active", "readonly", bga)],
                       foreground=[('readonly', '!disabled', fgc),
                                   ('readonly', '!disabled', 'focus', fgc),
                                   ('readonly', 'disabled', 'gray40'),
                                   ('readonly', 'disabled', 'focus', 'gray40')
                                   ],
                       arrowcolor=[("disabled", "gray40")])
        self.style.map('DateEntry', arrowcolor=[("disabled", "gray40")])
        self.style.configure('cal.TFrame', background='#424242')
        self.style.configure('month.TLabel',
                             background='#424242',
                             foreground='white')
        self.style.configure('R.TButton',
                             background='#424242',
                             arrowcolor='white',
                             bordercolor='#424242',
                             lightcolor='#424242',
                             darkcolor='#424242')
        self.style.configure('L.TButton',
                             background='#424242',
                             arrowcolor='white',
                             bordercolor='#424242',
                             lightcolor='#424242',
                             darkcolor='#424242')
        active_bg = self.style.lookup('TEntry', 'selectbackground',
                                      ('focus', ))
        self.style.map('R.TButton',
                       background=[('active', active_bg)],
                       bordercolor=[('active', active_bg)],
                       darkcolor=[('active', active_bg)],
                       lightcolor=[('active', active_bg)])
        self.style.map('L.TButton',
                       background=[('active', active_bg)],
                       bordercolor=[('active', active_bg)],
                       darkcolor=[('active', active_bg)],
                       lightcolor=[('active', active_bg)])
        self.style.configure('txt.TFrame', background='white')
        self.style.layout('down.TButton', [('down.TButton.downarrow', {
            'side': 'right',
            'sticky': 'ns'
        })])
        self.style.map('TRadiobutton',
                       indicatorforeground=[('disabled', 'gray40')])
        self.style.map('TCheckbutton',
                       indicatorforeground=[('disabled', 'gray40')],
                       indicatorbackground=[
                           ('pressed', '#dcdad5'),
                           ('!disabled', 'alternate', 'white'),
                           ('disabled', 'alternate', '#a0a0a0'),
                           ('disabled', '#dcdad5')
                       ])
        self.style.map('down.TButton', arrowcolor=[("disabled", "gray40")])

        self.style.map('TMenubutton',
                       arrowcolor=[('disabled',
                                    self.style.lookup('TMenubutton',
                                                      'foreground',
                                                      ['disabled']))])
        bg = self.style.lookup('TFrame', 'background', default='#ececec')
        self.configure(bg=bg)
        self.option_add('*Toplevel.background', bg)
        self.option_add('*Menu.background', bg)
        self.option_add('*Menu.tearOff', False)
        # toggle text
        self._open_image = PhotoImage(name='img_opened',
                                      file=IM_OPENED,
                                      master=self)
        self._closed_image = PhotoImage(name='img_closed',
                                        file=IM_CLOSED,
                                        master=self)
        self._open_image_sel = PhotoImage(name='img_opened_sel',
                                          file=IM_OPENED_SEL,
                                          master=self)
        self._closed_image_sel = PhotoImage(name='img_closed_sel',
                                            file=IM_CLOSED_SEL,
                                            master=self)
        self.style.element_create(
            "toggle",
            "image",
            "img_closed", ("selected", "!disabled", "img_opened"),
            ("active", "!selected", "!disabled", "img_closed_sel"),
            ("active", "selected", "!disabled", "img_opened_sel"),
            border=2,
            sticky='')
        self.style.map('Toggle', background=[])
        self.style.layout('Toggle', [('Toggle.border', {
            'children': [('Toggle.padding', {
                'children': [('Toggle.toggle', {
                    'sticky': 'nswe'
                })],
                'sticky': 'nswe'
            })],
            'sticky':
            'nswe'
        })])
        # toggle sound
        self._im_sound = PhotoImage(master=self, file=IM_SOUND)
        self._im_mute = PhotoImage(master=self, file=IM_MUTE)
        self._im_sound_dis = PhotoImage(master=self, file=IM_SOUND_DIS)
        self._im_mute_dis = PhotoImage(master=self, file=IM_MUTE_DIS)
        self.style.element_create(
            'mute',
            'image',
            self._im_sound, ('selected', '!disabled', self._im_mute),
            ('selected', 'disabled', self._im_mute_dis),
            ('!selected', 'disabled', self._im_sound_dis),
            border=2,
            sticky='')
        self.style.layout('Mute', [('Mute.border', {
            'children': [('Mute.padding', {
                'children': [('Mute.mute', {
                    'sticky': 'nswe'
                })],
                'sticky': 'nswe'
            })],
            'sticky':
            'nswe'
        })])
        self.style.configure('Mute', relief='raised')
        # widget scrollbar
        self._im_trough = {}
        self._im_slider_vert = {}
        self._im_slider_vert_prelight = {}
        self._im_slider_vert_active = {}
        self._slider_alpha = Image.open(IM_SCROLL_ALPHA)
        for widget in ['Events', 'Tasks']:
            bg = CONFIG.get(widget, 'background', fallback='gray10')
            fg = CONFIG.get(widget, 'foreground')

            widget_bg = self.winfo_rgb(bg)
            widget_fg = tuple(
                round(c * 255 / 65535) for c in self.winfo_rgb(fg))
            active_bg = active_color(*widget_bg)
            active_bg2 = active_color(*active_color(*widget_bg, 'RGB'))

            slider_vert = Image.new('RGBA', (13, 28), active_bg)
            slider_vert_active = Image.new('RGBA', (13, 28), widget_fg)
            slider_vert_prelight = Image.new('RGBA', (13, 28), active_bg2)

            self._im_trough[widget] = tkPhotoImage(width=15,
                                                   height=15,
                                                   master=self)
            self._im_trough[widget].put(" ".join(
                ["{" + " ".join([bg] * 15) + "}"] * 15))
            self._im_slider_vert_active[widget] = PhotoImage(
                slider_vert_active, master=self)
            self._im_slider_vert[widget] = PhotoImage(slider_vert, master=self)
            self._im_slider_vert_prelight[widget] = PhotoImage(
                slider_vert_prelight, master=self)
            self.style.element_create('%s.Vertical.Scrollbar.trough' % widget,
                                      'image', self._im_trough[widget])
            self.style.element_create(
                '%s.Vertical.Scrollbar.thumb' % widget,
                'image',
                self._im_slider_vert[widget],
                ('pressed', '!disabled', self._im_slider_vert_active[widget]),
                ('active', '!disabled', self._im_slider_vert_prelight[widget]),
                border=6,
                sticky='ns')
            self.style.layout(
                '%s.Vertical.TScrollbar' % widget,
                [('%s.Vertical.Scrollbar.trough' % widget, {
                    'children': [('%s.Vertical.Scrollbar.thumb' % widget, {
                        'expand': '1'
                    })],
                    'sticky':
                    'ns'
                })])
        # --- tree
        columns = {
            _('Summary'): ({
                'stretch': True,
                'width': 300
            }, lambda: self._sort_by_desc(_('Summary'), False)),
            _('Place'): ({
                'stretch': True,
                'width': 200
            }, lambda: self._sort_by_desc(_('Place'), False)),
            _('Start'): ({
                'stretch': False,
                'width': 150
            }, lambda: self._sort_by_date(_('Start'), False)),
            _('End'): ({
                'stretch': False,
                'width': 150
            }, lambda: self._sort_by_date(_("End"), False)),
            _('Category'): ({
                'stretch': False,
                'width': 100
            }, lambda: self._sort_by_desc(_('Category'), False))
        }
        self.tree = Treeview(self, show="headings", columns=list(columns))
        for label, (col_prop, cmd) in columns.items():
            self.tree.column(label, **col_prop)
            self.tree.heading(label, text=label, anchor="w", command=cmd)
        self.tree.tag_configure('0', background='#ececec')
        self.tree.tag_configure('1', background='white')
        self.tree.tag_configure('outdated', foreground='red')

        scroll = AutoScrollbar(self,
                               orient='vertical',
                               command=self.tree.yview)
        self.tree.configure(yscrollcommand=scroll.set)

        # --- toolbar
        toolbar = Frame(self)
        self.img_plus = PhotoImage(master=self, file=IM_ADD)
        Button(toolbar, image=self.img_plus, padding=1,
               command=self.add).pack(side="left", padx=4)
        Label(toolbar, text=_("Filter by")).pack(side="left", padx=4)
        # --- TODO: add filter by start date (after date)
        self.filter_col = Combobox(
            toolbar,
            state="readonly",
            # values=("",) + self.tree.cget('columns')[1:],
            values=("", _("Category")),
            exportselection=False)
        self.filter_col.pack(side="left", padx=4)
        self.filter_val = Combobox(toolbar,
                                   state="readonly",
                                   exportselection=False)
        self.filter_val.pack(side="left", padx=4)
        Button(toolbar,
               text=_('Delete All Outdated'),
               padding=1,
               command=self.delete_outdated_events).pack(side="right", padx=4)

        # --- grid
        toolbar.grid(row=0, columnspan=2, sticky='we', pady=4)
        self.tree.grid(row=1, column=0, sticky='eswn')
        scroll.grid(row=1, column=1, sticky='ns')

        # --- restore data
        data = {}
        self.events = {}
        self.nb = 0
        try:
            with open(DATA_PATH, 'rb') as file:
                dp = Unpickler(file)
                data = dp.load()
        except Exception:
            l = [
                f for f in os.listdir(os.path.dirname(BACKUP_PATH))
                if f.startswith('data.backup')
            ]
            if l:
                l.sort(key=lambda x: int(x[11:]))
                shutil.copy(os.path.join(os.path.dirname(BACKUP_PATH), l[-1]),
                            DATA_PATH)
                with open(DATA_PATH, 'rb') as file:
                    dp = Unpickler(file)
                    data = dp.load()
        self.nb = len(data)
        backup()
        now = datetime.now()
        for i, prop in enumerate(data):
            iid = str(i)
            self.events[iid] = Event(self.scheduler, iid=iid, **prop)
            self.tree.insert('',
                             'end',
                             iid,
                             values=self.events[str(i)].values())
            tags = [str(self.tree.index(iid) % 2)]
            self.tree.item(iid, tags=tags)
            if not prop['Repeat']:
                for rid, d in list(prop['Reminders'].items()):
                    if d < now:
                        del self.events[iid]['Reminders'][rid]
        self.after_id = self.after(15 * 60 * 1000, self.check_outdated)

        # --- bindings
        self.bind_class("TCombobox",
                        "<<ComboboxSelected>>",
                        self.clear_selection,
                        add=True)
        self.bind_class("TCombobox", "<Control-a>", self.select_all)
        self.bind_class("TEntry", "<Control-a>", self.select_all)
        self.tree.bind('<3>', self._post_menu)
        self.tree.bind('<1>', self._select)
        self.tree.bind('<Double-1>', self._edit_on_click)
        self.menu.bind('<FocusOut>', lambda e: self.menu.unpost())
        self.filter_col.bind("<<ComboboxSelected>>", self.update_filter_val)
        self.filter_val.bind("<<ComboboxSelected>>", self.apply_filter)

        # --- widgets
        self.widgets = {}
        prop = {
            op: CONFIG.get('Calendar', op)
            for op in CONFIG.options('Calendar')
        }
        self.widgets['Calendar'] = CalendarWidget(self,
                                                  locale=CONFIG.get(
                                                      'General', 'locale'),
                                                  **prop)
        self.widgets['Events'] = EventWidget(self)
        self.widgets['Tasks'] = TaskWidget(self)
        self.widgets['Timer'] = Timer(self)
        self.widgets['Pomodoro'] = Pomodoro(self)

        self._setup_style()

        for item, widget in self.widgets.items():
            self.menu_widgets.add_checkbutton(
                label=_(item),
                command=lambda i=item: self.display_hide_widget(i))
            self.menu_widgets.set_item_value(_(item), widget.variable.get())
            add_trace(widget.variable,
                      'write',
                      lambda *args, i=item: self._menu_widgets_trace(i))

        self.icon.loop(self)
        self.tk.eval("""
apply {name {
    set newmap {}
    foreach {opt lst} [ttk::style map $name] {
        if {($opt eq "-foreground") || ($opt eq "-background")} {
            set newlst {}
            foreach {st val} $lst {
                if {($st eq "disabled") || ($st eq "selected")} {
                    lappend newlst $st $val
                }
            }
            if {$newlst ne {}} {
                lappend newmap $opt $newlst
            }
        } else {
            lappend newmap $opt $lst
        }
    }
    ttk::style map $name {*}$newmap
}} Treeview
        """)

        # react to scheduler --update-date in command line
        signal.signal(signal.SIGUSR1, self.update_date)

        # update selected date in calendar and event list every day
        self.scheduler.add_job(self.update_date,
                               CronTrigger(hour=0, minute=0, second=1),
                               jobstore='memo')

        self.scheduler.start()