Exemple #1
0
class StructEditor(tk.Frame, Subscriber, Observable):
    """Displays and allow editing of the coordinates and points of one superstructure

    Args:
        parent (tk.Frame): widget that is the parent of the editor
        structure (model.structure.Structure): the ship superstructure that will be edited
    """
    def __init__(self, parent, structure, command_stack):
        Subscriber.__init__(self, structure)
        Observable.__init__(self)
        tk.Frame.__init__(self, parent, borderwidth=4, relief="raised")
        self._structure = structure
        self._command_stack = command_stack

        self.bind("<Button-1>", self._on_click)
        self.bind("<FocusIn>", self._on_get_focus)
        self.bind("<FocusOut>", self._on_lost_focus)

        self._tree = Treeview(self,
                              columns=["#", "X", "Y"],
                              selectmode="browse")
        #kill the icon column
        self._tree.column("#0", minwidth=0, width=0)

        style = Style()
        style.configure("Treeview.Heading", font=(None, 16))

        self._tree.column("#", minwidth=20, width=40, anchor=tk.CENTER)
        self._tree.column("X", minwidth=20, width=40, anchor=tk.CENTER)
        self._tree.column("Y", minwidth=20, width=40, anchor=tk.CENTER)
        self._tree.heading("#", text="#")
        self._tree.heading("X", text="\u21d5")
        self._tree.heading("Y", text="\u21d4")
        self._tree.grid(row=0, column=POINTS_TABLE_COL, sticky=tk.N + tk.S)

        self._tree.bind("<<TreeviewSelect>>", self._on_point_selected)
        self._tree.bind("<FocusIn>", self._on_get_focus)
        self._tree.bind("<FocusOut>", self._on_lost_focus)

        scroll = Scrollbar(self, command=self._tree.yview)
        scroll.grid(row=0, column=SCROLL_COL, sticky=tk.N + tk.S)
        scroll.bind("<FocusIn>", self._on_get_focus)

        self._tree.configure(yscrollcommand=scroll.set)

        self._index_of_sel_point = -1
        self._fill_tree()

        self._edit_zone = EditZone(self, self._structure, command_stack,
                                   self._on_get_focus)
        self._edit_zone.grid(column=EDIT_ZONE_COL, row=0, sticky=tk.N)

    def _set_selection(self, new_sel_index):
        """Set the selected point to the new_sel_index

        Gives correct focus, update, etc to the editor's widgets
        if the index is outside of the self.points, does nothing
        """
        if new_sel_index >= 0 and new_sel_index <= len(self.points) - 1:
            iid = self._tree.get_children()[new_sel_index]
            self._tree.selection_set(iid)

    def _on_click(self, *_args):
        self._tree.focus_set()

    def _on_get_focus(self, *_args):
        if self._index_of_sel_point == -1:
            self._set_selection(0)
        self.configure(relief="sunken")
        self._notify("focus", {})

    def _on_lost_focus(self, event):
        if event.widget not in self.winfo_children():
            self.configure(relief="raised")

    def _on_point_selected(self, _event):
        """called back when a point is selected in the table/treeview

        Updates the editable fields
        """
        selected_iid = self._tree.selection()
        self._index_of_sel_point = self._tree.index(selected_iid)
        self._edit_zone.set_editable_point(
            self._tree.item(selected_iid)["values"][0])
        self._notify("focus", {})

    def _fill_tree(self):
        """fills the treeview with data from the structure
        """
        self._tree.delete(*self._tree.get_children())
        for point_index, point in enumerate(self._structure.points):
            self._tree.insert(
                '',
                'end',
                values=[point_index,
                        round(point[0]),
                        round(point[1])])
            if point_index == self._index_of_sel_point:
                self._set_selection(point_index)

    def _on_notification(self, observable, event_type, event_info):
        """Rebuild the treeview on structure update
        Depending on the structure state and the operation, change the selcted point
        """
        if event_type == "add_point":
            self._index_of_sel_point = event_info["index"]
            self._fill_tree()
        else:
            if self._index_of_sel_point >= len(self._structure.points):
                self._index_of_sel_point = len(self._structure.points)
                self._edit_zone.unset_point()
            self._fill_tree()
        self._notify("focus", {})

    def update_to_coord(self, point):
        """Move the selected point to the position of the given point

        Intended to be called from click on the top view
        Args:
            point (x, y): new position in funnel coordinates
        """
        if self._index_of_sel_point != -1 and self._index_of_sel_point <= len(
                self.points) - 1:
            self._command_stack.do(
                model.structure.UpdatePoint(self._structure,
                                            self._index_of_sel_point,
                                            round(point[0]), round(point[1])))
        elif self._index_of_sel_point == len(self.points) or not self.points:
            self._command_stack.do(
                model.structure.AddPoint(self._structure,
                                         self._index_of_sel_point + 1,
                                         round(point[0]), round(point[1])))
        if self._index_of_sel_point + 1 >= len(self.points):
            self.winfo_toplevel().update()
            self._index_of_sel_point = len(self.points)
        else:
            self._set_selection(self._index_of_sel_point + 1)
            self.winfo_toplevel().update()

    @property
    def points(self):
        """Pipe throught the struct's properties"""
        return self._structure.points

    @property
    def fill(self):
        """Pipe throught the struct's properties"""
        return self._structure.fill

    @property
    def selected_index(self):
        """the index in the struct's point list of the currently selected point

        Should be -1 if none selected
        """
        return self._index_of_sel_point
Exemple #2
0
class MainWindow:
    """
    Класс главного окна
    """
    def __init__(self, top=None):
        """
        Создание всех элементов окна
        """

        top.geometry("1200x570+120+20")
        top.title("Football Analyser")
        top.configure(background=config.background_color)
        top.configure(highlightbackground=config.background_color)
        top.configure(highlightcolor="black")

        self.tree_view = Treeview(top, show="headings")

        self.first_name_label = Label(top)
        self.last_name_label = Label(top)
        self.country_label = Label(top)
        self.age_label = Label(top)
        self.plays_label = Label(top)
        self.goals_label = Label(top)

        self.first_name_entry = Entry(top)
        self.last_name_entry = Entry(top)
        self.country_entry = Entry(top)
        self.age_entry = Entry(top)
        self.plays_entry = Entry(top)
        self.goals_entry = Entry(top)

        self.entries_list = [
            self.first_name_entry, self.last_name_entry, self.country_entry,
            self.age_entry, self.plays_entry, self.goals_entry
        ]

        self.add_button = Button(top)
        self.delete_button = Button(top)
        self.modify_button = Button(top)
        self.clear_fields_button = Button(top)
        self.analyze_button = Button(top)

        self.configure_tree_view()\
            .configure_labels()\
            .configure_entries()\
            .configure_buttons()\
            .fill_on_start()

    def configure_tree_view(self):
        """
        Настройка treeview для отображения всех записей
        """

        self.tree_view.place(relx=0.008,
                             rely=0.018,
                             relheight=0.837,
                             relwidth=0.754)

        self.tree_view["columns"] = ("First name", "Last name", "Country",
                                     "Age", "Plays", "Goals")

        self.tree_view.column("First name", width=200)
        self.tree_view.column("Last name", width=200)
        self.tree_view.column("Country", width=100)
        self.tree_view.column("Age", width=100)
        self.tree_view.column("Plays", width=100)
        self.tree_view.column("Goals", width=100)

        self.tree_view.heading("First name", text="First name")
        self.tree_view.heading("Last name", text="Last name")
        self.tree_view.heading("Country", text="Country")
        self.tree_view.heading("Age", text="Age")
        self.tree_view.heading("Plays", text="Plays")
        self.tree_view.heading("Goals", text="Goals")

        self.tree_view.bind("<<TreeviewSelect>>",
                            lambda event: self.on_select_item())

        return self

    def configure_labels(self):
        """
        Настройка текста над полями ввода
        """

        self.first_name_label.place(relx=0.775, rely=0.07, height=26, width=74)
        self.first_name_label.configure(background=config.background_color)
        self.first_name_label.configure(text="First name")

        self.last_name_label.place(relx=0.775, rely=0.193, height=26, width=73)
        self.last_name_label.configure(background=config.background_color)
        self.last_name_label.configure(text="Last name")

        self.country_label.place(relx=0.775, rely=0.316, height=26, width=57)
        self.country_label.configure(background=config.background_color)
        self.country_label.configure(text="Country")

        self.age_label.place(relx=0.775, rely=0.439, height=26, width=33)
        self.age_label.configure(background=config.background_color)
        self.age_label.configure(text="Age")

        self.plays_label.place(relx=0.775, rely=0.561, height=26, width=39)
        self.plays_label.configure(background=config.background_color)
        self.plays_label.configure(text="Plays")

        self.goals_label.place(relx=0.775, rely=0.684, height=26, width=43)
        self.goals_label.configure(background=config.background_color)
        self.goals_label.configure(text="Goals")

        return self

    def configure_entries(self):
        """
        Настройка полей ввода
        """

        self.first_name_entry.place(relx=0.775,
                                    rely=0.123,
                                    height=24,
                                    relwidth=0.17)
        self.first_name_entry.configure(font=config.font)

        self.last_name_entry.place(relx=0.775,
                                   rely=0.246,
                                   height=24,
                                   relwidth=0.17)
        self.last_name_entry.configure(font=config.font)

        self.country_entry.place(relx=0.775,
                                 rely=0.368,
                                 height=24,
                                 relwidth=0.17)
        self.country_entry.configure(font=config.font)

        self.age_entry.place(relx=0.775, rely=0.491, height=24, relwidth=0.17)
        self.age_entry.configure(font=config.font)

        self.plays_entry.place(relx=0.775,
                               rely=0.614,
                               height=24,
                               relwidth=0.17)
        self.plays_entry.configure(font=config.font)

        self.goals_entry.place(relx=0.775,
                               rely=0.737,
                               height=24,
                               relwidth=0.17)
        self.goals_entry.configure(font=config.font)

        return self

    def configure_buttons(self):
        """
        Настройка кнопок
        """

        self.add_button.place(relx=0.792, rely=0.807, height=33, width=40)
        self.add_button.configure(background=config.background_color)
        self.add_button.configure(text="Add")
        self.add_button.configure(command=self.add_item)

        self.delete_button.place(relx=0.9, rely=0.807, height=33, width=56)
        self.delete_button.configure(background=config.background_color)
        self.delete_button.configure(text="Delete")
        self.delete_button.configure(command=self.delete_item)

        self.modify_button.place(relx=0.842, rely=0.807, height=33, width=59)
        self.modify_button.configure(background=config.background_color)
        self.modify_button.configure(text="Modify")
        self.modify_button.configure(command=self.modify_item)

        self.clear_fields_button.place(relx=0.8,
                                       rely=0.895,
                                       height=33,
                                       width=166)
        self.clear_fields_button.configure(background=config.background_color)
        self.clear_fields_button.configure(text="Clear fields")
        self.clear_fields_button.configure(command=self.clear_all_entries)

        self.analyze_button.place(relx=0.225, rely=0.877, height=53, width=336)
        self.analyze_button.configure(background=config.background_color)
        self.analyze_button.configure(text="Analyze")
        self.analyze_button.configure(font="-size 18")
        self.analyze_button.configure(command=self.analyze)

        return self

    def fill_on_start(self):
        """
        Заполнение treeview записями из базы данных
        """

        for row in db.get_records():
            self.tree_view.insert("", tk.END, values=row)
        return self

    def on_select_item(self):
        """
        Отображение выбранной записи в полях ввода для редактирования
        """

        values = self.tree_view.item(self.tree_view.focus())["values"]
        for entry, val in zip(self.entries_list, values):
            entry.delete(0, tk.END)
            entry.insert(0, val)

    def clear_all_entries(self):
        """
        Очистка всех полей ввода
        """

        for entry in self.entries_list:
            entry.delete(0, tk.END)

    def delete_item(self):
        """
        Удаление записи
        """

        item = self.tree_view.focus()
        db.delete_record(self.tree_view.index(item))
        self.tree_view.delete(item)
        self.clear_all_entries()

    def add_item(self):
        """
        Добавление записи
        """

        try:
            first_name = validator.validate_text(self.first_name_entry.get())
            last_name = validator.validate_text(self.last_name_entry.get())
            country = validator.validate_text(self.country_entry.get())
            age = validator.validate_number(self.age_entry.get())
            plays = validator.validate_number(self.plays_entry.get())
            goals = validator.validate_number(self.goals_entry.get())

            db.insert_record({
                "first_name": first_name,
                "last_name": last_name,
                "country": country,
                "age": age,
                "plays": plays,
                "goals": goals
            })
            self.tree_view.insert("",
                                  tk.END,
                                  values=(first_name, last_name, country, age,
                                          plays, goals))

        except ValueError:
            messagebox.showerror("Invalid input",
                                 "Input are not valid string or number")

        self.on_select_item()

    def modify_item(self):
        """
        Изменение записи
        """

        try:
            item = self.tree_view.focus()
            index = self.tree_view.index(item)

            first_name = validator.validate_text(self.first_name_entry.get())
            last_name = validator.validate_text(self.last_name_entry.get())
            country = validator.validate_text(self.country_entry.get())
            age = validator.validate_number(self.age_entry.get())
            plays = validator.validate_number(self.plays_entry.get())
            goals = validator.validate_number(self.goals_entry.get())

            db.update_record(
                index, (first_name, last_name, country, age, plays, goals))
            self.tree_view.item(item,
                                values=(first_name, last_name, country, age,
                                        plays, goals))

        except ValueError:
            messagebox.showerror("Invalid input",
                                 "Input are not valid string or number")

        self.on_select_item()

    def analyze(self):
        """
        Вызов анализа
        """

        analyse.full_analysis()
        messagebox.showinfo("Done",
                            "Файлы отчета сохранены в output и graphics")
Exemple #3
0
class StoreView(BaseView):

    ########
    # Initializes and places all GUI elements.
    def _create_widgets(self):
        # left frame
        self._leftFrame = Frame(self)
        self._imgLabel = Label(self._leftFrame)
        self._locLabel = Label(self._leftFrame,
                               text="Location:",
                               font=BaseView.NORMAL_FONT)
        self._locPreview = SimpleLocationView(self._leftFrame)

        # right frame
        self._rightFrame = Frame(self)
        self._nameLabel = Label(self._rightFrame,
                                compound=LEFT,
                                text="Name",
                                font=BaseView.LARGE_FONT)
        self._descLabel = Label(self._rightFrame,
                                text="Description:",
                                font=BaseView.NORMAL_FONT)
        self._descFrame = Frame(self._rightFrame)
        self._descText = Text(self._descFrame,
                              width=50,
                              height=4,
                              state=DISABLED,
                              wrap=WORD,
                              font=BaseView.NORMAL_FONT)
        self._descScroll = Scrollbar(self._descFrame,
                                     command=self._descText.yview)
        self._descText.config(yscrollcommand=self._descScroll.set)
        self._notesLabel = Label(self._rightFrame,
                                 text="Notes:",
                                 font=BaseView.NORMAL_FONT)
        self._notesFrame = Frame(self._rightFrame)
        self._notesText = Text(self._notesFrame,
                               width=50,
                               height=4,
                               state=DISABLED,
                               wrap=WORD,
                               font=BaseView.NORMAL_FONT)
        self._notesScroll = Scrollbar(self._notesFrame,
                                      command=self._notesText.yview)
        self._notesText.config(yscrollcommand=self._notesScroll.set)

        # bottom
        self._sep1 = Separator(self, orient="horizontal")
        self._invLabel = Label(self,
                               text="Inventory:",
                               font=BaseView.NORMAL_FONT)
        self._invFrame = Frame(self)
        self._inventory = Treeview(
            self._invFrame,
            columns=["type", "price", "qty", "stockDays"],
            selectmode="browse",
            height=15)
        self._inventory.heading("#0", text="Item", anchor=N + W)
        self._inventory.column("#0", width=300, anchor=N + W, stretch=True)
        self._inventory.heading("type", text="Type", anchor=N + W)
        self._inventory.column("type", width=100, anchor=N + W)
        self._inventory.heading("price", text="Price", anchor=N + W)
        self._inventory.column("price", width=60, anchor=N + W)
        self._inventory.heading("qty", text="Qty", anchor=N + W)
        self._inventory.column("qty", width=40, anchor=N + W)
        self._inventory.heading("stockDays", text="Stock Days", anchor=N + W)
        self._inventory.column("stockDays",
                               width=200,
                               anchor=N + W,
                               stretch=True)
        self._invScroll = Scrollbar(self._invFrame,
                                    command=self._inventory.yview)
        self._inventory.config(yscrollcommand=self._invScroll.set)

        # placement: scrollbars
        self._descText.grid(row=0, column=0, sticky=N + W + E + S)
        self._descScroll.grid(row=0, column=1, sticky=N + S)
        self._notesText.grid(row=0, column=0, sticky=N + W + E + S)
        self._notesScroll.grid(row=0, column=1, sticky=N + S)
        self._inventory.grid(row=0, column=0, sticky=N + W + E + S)
        self._invScroll.grid(row=0, column=1, sticky=N + S)

        # placement: left frame
        self._imgLabel.grid(row=0, column=0, sticky=N + W + E + S)
        self._locLabel.grid(row=1, column=0, sticky=N + W)
        self._locPreview.grid(row=2, column=0, sticky=W + E)

        # placement: right frame
        self._nameLabel.grid(row=0, column=0, sticky=W)
        self._descLabel.grid(row=1, column=0, sticky=W)
        self._descFrame.grid(row=2, column=0, sticky=W)
        self._notesLabel.grid(row=3, column=0, sticky=W)
        self._notesFrame.grid(row=4, column=0, sticky=W)

        # bottom
        self._sep1.grid(row=1, column=0, columnspan=2, sticky=W + E)
        self._invLabel.grid(row=2, column=0, columnspan=2, sticky=N + W)
        self._invFrame.grid(row=3, column=0, columnspan=2, sticky=W + E)

        self._leftFrame.grid(row=0, column=0, sticky=N + W)
        self._rightFrame.grid(row=0, column=1, sticky=N + W)

    ########
    # Add callbacks for all GUI element events and Tkinter variables.
    def _bind_widgets(self):

        self._locPreview.bind("<Double-Button-1>", self._open_loc)
        self._inventory.bind("<Double-Button-1>",
                             self._inventory_on_double_click)

    ########
    # Populates all GUI elements with new data.
    def populate(self, data):
        self._data = data
        if data == None:  # null check
            self.set_defaults()
            return
        for k, v in data.items():
            if k == "name":
                # non-null
                self._nameLabel.config(text=v)
            elif k == "img":
                if v == None:  # null check
                    v = BaseView.DEFAULT_IMG
                utility.update_img(self._imgLabel, v, maxSize=300)
            elif k == "location":
                # non-null
                self._locPreview.populate(v)
            elif k == "description":
                if v == None:  # null check
                    v = BaseView.EMPTY_STR
                utility.update_text(self._descText, v)
            elif k == "notes":
                if v == None:  # null check
                    v = BaseView.EMPTY_STR
                utility.update_text(self._notesText, v)
            elif k == "sells":
                self._update_inventory()

    ########
    # Resets GUI elements to default values.
    def set_defaults(self):
        utility.update_img(self._imgLabel, BaseView.DEFAULT_IMG, maxSize=300)
        utility.update_text(self._descText, BaseView.EMPTY_STR)
        utility.update_text(self._notesText, BaseView.EMPTY_STR)

    ########
    # Populates inventory with correct values
    def _update_inventory(self):
        allItems = self._inventory.get_children()
        for item in allItems:
            self._inventory.delete(item)
        self._imgs = []
        if self._data == None:
            return
        for entry in self._data["sells"]:
            img = entry["item"]["img"]
            if img == None:
                img = BaseView.DEFAULT_IMG
            img = utility.get_img(img, maxSize=20)
            name = entry["item"]["name"]
            fields = [
                BaseView.TYPE_MAP[entry["item"]["type"]], entry["price"],
                entry["qty"], entry["stockDays"]
            ]
            for i in range(len(fields)):
                if fields[i] == None:
                    fields[i] = BaseView.EMPTY_STR
            self._imgs.append(img)
            self._inventory.insert("",
                                   END,
                                   image=img,
                                   text=name,
                                   values=fields)

    ########
    # Opens location view through refBook.
    def _open_loc(self, *args, **kwargs):
        if self._refBook == None or self._data == None:
            return
        self._refBook.show_location(self._data["location"]["name"])

    ########
    # Callback for double clicking in inventory treeview.
    def _inventory_on_double_click(self, *args, **kwargs):
        if self._refBook == None or self._data == None or len(
                self._inventory.selection()) == 0:
            return
        self._refBook.show_item(self._data["sells"][self._inventory.index(
            self._inventory.selection())]["item"]["name"])
class App(Frame):
    """
    Main App with treeview
    """
    def __init__(self, parent=None, *args, **kwargs):
        Frame.__init__(self, parent)
        self.parent = parent

        # Create scrollbar
        scrollbar = Scrollbar(self)
        scrollbar.pack(side=RIGHT, fill=Y)

        # Create Treeview
        self.tree = Treeview(self,
                             columns=('#0', '#1', '#2'),
                             selectmode="browse",
                             yscrollcommand=scrollbar.set)
        self.tree.pack(expand=TRUE, fill=X)
        scrollbar.config(command=self.tree)

        # Setup column heading
        self.tree.heading('#0', text='Thumbnail', anchor='center')
        self.tree.heading('#1', text='Name', anchor='center')
        self.tree.heading('#2', text='Origin Text', anchor='nw')

        # Setup column
        self.tree.column('#0', stretch=NO)
        self.tree.column('#1', stretch=NO)
        self.tree.column('#2', anchor='nw', minwidth=300)

        # Bind event
        self.tree.bind('<Double-1>', self.show_menu)

        # Variables init
        self.video_path_list = []
        self.video_texts_list = []
        self.video_name_list = []
        self.video_images = []
        self.load_video_info()

    def load_video_info(self):
        """
        Load video text information
        """
        self.video_path_list = get_video_list()
        self.video_texts_list = []
        self.video_name_list = []
        for path in self.video_path_list:
            self.video_texts_list.append(
                get_video_texts(get_video_info_filename(path)))
            self.video_name_list.append(
                get_video_names(get_video_meta_filename(path)))

        self.video_images = []
        images = [
            Image.open(get_cover_image_filename(path))
            for path in self.video_path_list
        ]
        for img in images:
            img = img.resize((160, 90), Image.ANTIALIAS)
            self.video_images.append(ImageTk.PhotoImage(img))
        for i in range(0, len(self.video_images)):
            txt = '\n'.join(self.video_texts_list[i][:5])
            self.tree.insert('',
                             'end',
                             image=self.video_images[i],
                             value=(self.video_name_list[i], txt),
                             tags='video')

    def refresh_treeview(self):
        """
        Reload all treeview items after translate or replace image complete
        """
        self.tree.delete(*self.tree.get_children())
        self.update()
        self.load_video_info()
        self.update()

    def show_menu(self, event):
        """
        Show menu when double click on treeview item
        :param event: Treeview double click event objject
        """
        menu = tkinter.Menu(self, tearoff=0)
        menu.add_command(label='Translate to TW', command=self.do_translate)
        menu.add_command(label='Export SRT', command=self.export_srt)
        menu.add_command(label='Replace cover image',
                         command=self.replace_cover_image)

        try:
            menu.tk_popup(event.x_root, event.y_root)
        finally:
            menu.grab_release()

    def do_translate(self):
        """
        Translate selected video's texts from CN to TW
        """
        index = self.tree.index(self.tree.focus())
        texts = self.video_texts_list[index]
        tw_texts = []

        progress_var = tkinter.DoubleVar()
        progress_step = float(100.0 / len(texts))
        progress = 0

        popup = tkinter.Toplevel(self.parent)
        popup.geometry('300x50')
        popup.transient(self.parent)
        popup.grab_set()
        popup.protocol('WM_DELETE_WINDOW', disable_popup_close)

        tkinter.Label(popup, text="Translating...").grid(row=0, column=0)
        progress_bar = ttk.Progressbar(popup,
                                       variable=progress_var,
                                       maximum=100,
                                       length=300)
        progress_bar.grid(row=1, column=0)
        popup.pack_slaves()

        try:
            for i in range(0, len(texts)):
                popup.update()
                tw_texts.append(do_single_translate(texts[i]))
                progress += progress_step
                progress_var.set(progress)

        except:
            messagebox.showerror(title='Error',
                                 message='Translate fail! Please try again')

        set_video_texts(tw_texts,
                        get_video_info_filename(self.video_path_list[index]))

        popup.destroy()
        messagebox.showinfo(title='Success', message='Translate success!')
        self.refresh_treeview()

    def export_srt(self):
        """
        Export selected video's subtitle to SRT file
        """
        index = self.tree.index(self.tree.focus())
        filename = simpledialog.askstring("File name",
                                          "What is SRT file name?",
                                          parent=self)

        f = open(get_video_info_filename(self.video_path_list[index]),
                 encoding='utf-8')
        txt = f.read()
        f.close()

        subtitle_txt = analyseFile(txt)
        subtitle_srt = createSrt(subtitle_txt)

        f = open(get_export_srt_filename(filename), "w", encoding='utf-8')
        f.write(subtitle_srt)
        f.close()

        messagebox.showinfo(
            title='Export',
            message='Export srt file success!\nPlease check it on the Desktop.'
        )

    def replace_cover_image(self):
        """
        Replace selected video cover image
        """
        index = self.tree.index(self.tree.focus())
        img_file = filedialog.askopenfilename(
            parent=self,
            initialdir=os.getcwd(),
            title="Please select a JPEG file:",
            filetypes=[('JPEG', ".jpg")])
        cover_image_filename = get_cover_image_filename(
            self.video_path_list[index])
        shutil.copy(img_file, cover_image_filename)

        messagebox.showinfo(title='Replace',
                            message='Replace cover image success!')
        self.refresh_treeview()
class RefBook(Frame):

    ########
    # Opens database from provided arguments and creates and populates widgets.
    def __init__(self, master, *args, **kwargs):
        super().__init__(master)

        self._dbm = DatabaseManager(*args, **kwargs)
        self._openItems = {}
        self._openStores = {}
        self._openAttacks = {}
        self._openCreatures = {}
        self._openLocations = {}

        self._create_widgets()
        self._populate_items()
        self._populate_stores()
        self._populate_attacks()
        self._populate_creatures()
        self._populate_locations()

    ########
    # Initializes and places all GUI elements.
    def _create_widgets(self):
        self._nb = Notebook(self)

        # items
        self._itemTab = Frame(self._nb)
        self._itemTree = Treeview(self._itemTab,
                                  columns=["type", "value"],
                                  selectmode="browse",
                                  height=25)
        self._itemTree.heading("#0", text="Item", anchor=N + W)
        self._itemTree.column("#0", width=300, anchor=N + W, stretch=True)
        self._itemTree.heading("type", text="Type", anchor=N + W)
        self._itemTree.column("type", width=100, anchor=N + W)
        self._itemTree.heading("value", text="Value", anchor=N + W)
        self._itemTree.column("value", width=60, anchor=N + W)
        self._itemScroll = Scrollbar(self._itemTab,
                                     command=self._itemTree.yview)
        self._itemTree.config(yscrollcommand=self._itemScroll.set)

        self._itemTree.grid(row=0, column=0, sticky=N + W + S + E)
        self._itemScroll.grid(row=0, column=1, sticky=N + S)
        self._nb.add(self._itemTab, text="Items")

        # stores
        self._storeTab = Frame(self._nb)
        self._storeTree = Treeview(self._storeTab,
                                   columns=["location"],
                                   selectmode="browse",
                                   height=25)
        self._storeTree.heading("#0", text="Store", anchor=N + W)
        self._storeTree.column("#0", width=330, anchor=N + W, stretch=True)
        self._storeTree.heading("location", text="Location", anchor=N + W)
        self._storeTree.column("location", width=130, anchor=N + W)
        self._storeScroll = Scrollbar(self._storeTab,
                                      command=self._storeTree.yview)
        self._storeTree.config(yscrollcommand=self._storeScroll.set)

        self._storeTree.grid(row=0, column=0, sticky=N + W + S + E)
        self._storeScroll.grid(row=0, column=1, sticky=N + S)
        self._nb.add(self._storeTab, text="Stores")

        # attacks
        self._attackTab = Frame(self._nb)
        self._attackTree = Treeview(self._attackTab,
                                    columns=["id", "spell"],
                                    selectmode="browse",
                                    height=25)
        self._attackTree.heading("#0", text="Attacks", anchor=N + W)
        self._attackTree.column("#0", width=370, anchor=N + W, stretch=True)
        self._attackTree.heading("id", text="ID", anchor=N + W)
        self._attackTree.column("id", width=40, anchor=N + E)
        self._attackTree.heading("spell", text="Spell", anchor=N + W)
        self._attackTree.column("spell", width=50, anchor=CENTER)
        self._attackScroll = Scrollbar(self._attackTab,
                                       command=self._attackTree.yview)
        self._attackTree.config(yscrollcommand=self._attackScroll.set)

        self._attackTree.grid(row=0, column=0, sticky=N + W + S + E)
        self._attackScroll.grid(row=0, column=1, sticky=N + S)
        self._nb.add(self._attackTab, text="Attacks")

        # creatures
        self._creatureTab = Frame(self._nb)
        self._creatureTree = Treeview(self._creatureTab,
                                      columns=["hd"],
                                      selectmode="browse",
                                      height=25)
        self._creatureTree.heading("#0", text="Creatures", anchor=N + W)
        self._creatureTree.column("#0", width=420, anchor=N + W, stretch=True)
        self._creatureTree.heading("hd", text="HD", anchor=N + W)
        self._creatureTree.column("hd", width=40, anchor=N + E)
        self._creatureScroll = Scrollbar(self._creatureTab,
                                         command=self._creatureTree.yview)
        self._creatureTree.config(yscrollcommand=self._creatureScroll.set)

        self._creatureTree.grid(row=0, column=0, sticky=N + W + S + E)
        self._creatureScroll.grid(row=0, column=1, sticky=N + S)
        self._nb.add(self._creatureTab, text="Creatures")

        # locations
        self._locationTab = Frame(self._nb)
        self._locationTree = Treeview(self._locationTab,
                                      selectmode="browse",
                                      height=25)
        self._locationTree.heading("#0", text="Locations", anchor=N + W)
        self._locationTree.column("#0", width=460, anchor=N + W, stretch=True)
        self._locationScroll = Scrollbar(self._locationTab,
                                         command=self._locationTree.yview)
        self._locationTree.config(yscrollcommand=self._locationScroll.set)

        self._locationTree.grid(row=0, column=0, sticky=N + W + S + E)
        self._locationScroll.grid(row=0, column=1, sticky=N + S)
        self._nb.add(self._locationTab, text="Locations")

        self._nb.grid(row=0, column=0, sticky=N + W + S + E)

        # bindings
        self._itemTree.bind("<Double-Button-1>", self._item_on_double_click)
        self._storeTree.bind("<Double-Button-1>", self._store_on_double_click)
        self._attackTree.bind("<Double-Button-1>",
                              self._attack_on_double_click)
        self._creatureTree.bind("<Double-Button-1>",
                                self._creature_on_double_click)
        self._locationTree.bind("<Double-Button-1>",
                                self._location_on_double_click)

    ########
    # Populates item list.
    def _populate_items(self):
        self._itemList = self._dbm.get_item_list()

        allItems = self._itemTree.get_children()
        for item in allItems:
            self._itemTree.delete(item)
        self._itemImgs = []
        if self._dbm == None:
            return
        for entry in self._itemList:
            img = entry["img"]
            if img == None:
                img = BaseView.DEFAULT_IMG
            img = utility.get_img(img, maxSize=20)
            name = entry["name"]
            fields = [BaseView.TYPE_MAP[entry["type"]], entry["value"]]
            for i in range(len(fields)):
                if fields[i] == None:
                    fields[i] = BaseView.EMPTY_STR
            self._itemImgs.append(img)
            self._itemTree.insert("", END, image=img, text=name, values=fields)

    ########
    # Populates store list.
    def _populate_stores(self):
        self._storeList = self._dbm.get_store_list()

        allStores = self._storeTree.get_children()
        for store in allStores:
            self._storeTree.delete(store)
        self._storeImgs = []
        if self._dbm == None:
            return
        for entry in self._storeList:
            img = entry["img"]
            if img == None:
                img = BaseView.DEFAULT_IMG
            img = utility.get_img(img, maxSize=20)
            name = entry["name"]
            fields = [entry["location"]]
            for i in range(len(fields)):
                if fields[i] == None:
                    fields[i] = BaseView.EMPTY_STR
            self._storeImgs.append(img)
            self._storeTree.insert("",
                                   END,
                                   image=img,
                                   text=name,
                                   values=fields)

    ########
    # Populates attack list.
    def _populate_attacks(self):
        self._attackList = self._dbm.get_attack_list()

        allAttacks = self._attackTree.get_children()
        for attack in allAttacks:
            self._attackTree.delete(attack)
        self._attackImgs = []
        if self._dbm == None:
            return
        for entry in self._attackList:
            img = entry["img"]
            if img == None:
                img = BaseView.DEFAULT_IMG
            img = utility.get_img(img, maxSize=20)
            name = entry["name"]
            fields = [entry["id"], "✔" if entry["isSpell"] else ""]
            for i in range(len(fields)):
                if fields[i] == None:
                    fields[i] = BaseView.EMPTY_STR
            self._attackImgs.append(img)
            self._attackTree.insert("",
                                    END,
                                    image=img,
                                    text=name,
                                    values=fields)

    ########
    # Populates creature list.
    def _populate_creatures(self):
        self._creatureList = self._dbm.get_creature_list()

        allCreatures = self._creatureTree.get_children()
        for creature in allCreatures:
            self._creatureTree.delete(creature)
        self._creatureImgs = []
        if self._dbm == None:
            return
        for entry in self._creatureList:
            img = entry["img"]
            if img == None:
                img = BaseView.DEFAULT_IMG
            img = utility.get_img(img, maxSize=20)
            name = entry["name"]
            fields = [str(entry["hd"])]
            for i in range(len(fields)):
                if fields[i] == None:
                    fields[i] = BaseView.EMPTY_STR
            self._creatureImgs.append(img)
            self._creatureTree.insert("",
                                      END,
                                      image=img,
                                      text=name,
                                      values=fields)

    ########
    # Populates location list.
    def _populate_locations(self):
        self._locationList = self._dbm.get_location_list()

        allLocations = self._locationTree.get_children()
        for location in allLocations:
            self._locationTree.delete(location)
        self._locationImgs = []
        if self._dbm == None:
            return
        for entry in self._locationList:
            img = entry["img"]
            if img == None:
                img = BaseView.DEFAULT_IMG
            img = utility.get_img(img, maxSize=20)
            name = entry["name"]
            self._locationImgs.append(img)
            self._locationTree.insert("", END, image=img, text=name)

    ########
    # Callback for double clicking in item treeview.
    def _item_on_double_click(self, *args, **kwargs):
        if len(self._itemTree.selection()) == 0:
            return
        self.show_item(self._itemList[self._itemTree.index(
            self._itemTree.selection())]["name"])

    ########
    # Callback for double clicking in store treeview.
    def _store_on_double_click(self, *args, **kwargs):
        if len(self._storeTree.selection()) == 0:
            return
        curr = self._storeList[self._storeTree.index(
            self._storeTree.selection())]
        self.show_store(curr["name"], curr["location"])

    ########
    # Callback for double clicking in attack treeview.
    def _attack_on_double_click(self, *args, **kwargs):
        if len(self._attackTree.selection()) == 0:
            return
        self.show_attack(self._attackList[self._attackTree.index(
            self._attackTree.selection())]["id"])

    ########
    # Callback for double clicking in creature treeview.
    def _creature_on_double_click(self, *args, **kwargs):
        if len(self._creatureTree.selection()) == 0:
            return
        self.show_creature(self._creatureList[self._creatureTree.index(
            self._creatureTree.selection())]["name"])

    ########
    # Callback for double clicking in location treeview.
    def _location_on_double_click(self, *args, **kwargs):
        if len(self._locationTree.selection()) == 0:
            return
        self.show_location(self._locationList[self._locationTree.index(
            self._locationTree.selection())]["name"])

    ########
    # Shows a full item in a new window.
    def show_item(self, name):
        if name in self._openItems.keys():
            # TODO: make this cleaner
            try:
                self._openItems[name].lift()
                self._openItems[name].focus_set()
                return
            except TclError:
                pass  # fallthrough to make new window
        newWindow = Toplevel(self)
        itemView = ItemView(newWindow, self._dbm.get_item(name), refBook=self)
        itemView.grid(row=0, column=0, sticky=N + W + E + S)
        newWindow.title(name)
        newWindow.lift()
        newWindow.focus_set()
        self._openItems[name] = newWindow

    ########
    # Shows a full store in a new window.
    def show_store(self, name, location):
        key = (name, location)
        if key in self._openStores.keys():
            # TODO: make this cleaner
            try:
                self._openStores[key].lift()
                self._openStores[key].focus_set()
                return
            except TclError:
                pass  # fallthrough to make new window
        newWindow = Toplevel(self)
        storeView = StoreView(newWindow,
                              self._dbm.get_store(name, location),
                              refBook=self)
        storeView.grid(row=0, column=0, sticky=N + W + E + S)
        newWindow.title(name)
        newWindow.lift()
        newWindow.focus_set()
        self._openStores[key] = newWindow

    ########
    # Shows a full attack in a new window.
    def show_attack(self, attackId):
        if attackId in self._openAttacks.keys():
            # TODO: make this cleaner
            try:
                self._openAttacks[attackId].lift()
                self._openAttacks[attackId].focus_set()
                return
            except TclError:
                pass  # fallthrough to make new window
        attack = self._dbm.get_attack(attackId)
        newWindow = Toplevel(self)
        attackView = AttackView(newWindow,
                                self._dbm.get_attack(attackId),
                                refBook=self)
        attackView.grid(row=0, column=0, sticky=N + W + E + S)
        newWindow.title("{} ({})".format(attack["name"], attack["id"]))
        newWindow.lift()
        newWindow.focus_set()
        self._openAttacks[attackId] = newWindow

    ########
    # Shows a full creature in a new window.
    def show_creature(self, name):
        if name in self._openCreatures.keys():
            # TODO: make this cleaner
            try:
                self._openCreatures[name].lift()
                self._openCreatures[name].focus_set()
                return
            except TclError:
                pass  # fallthrough to make new window
        newWindow = Toplevel(self)
        creatureView = CreatureView(newWindow,
                                    self._dbm.get_creature(name),
                                    refBook=self)
        creatureView.grid(row=0, column=0, sticky=N + W + E + S)
        newWindow.title(name)
        newWindow.lift()
        newWindow.focus_set()
        self._openCreatures[name] = newWindow

    ########
    # Shows a full location in a new window.
    def show_location(self, name):
        if name in self._openLocations.keys():
            # TODO: make this cleaner
            try:
                self._openLocations[name].lift()
                self._openLocations[name].focus_set()
                return
            except TclError:
                pass  # fallthrough to make new window
        newWindow = Toplevel(self)
        locationView = LocationView(newWindow,
                                    self._dbm.get_location(name),
                                    refBook=self)
        locationView.grid(row=0, column=0, sticky=N + W + E + S)
        newWindow.title(name)
        newWindow.lift()
        newWindow.focus_set()
        self._openLocations[name] = newWindow
class ElementListWidget(Frame):
	def __init__(self, parent, label, columns, showError):
		Frame.__init__(self, parent)
		
		self.showError = showError
		
		self.columnconfigure(0, weight = 1)
		self.rowconfigure(1, weight = 1)
		
		
		# Название таблицы
		self.titleLabel = Label(self, text = label)
		self.titleLabel.grid(column = 0, row = 0, sticky = W + E)
		
		
		# Таблица значений
		columns = ("Метка", "№") + columns
		self.tree = Treeview(self, columns = columns, displaycolumns = columns,
							 selectmode = "browse")
		self.tree.grid(column = 0, row = 1, sticky = W + N + E + S)
		
		# Настраиваем внешний вид таблицы (первые колонки)
		self.tree.column("#0", width = 0, stretch = 0)	# Прячем колонку с иконкой
		
		self.tree.column( columns[0], anchor = W, width = 150)
		self.tree.heading(columns[0], anchor = W, text = columns[0])
		
		self.tree.column( columns[1], anchor = E, width = 80)
		self.tree.heading(columns[1], anchor = E, text = columns[1])
		
		self.tree.bind("<<TreeviewSelect>>", self.onSelectionChanged)
		
		
		# Панель с кнопками
		self.buttonPanel = Frame(self)
		self.buttonPanel.grid(column = 0, row = 2, sticky = W + E)
		
		self.buttonPanel.columnconfigure(0, weight = 1)
		self.buttonPanel.columnconfigure(3, minsize = emptySpaceSize, weight = 0)
		self.buttonPanel.columnconfigure(6, minsize = emptySpaceSize, weight = 0)
		self.buttonPanel.columnconfigure(9, weight = 1)
		
		# Кнопки добавления/удаления элемента
		self.buttonAdd = Button(self.buttonPanel, text = "+", width = 3,
								command = self.onButtonAddClicked)
		self.buttonAdd.grid(column = 1, row = 0)
		
		self.buttonRemove = Button(self.buttonPanel, text = "-", width = 3, state = DISABLED,
								   command = self.onButtonRemoveClicked)
		self.buttonRemove.grid(column = 2, row = 0)
		
		# Кнопки перемещения элемента
		self.buttonUp = Button(self.buttonPanel, text = "↑", width = 3, state = DISABLED,
							   command = self.onButtonUpClicked)
		self.buttonUp.grid(column = 4, row = 0)
		
		self.buttonDown = Button(self.buttonPanel, text = "↓", width = 3, state = DISABLED,
								 command = self.onButtonDownClicked)
		self.buttonDown.grid(column = 5, row = 0)
		
		# Кнопки применить/отменить (для выбранного элемента)
		self.buttonCancel = Button(self.buttonPanel, text = "✗", width = 3,
								   command = self.updateSelectedFrame)
		self.buttonCancel.grid(column = 7, row = 0)
		
		self.buttonApply = Button(self.buttonPanel, text = "✓", width = 3,
								  command = self.onButtonApplyClicked)
		self.buttonApply.grid(column = 8, row = 0)
		
		
		# Редактирование выделенного элемента
		self.i     = StringVar()
		self.label = (StringVar(), StringVar())
		
		self.selectedFrame = Frame(self)
		self.selectedFrame.grid(column = 0, row = 3, sticky = W + E)
		
		# Номер
		Label(self.selectedFrame, text = "№:") \
			.grid(column = 0, row = 0)
		Label(self.selectedFrame, textvariable = self.i, width = 3, justify = RIGHT) \
			.grid(column = 1, row = 0)
		
		# Пустое пространство
		self.selectedFrame.columnconfigure(2, minsize = emptySpaceSize, weight = 0)
		
		# Метка
		Entry(self.selectedFrame, textvariable = self.label[0]) \
			.grid(column = 3, row = 0, sticky = W + E)
		
		Entry(self.selectedFrame, textvariable = self.label[1], bg = defaultValueBG) \
			.grid(column = 4, row = 0, sticky = W + E)
		
		# Виджет для элементов классов-потомков
		self.detailFrame = Frame(self.selectedFrame)
		self.detailFrame.grid(column = 3, row = 1, columnspan = 2, sticky = W + N + E + S)
		
		self.selectedFrame.columnconfigure(3, weight = 1)
		self.selectedFrame.columnconfigure(4, weight = 1)
		self.selectedFrame.rowconfigure(1, weight = 1)
	
	
	def onButtonUpClicked(self):
		item = self.selectedItem()
		if item is None: return
		
		prev = self.tree.prev(item)
		if prev != "":
			parent, index = self.tree.parent(item), self.tree.index(item)
			self.tree.move(item, parent, index - 1)
			
			# Корректируем номера элементов
			self.tree.set(item, "№", index - 1)
			self.tree.set(prev, "№", index)
			
			self.updateSelectedFrame(item)
	
	
	def onButtonDownClicked(self):
		item = self.selectedItem()
		if item is None: return
		
		next = self.tree.next(item)
		if next != "":
			parent, index = self.tree.parent(item), self.tree.index(item)
			self.tree.move(item, parent, index + 1)
			
			# Корректируем номера элементов
			self.tree.set(item, "№", index + 1)
			self.tree.set(next, "№", index)
			
			self.updateSelectedFrame(item)
	
	
	def onButtonAddClicked(self):
		pass
	
	
	def onButtonRemoveClicked(self):
		item = self.selectedItem()
		if item is None: return
		
		next = self.tree.next(item)
		self.tree.delete(item)
		
		while next != "":
			i = int(self.tree.set(next, "№"))
			self.tree.set(next, "№", i - 1)
			next = self.tree.next(next)
		
		self.onSelectionChanged()
	
	
	def onButtonApplyClicked(self, item = None):
		if item is None: item = self.selectedItem()
		if item is None: return None
		
		label = self.label[0].get()
		self.tree.set(item, "Метка", label)
		
		return item
	
	
	def onSelectionChanged(self, event = None):
		item = self.selectedItem()
		
		# Обновляем состояние кнопок
		state = DISABLED if item is None else NORMAL
		for x in (self.buttonRemove, self.buttonUp, self.buttonDown):
			x["state"] = state
		
		self.updateSelectedFrame(item)
	
	
	def selectedItem(self):
		selection = self.tree.selection()
		return None if type(selection) == type("") else selection[0]
	
	
	def clear(self):
		for item in self.tree.get_children():
			self.tree.delete(item)
	
	
	def updateSelectedFrame(self, item = None, values = None):
		if item is None: item = self.selectedItem()
		values = None
		
		if item is None:
			i     = ""
			label = ""
		else:
			if values is None: values = self.tree.set(item)
			
			i     = values["№"]
			label = values["Метка"]
		
		self.i.set(i)
		self.label[0].set(label)
		
		return (item, values)
	
	
	def addElement(self, values):
		self.tree.insert(parent = "", index = END, values = values)
	
	
	def setDefaultElement(self, label):
		self.label[1].set(label)
	
	
	def elementsCount(self):
		return len(self.tree.get_children())
	
	
	def elements(self, transform):
		return [ transform(item) for item in self.tree.get_children() ]
Exemple #7
0
class ShowListUserDialog(Toplevel):
    def __init__(self,parent,**kw):
        Toplevel.__init__(self,parent,**kw)
        self.parent = parent
        self.numberTempUserAdd = 0
        self.data = Database()
        self.listUser = self.data.getUserList()

        self.menu = Menu(self)
        self.config(menu = self.menu)
        self.drawMenu(self.menu)
        self.drawDialog()
        
        for user in self.listUser:
            self.addUserIntoTree(user)

        self.linkAccelerator()

    def linkAccelerator(self):
        self.bind_all("<Control-N>",self.onAddUser)
        self.bind_all("<Delete>", self.onDeleteUser)
        self.treeUser.bind(sequence="<Double-Button-1>",func=self.onEditUser)

    def drawMenu(self, parent):
        # Type Menu
        typeMenu = Menu(parent)
        typeMenu.add_command(label = "Thêm User", command = self.onAddUser, accelerator = "Ctrl+Shift+N")
        typeMenu.add_command(label = "Xóa User", command = self.onDeleteUser, accelerator = "Delete")
        parent.add_cascade(label = "Chức năng", menu = typeMenu)
        

    def drawDialog(self):
        layoutTreeUser = Frame(self)
        
        listAttribute = ["Password", "Họ tên"]
        
        yScrollTree = Scrollbar(layoutTreeUser, orient = VERTICAL)
        xScrollTree = Scrollbar(layoutTreeUser, orient = HORIZONTAL)

        # Create Delete, Add and Edit Button

        # Create Tree View
        self.treeUser = Treeview(layoutTreeUser, column = listAttribute,
            yscrollcommand = yScrollTree.set, 
            xscrollcommand = xScrollTree.set
        )

        self.treeUser.heading(column = "#0", text = "Username")
        self.treeUser.column(column = "#0", width = 100, minwidth = 100)
        for nameAttr in listAttribute:
            self.treeUser.heading(column = nameAttr, text = nameAttr)
            self.treeUser.column(column = nameAttr, width = 100, minwidth = 100)

        
        #Create Scrollbar for tree view
        yScrollTree.pack(side = RIGHT, fill = Y)
        xScrollTree.pack(side = BOTTOM, fill = X)
        

        self.treeUser.pack(side = TOP, anchor = "w", fill = BOTH, expand = True)
        yScrollTree.config(command = self.treeUser.yview)
        xScrollTree.config(command = self.treeUser.xview)
        layoutTreeUser.pack(side = TOP, fill = BOTH, expand = True)

    def addUserIntoTree(self,userInput):
        temp = userInput

        # if not self.treeUser.exists(temp.idParent) :
        #     self.treeUser.insert("","end", str(temp.idParent), text = temp.idParent)

        # self.treeUser.item(temp.idParent, open = True)

        self.treeUser.insert("","end", str(temp.username), text = temp.username)
        self.treeUser.set(temp.username, "Password", temp.passWord)
        self.treeUser.set(temp.username, "Họ tên", temp.name)

    def onAddUser(self, event = None):
        if self.numberTempUserAdd > 9:
            messagebox.showwarning("Warning","Bạn chỉ có thể thêm tạm 10 username \n Hãy Sửa ID để lưu lại những loại tạm trên", parent = self)
            return
        userTemp = User(name = "#",username=str(self.numberTempUserAdd),passWord="******")
        self.addUserIntoTree(userTemp)
        self.numberTempUserAdd += 1
    
    def onEditUser(self, event = None):
        listTemp = ["0","1","2","3","4","5","6","7","8","9"]

        curUser = self.treeUser.item(self.treeUser.focus())
        col = self.treeUser.identify_column(event.x)
        print(curUser)
        print(col)
        
        if curUser["text"] in listTemp and col != "#0":
            if col != "#0":
                messagebox.showinfo("Thêm User","Sửa username tạm này để lưu lại vào Database",parent = self)
                return                

        cellValue = None

        if col == "#0":
            temp =  simpledialog.askstring("Đổi Username", "Nhập mới", parent = self)

            if  temp != None:
                if len(temp)>0:
                    cellValue = curUser["text"]
                    try:
                        if cellValue in listTemp:
                            newUser = User(curUser["values"][1],temp,curUser["values"][0])
                            self.data.insertUser(newUser)
                            self.treeUser.update()
                            print("Sau khi sua:",self.treeUser.item(self.treeUser.focus()))
                        else:
                            self.data.updateUsernameOfUser(cellValue,temp)
                    except (sqlite3.IntegrityError, TclError):
                        messagebox.showwarning("Opps !!!!",message="Username bạn nhập đã tồn tại !!!!", parent = self)
                        return
                    if cellValue in listTemp:
                        self.numberTempUserAdd -= 1
                                    
                else:
                    messagebox.showwarning("Empty !!!", "Username không thể để trống",parent = self)
                
                self.treeUser.insert("", str(self.treeUser.index(self.treeUser.focus())), temp ,text = temp, values = curUser["values"])
                self.treeUser.delete(self.treeUser.focus())

            return

        if col == "#1":
            temp =  simpledialog.askstring("Đổi Password", "Nhập Password mới: ", parent = self)
            if temp != None:
                if len(temp) > 0:
                    cellValue = curUser["values"][0]
                    self.data.updatePassOfUser(curUser["text"],temp)
                    curUser["values"][0] = temp
                    print(curUser["text"])
                    self.treeUser.item(curUser["text"], values = curUser["values"])
                    self.treeUser.update()
                else:
                    messagebox.showwarning("Empty !!!", "Password không thể để trống",parent = self)

            return 

        if col == "#2":
            
            temp = simpledialog.askstring("Đổi tên", "Nhập tên mới",parent = self)
            
            if temp != None:
                if len(temp) > 0:                
                    cellValue = curUser["values"][1]
                    self.data.updateNameOfuser(curUser["text"],temp)
                    curUser["values"][1] = temp
                    print(curUser["text"])
                    self.treeUser.item(curUser["text"], values = curUser["values"])
                    self.treeUser.update()
                else:
                    messagebox.showwarning("Empty !!!", "Họ tên không thể để trống",parent = self)
                    
            return 

        print("Cell Values = ", cellValue)
        
    def onDeleteUser(self, event = None):
        curItem = self.treeUser.selection()

        accept = messagebox.askokcancel("Xóa User này","Bạn thật sự muốn xóa!!! Bạn không thể hoàn tác hành động này", parent = self)
        if accept == True:
            if len(curItem) == 0:
                messagebox.showwarning(title = "Empty !!!", message = "Xin hãy chọn User bạn muốn xóa", parent = self)
                return

            for choose in curItem:
                treeItem = self.treeUser.item(choose)
                self.data.deleteUser(treeItem["text"])
                self.treeUser.delete(treeItem["text"])

    def onDestroy(self, event):
        # ask = """
        # Bạn thực sự muốn đóng ứng dụng 
        # """
        # messagebox.askokcancel("Closing!!!",)
        self.parent.linkAccelerator()
        self.destroy()


# if __name__ == "__main__":
#     root = Tk()

#     ShowListUserDialog(root)
    
#     root.mainloop()
Exemple #8
0
class FormChildTemplate:
    def __init__(self, frm_parent, connection):
        self.connection = connection
        self.directive = Message()
        self.decide_template = True
        self.id_selected = 0
        self.frm_child_list = LabelFrame(frm_parent)
        self.frm_child_crud = LabelFrame(frm_parent)
        self.frm_child_crud.config(fg=TEXT_COLOR, font=SUBTITLE_FONT)
        self.initialize_components()

    def initialize_components(self):
        """
        Method that initialize the visual components for each form associated with the local administration
        """
        # Resources for the Forms
        self.new_icon = PhotoImage(file=r"./Resources/create.png")
        self.modify_icon = PhotoImage(file=r"./Resources/modify.png")
        self.remove_icon = PhotoImage(file=r"./Resources/delete.png")
        self.save_icon = PhotoImage(file=r"./Resources/save.png")
        self.cancel_icon = PhotoImage(file=r"./Resources/cancel.png")
        self.add_icon = PhotoImage(file=r"./Resources/right.png")
        self.delete_icon = PhotoImage(file=r"./Resources/left.png")
        self.up_arrow = PhotoImage(file=r"./Resources/up_arrow.png")
        self.down_arrow = PhotoImage(file=r"./Resources/down_arrow.png")
        self.star_icon = PhotoImage(file=r"./Resources/star.png")
        self.back_icon = PhotoImage(file=r"./Resources/back.png")
        self.view_icon = PhotoImage(file=r"./Resources/view.png")

        # Components for List FRM
        lbl_sep1 = Label(self.frm_child_list)
        lbl_sep1.grid(row=0, column=0, padx=10, pady=25)
        self.trv_available = Treeview(self.frm_child_list,
                                      height=20,
                                      columns=('N', 'Name'))
        self.trv_available.heading('#0', text='ID', anchor=CENTER)
        self.trv_available.heading('#1', text='N', anchor=CENTER)
        self.trv_available.heading('#2', text='Name', anchor=CENTER)
        self.trv_available.column('#0', width=0, minwidth=50, stretch=NO)
        self.trv_available.column('#1', width=20, minwidth=20, stretch=NO)
        self.trv_available.column('#2', width=375, minwidth=375, stretch=NO)
        self.trv_available.bind("<ButtonRelease-1>",
                                self.select_template_summary)
        self.trv_available.grid(row=0, column=1, sticky=W, pady=25)
        vsb_trv_av = Scrollbar(self.frm_child_list,
                               orient="vertical",
                               command=self.trv_available.yview)
        vsb_trv_av.grid(row=0, column=2, pady=25, sticky=NS)
        self.trv_available.configure(yscrollcommand=vsb_trv_av.set)
        frm_aux4 = Frame(self.frm_child_list)
        btn_new = Button(frm_aux4, image=self.new_icon, command=self.click_new)
        btn_new.grid(row=0, column=0, pady=5, padx=5, sticky=E)
        btn_new_ttp = CreateToolTip(btn_new, 'New template')
        btn_view = Button(frm_aux4,
                          image=self.view_icon,
                          command=self.click_view)
        btn_view.grid(row=1, column=0, pady=5, padx=5, sticky=E)
        btn_view_ttp = CreateToolTip(btn_new, 'View template')
        btn_edit = Button(frm_aux4,
                          image=self.modify_icon,
                          command=self.click_update)
        btn_edit.grid(row=2, column=0, pady=5, padx=5, sticky=E)
        btn_edit_ttp = CreateToolTip(btn_edit, 'Edit template')
        btn_delete = Button(frm_aux4,
                            image=self.remove_icon,
                            command=self.click_delete)
        btn_delete.grid(row=3, column=0, pady=5, padx=5, sticky=E)
        btn_delete_ttp = CreateToolTip(btn_delete, 'Delete template')
        frm_aux4.grid(row=0, column=3, pady=25, padx=25, sticky=NW)
        sep_template = Separator(self.frm_child_list, orient=VERTICAL)
        sep_template.grid(row=0, column=4, sticky=NS, padx=25)
        frm_aux3 = Frame(self.frm_child_list)
        lbl_sep3 = Label(frm_aux3)
        lbl_sep3.grid(row=0, column=0, padx=10, pady=25, rowspan=3)
        lbl_details = Label(frm_aux3, text='Details')
        lbl_details.config(fg=TEXT_COLOR, font=SUBTITLE_FONT)
        lbl_details.grid(row=0, column=1, sticky=W, pady=25, columnspan=2)
        self.txt_summary = Text(frm_aux3, height=22, width=50)
        self.txt_summary.config(font=TEXT_FONT, bg=DISABLED_COLOR)
        self.txt_summary.grid(row=1, column=1)
        vsb_txt_sum = Scrollbar(frm_aux3,
                                orient="vertical",
                                command=self.txt_summary.yview)
        vsb_txt_sum.grid(row=1, column=2, sticky=NS)
        self.txt_summary.configure(yscrollcommand=vsb_txt_sum.set)
        lbl_sep4 = Label(frm_aux3)
        lbl_sep4.grid(row=0, column=3, padx=10, pady=25, rowspan=3)
        lbl_sep5 = Label(frm_aux3)
        lbl_sep5.grid(row=2, column=1, pady=5, columnspan=2)
        frm_aux3.grid(row=0, column=5)

        # Components for CRUD FRM
        lbl_sep6 = Label(self.frm_child_crud)
        lbl_sep6.grid(row=0, column=0, padx=10, pady=25, rowspan=10)
        lbl_name = Label(self.frm_child_crud, text='Name*')
        lbl_name.config(fg=TEXT_COLOR, font=LABEL_FONT)
        lbl_name.grid(row=0, column=1, pady=25, sticky=NW)
        lbl_description = Label(self.frm_child_crud, text='Description*')
        lbl_description.config(fg=TEXT_COLOR, font=LABEL_FONT)
        lbl_description.grid(row=0, column=6, pady=25, sticky=NW)
        lbl_sep3 = Label(self.frm_child_crud)
        lbl_sep3.grid(row=0, column=2, padx=10, pady=25)
        self.txt_name = Entry(self.frm_child_crud, width=30, font=TEXT_FONT)
        self.txt_name.grid(row=0, column=3, pady=25, sticky=NW)
        lbl_sep4 = Label(self.frm_child_crud)
        lbl_sep4.grid(row=0, column=7, padx=10, pady=25)
        self.txt_description = Text(self.frm_child_crud, height=5, width=49)
        self.txt_description.config(font=TEXT_FONT)
        self.txt_description.grid(row=0, column=8, pady=25, sticky=W)
        vsb_txt_desc = Scrollbar(self.frm_child_crud,
                                 orient="vertical",
                                 command=self.txt_description.yview)
        vsb_txt_desc.grid(row=0, column=9, pady=25, sticky=NS)
        self.txt_description.configure(yscrollcommand=vsb_txt_desc.set)
        lbl_sep7 = Label(self.frm_child_crud)
        lbl_sep7.grid(row=0, column=5, padx=10, pady=25, rowspan=3)
        lbl_sep8 = Label(self.frm_child_crud)
        lbl_sep8.grid(row=0, column=10, padx=10, pady=25, rowspan=2)
        lbl_available_d = Label(self.frm_child_crud, text='Available sections')
        lbl_available_d.config(fg=TEXT_COLOR, font=LABEL_FONT)
        lbl_available_d.grid(row=1, column=1, pady=10, sticky=W, columnspan=4)
        lbl_selected_d = Label(self.frm_child_crud, text='Selected sections*')
        lbl_selected_d.config(fg=TEXT_COLOR, font=LABEL_FONT)
        lbl_selected_d.grid(row=1, column=6, pady=10, sticky=W, columnspan=4)
        self.trv_available_sections = Treeview(self.frm_child_crud,
                                               height=10,
                                               columns=('N', 'Name',
                                                        'Data Type'))
        self.trv_available_sections.heading('#0', text='ID', anchor=CENTER)
        self.trv_available_sections.heading('#1', text='N', anchor=CENTER)
        self.trv_available_sections.heading('#2', text='Name', anchor=CENTER)
        self.trv_available_sections.heading('#3',
                                            text='Data Type',
                                            anchor=CENTER)
        self.trv_available_sections.column('#0',
                                           width=0,
                                           minwidth=20,
                                           stretch=NO)
        self.trv_available_sections.column('#1',
                                           width=20,
                                           minwidth=20,
                                           stretch=NO)
        self.trv_available_sections.column('#2',
                                           width=150,
                                           minwidth=150,
                                           stretch=NO)
        self.trv_available_sections.column('#3',
                                           width=120,
                                           minwidth=120,
                                           stretch=NO)
        self.trv_available_sections.bind("<Button-1>",
                                         self.click_trv_asections)
        self.trv_available_sections.grid(row=2,
                                         column=1,
                                         rowspan=7,
                                         columnspan=3,
                                         sticky=W,
                                         pady=10)
        vsb_trv_avs = Scrollbar(self.frm_child_crud,
                                orient="vertical",
                                command=self.trv_available_sections.yview)
        vsb_trv_avs.grid(row=2, column=4, rowspan=7, pady=10, sticky=NS)
        self.trv_available_sections.configure(yscrollcommand=vsb_trv_avs.set)
        self.trv_selected_sections = Treeview(self.frm_child_crud,
                                              height=10,
                                              columns=('N', 'Name',
                                                       'Data type',
                                                       'Mandatory', 'Main'))
        self.trv_selected_sections.heading('#0', text='ID', anchor=CENTER)
        self.trv_selected_sections.heading('#1', text='N', anchor=CENTER)
        self.trv_selected_sections.heading('#2', text='Name', anchor=CENTER)
        self.trv_selected_sections.heading('#3',
                                           text='Data type',
                                           anchor=CENTER)
        self.trv_selected_sections.heading('#4',
                                           text='Mandatory',
                                           anchor=CENTER)
        self.trv_selected_sections.heading('#5', text='Main', anchor=CENTER)
        self.trv_selected_sections.column('#0',
                                          width=0,
                                          minwidth=20,
                                          stretch=NO)
        self.trv_selected_sections.column('#1',
                                          width=20,
                                          minwidth=20,
                                          stretch=NO)
        self.trv_selected_sections.column('#2',
                                          width=150,
                                          minwidth=150,
                                          stretch=NO)
        self.trv_selected_sections.column('#3',
                                          width=120,
                                          minwidth=120,
                                          stretch=NO)
        self.trv_selected_sections.column('#4',
                                          width=80,
                                          minwidth=80,
                                          stretch=NO)
        self.trv_selected_sections.column('#5',
                                          width=80,
                                          minwidth=80,
                                          stretch=NO)
        self.trv_selected_sections.bind("<Button-1>", self.click_trv_ssections)
        self.trv_selected_sections.bind("<Double-1>",
                                        self.click_switch_mandatory)
        self.trv_selected_sections.grid(row=2,
                                        column=6,
                                        rowspan=7,
                                        columnspan=3,
                                        sticky=W,
                                        pady=10)
        vsb_trv_ses = Scrollbar(self.frm_child_crud,
                                orient="vertical",
                                command=self.trv_selected_sections.yview)
        vsb_trv_ses.grid(row=2, column=9, rowspan=7, pady=10, sticky=NS)
        self.trv_selected_sections.configure(yscrollcommand=vsb_trv_ses.set)
        self.lbl_note_optional = Label(
            self.frm_child_crud,
            text=
            'NOTES:\tTo switch between optional and mandatory, double click '
            'on selected section.\n\tChoose one or up to three main sections '
            'by first selecting the target sections\n\tand then clicking the '
            'star button.\n')
        self.lbl_note_optional.config(fg=TEXT_COLOR,
                                      font=NOTE_FONT,
                                      justify=LEFT)
        self.btn_add = Button(self.frm_child_crud,
                              image=self.add_icon,
                              command=self.click_add)
        btn_add_ttp = CreateToolTip(self.btn_add, 'Add section')
        self.btn_remove = Button(self.frm_child_crud,
                                 image=self.delete_icon,
                                 command=self.click_remove)
        btn_remove_ttp = CreateToolTip(self.btn_remove, 'Remove section')
        self.btn_main_section = Button(self.frm_child_crud,
                                       image=self.star_icon,
                                       command=self.click_main_section)
        btn_main_section_ttp = CreateToolTip(self.btn_main_section,
                                             'Main section(s)')
        self.btn_up = Button(self.frm_child_crud,
                             image=self.up_arrow,
                             command=self.click_up)
        btn_up_ttp = CreateToolTip(self.btn_up, 'Move up')
        self.btn_down = Button(self.frm_child_crud,
                               image=self.down_arrow,
                               command=self.click_down)
        btn_down_ttp = CreateToolTip(self.btn_down, 'Move down')
        sep_aux1 = Separator(self.frm_child_crud, orient=VERTICAL)
        sep_aux1.grid(row=0, column=11, sticky=NS, rowspan=10)
        frm_aux1 = Frame(self.frm_child_crud)
        self.btn_save = Button(frm_aux1,
                               image=self.save_icon,
                               command=self.click_save)
        btn_save_ttp = CreateToolTip(self.btn_save, 'Save template')
        self.btn_back = Button(frm_aux1,
                               image=self.back_icon,
                               command=self.click_back)
        btn_back_ttp = CreateToolTip(self.btn_back, 'Go back')
        self.btn_cancel = Button(frm_aux1,
                                 image=self.cancel_icon,
                                 command=self.click_cancel)
        btn_cancel_ttp = CreateToolTip(self.btn_cancel, 'Cancel')
        frm_aux1.grid(row=0,
                      column=12,
                      pady=10,
                      padx=25,
                      sticky=NW,
                      rowspan=10)

    def retrieve_list(self):
        # Remove existing elements in the list
        for item in self.trv_available.get_children():
            self.trv_available.delete(item)
        self.directive = Message(action=37)
        self.connection = self.directive.send_directive(self.connection)
        # Adding elements into the list
        for index, item in enumerate(self.connection.message.information):
            elements = item.split('¥')
            self.trv_available.insert('',
                                      'end',
                                      text=elements[0],
                                      values=(index + 1,
                                              summarize_text(elements[1],
                                                             375)))
        if len(self.trv_available.get_children()) != 0:
            self.trv_available.selection_set(
                self.trv_available.get_children()[0])
            self.select_template_summary()

    def select_template_summary(self, event=None):
        """
        Function activated when the event of selecting an item in the available templates TV is generated. It fills the
        summary text box with information of the selected template
        :param event:
        """
        if self.trv_available.item(
                self.trv_available.selection())['text'] != '':
            # Clear summary txt box
            self.txt_summary['state'] = NORMAL
            self.txt_summary.delete('1.0', 'end-1c')
            self.id_selected = int(
                self.trv_available.item(self.trv_available.selection())
                ['text'])  # Retrieve id of selected item from TreeView
            self.directive = Message(action=40,
                                     information=[self.id_selected
                                                  ])  # ask for the template
            self.connection = self.directive.send_directive(self.connection)
            # Insert template's name and description
            self.txt_summary.insert(
                'end-1c',
                "Name:\n{}\n\n".format(self.connection.message.information[0]))
            self.txt_summary.insert(
                'end-1c', "Description:\n{}\n\nSections:\n".format(
                    self.connection.message.information[1]))
            self.directive = Message(action=77, information=[
                self.id_selected
            ])  # ask for the sections of the selected template
            self.connection = self.directive.send_directive(self.connection)
            # Adding elements in the summary text box
            index = 0
            for item in self.connection.message.information:
                elements = item.split('¥')
                self.txt_summary.insert('end-1c', "{}) {}\t\t{}\t\t{}\n".format(index + 1, elements[3], 'optional' \
                    if elements[7] == '' else 'mandatory', '' if elements[8] == '' else '(MAIN)'))
                index += 1
            self.txt_summary['state'] = DISABLED

    def show_frm(self):
        self.retrieve_list()
        self.frm_child_list.grid(row=1,
                                 column=0,
                                 columnspan=9,
                                 rowspan=8,
                                 pady=10,
                                 padx=10)

    def hide_frm(self):
        self.clear_fields()
        self.frm_child_list.grid_forget()
        self.frm_child_crud.grid_forget()

    def click_new(self):
        self.view_decision = False  # Decision when viewing a template
        self.template = Template()
        self.retrieve_sections()
        self.txt_name.focus_set()
        self.show_cu_buttons()
        self.frm_child_crud['text'] = 'New template'
        self.frm_child_list.grid_forget()
        self.frm_child_crud.grid(row=1,
                                 column=0,
                                 columnspan=9,
                                 rowspan=8,
                                 pady=10,
                                 padx=10)

    def click_view(self):
        if len(self.trv_available.selection()) == 1:
            self.view_decision = True  # Decision when viewing a template
            self.directive = Message(action=40, information=[self.id_selected])
            self.connection = self.directive.send_directive(self.connection)
            self.template = Template(
                id=self.id_selected,
                name=self.connection.message.information[0],
                description=self.connection.message.information[1],
                sections=self.connection.message.information[2])
            self.txt_name.insert(0, self.template.name)
            self.txt_description.insert('1.0', self.template.description)
            self.retrieve_sections(self.template.sections)
            self.txt_name.focus_set()
            self.disable_visual_components()
            self.btn_back.grid(row=0, column=0, padx=5, pady=5, sticky=W)
            self.frm_child_list.grid_forget()
            self.frm_child_crud['text'] = 'View template'
            self.frm_child_crud.grid(row=1,
                                     column=0,
                                     columnspan=9,
                                     rowspan=8,
                                     pady=10,
                                     padx=10)
        else:
            messagebox.showwarning(parent=self.frm_child_list,
                                   title='No selection',
                                   message='You must select one item')

    def click_update(self):
        if len(self.trv_available.selection()) == 1:
            self.view_decision = False  # Decision when viewing a template
            self.directive = Message(
                action=40, information=[self.id_selected, 'validate'])
            self.connection = self.directive.send_directive(self.connection)
            if self.connection.message.action == 5:  # An error ocurred while trying to update the item
                messagebox.showerror(
                    parent=self.frm_child_list,
                    title='Can not edit the template',
                    message=self.connection.message.information[0])
            else:
                self.template = Template(
                    id=self.id_selected,
                    name=self.connection.message.information[0],
                    description=self.connection.message.information[1],
                    sections=self.connection.message.information[2])
                self.txt_name.insert(0, self.template.name)
                self.txt_description.insert('1.0', self.template.description)
                self.retrieve_sections(self.template.sections)
                self.txt_name.focus_set()
                self.show_cu_buttons()
                self.frm_child_crud['text'] = 'Update template'
                self.frm_child_list.grid_forget()
                self.frm_child_crud.grid(row=1,
                                         column=0,
                                         columnspan=9,
                                         rowspan=8,
                                         pady=10,
                                         padx=10)
        else:
            messagebox.showwarning(parent=self.frm_child_list,
                                   title='No selection',
                                   message='You must select one item')

    def click_delete(self):
        if len(self.trv_available.selection()) == 1:
            decision = messagebox.askyesno(
                parent=self.frm_child_list,
                title='Confirmation',
                message='Are you sure you want to delete the item?')
            if decision:
                self.directive = Message(action=39,
                                         information=[self.id_selected])
                self.connection = self.directive.send_directive(
                    self.connection)
                if self.connection.message.action == 5:  # An error ocurred while deleting the item
                    messagebox.showerror(
                        parent=self.frm_child_list,
                        title='Can not delete the item',
                        message=self.connection.message.information[0])
                else:
                    self.retrieve_list()
        else:
            messagebox.showwarning(parent=self.frm_child_list,
                                   title='No selection',
                                   message='You must select one item')

    def show_cu_buttons(self):
        self.btn_add.grid(row=5, column=5, padx=25)
        self.btn_remove.grid(row=6, column=5, padx=25)
        self.btn_main_section.grid(row=2, column=10, padx=25)
        self.btn_down.grid(row=6, column=10, padx=25)
        self.btn_up.grid(row=5, column=10, padx=25)
        self.btn_save.grid(row=0, column=0, padx=5, pady=5, sticky=W)
        self.btn_cancel.grid(row=1, column=0, padx=5, pady=5, sticky=W)
        self.lbl_note_optional.grid(row=9, column=6, columnspan=4, sticky=W)

    def disable_visual_components(self):
        self.txt_name['bg'] = DISABLED_COLOR
        self.txt_description['bg'] = DISABLED_COLOR
        self.txt_name['state'] = DISABLED
        self.txt_description['state'] = DISABLED
        self.btn_add.grid_forget()
        self.btn_remove.grid_forget()
        self.btn_main_section.grid_forget()
        self.btn_down.grid_forget()
        self.btn_up.grid_forget()

    def retrieve_sections(self, s_sections=None):
        if s_sections is None:
            s_sections = []
        self.directive = Message(action=32)
        self.connection = self.directive.send_directive(self.connection)
        a_sections = self.connection.message.information
        for item in self.trv_available_sections.get_children():
            self.trv_available_sections.delete(item)
        for item in self.trv_selected_sections.get_children():
            self.trv_selected_sections.delete(item)
        for item in s_sections:
            item_aux1 = item.split('¥')[2:6]
            item_aux2 = [item.split('¥')[-1]]
            item = item_aux1 + item_aux2
            item = '¥'.join(item)
            if item in a_sections:
                a_sections.remove(item)
        for index, item in enumerate(a_sections):
            elements = item.split('¥')
            self.trv_available_sections.insert(
                '',
                'end',
                text=elements[0],
                values=(index + 1, summarize_text(elements[1], 150),
                        summarize_text(elements[3], 120)))
        for index, item in enumerate(s_sections):
            elements = item.split('¥')
            self.trv_selected_sections.insert(
                '',
                'end',
                text=elements[2],
                values=(index + 1, summarize_text(elements[3], 150),
                        summarize_text(elements[5],
                                       120), elements[7], elements[8]))

    def click_add(self):
        """
        Function that moves a 'Section' from available tree view to selected tree view (in frm_child_crud)
        """
        if len(self.trv_available_sections.selection()) != 0 and len(
                self.trv_selected_sections.selection()) == 0:
            if len(self.trv_selected_sections.get_children()) != 0:
                index = self.trv_selected_sections.item(
                    self.trv_selected_sections.get_children()[-1])['values'][0]
            else:
                index = 0
            for row in self.trv_available_sections.selection():
                index += 1
                values = self.trv_available_sections.item(row)['values']
                self.trv_selected_sections.insert(
                    '',
                    'end',
                    text=self.trv_available_sections.item(row)['text'],
                    values=(index, values[1], values[2], '✓', ''))
                self.trv_available_sections.delete(row)

    def click_remove(self):
        """
        Function that moves a 'Section' from selected tree view to available tree view (in frm_child_crud)
        """
        if len(self.trv_selected_sections.selection()) != 0 and len(
                self.trv_available_sections.selection()) == 0:
            if len(self.trv_available_sections.get_children()) != 0:
                index = self.trv_available_sections.item(
                    self.trv_available_sections.get_children()
                    [-1])['values'][0]
            else:
                index = 0
            for row in self.trv_selected_sections.selection():
                index += 1
                values = self.trv_selected_sections.item(row)['values']
                self.trv_available_sections.insert(
                    '',
                    'end',
                    text=self.trv_selected_sections.item(row)['text'],
                    values=(index, values[1], values[2]))
                self.trv_selected_sections.delete(row)

    def click_up(self):
        # Make sure only one item in 'selected sections' is selected
        if len(self.trv_selected_sections.selection()) == 1 and len(
                self.trv_available_sections.selection()) == 0:
            item = self.trv_selected_sections.selection()
            index = self.trv_selected_sections.index(item)
            self.trv_selected_sections.move(item, '', index - 1)

    def click_down(self):
        # Make sure only one item in 'selected sections' is selected
        if len(self.trv_selected_sections.selection()) == 1 and len(
                self.trv_available_sections.selection()) == 0:
            item = self.trv_selected_sections.selection()
            index = self.trv_selected_sections.index(item)
            self.trv_selected_sections.move(item, '', index + 1)

    def click_main_section(self):
        # Make sure a max of three items of 'selected sections' are selected
        if 4 > len(self.trv_selected_sections.selection()) > 0 and len(
                self.trv_available_sections.selection()) == 0:
            # First change all sections as normal (not main)
            for item in self.trv_selected_sections.get_children():
                if self.trv_selected_sections.item(item)['values'][3] != '':
                    values = self.trv_selected_sections.item(item)['values']
                    self.trv_selected_sections.item(
                        item,
                        values=(values[0], values[1], values[2], values[3],
                                ''))
            # Set new main sections
            cont_error = False
            for row in self.trv_selected_sections.selection():
                values = self.trv_selected_sections.item(row)['values']
                if values[2] == 'Classification' or values[2] == 'Text':
                    self.trv_selected_sections.item(
                        row,
                        values=(values[0], values[1], values[2], values[3],
                                '✓'))
                else:  # File sections can not be main
                    cont_error = True
            if cont_error:
                messagebox.showwarning(
                    parent=self.frm_child_crud,
                    title='Main section(s)',
                    message=
                    "Main section(s) must be of 'Text' or 'Classification' data type"
                )
        else:
            messagebox.showwarning(
                parent=self.frm_child_crud,
                title='Main section(s)',
                message=
                'You must select a minimum of one and a maximum of three sections'
            )

    def click_trv_asections(self, event):
        """
        Function that removes selection from 'available' tree view when 'selected' tree view is selected (in frm_child_crud)
        """
        self.trv_selected_sections.selection_remove(
            self.trv_selected_sections.selection())

    def click_trv_ssections(self, event):
        """
        Function that removes selection from 'selected' tree view when 'available' tree view is selected (in frm_child_crud)
        """
        self.trv_available_sections.selection_remove(
            self.trv_available_sections.selection())

    def click_save(self):
        if self.validate_fields():
            self.template.name = self.txt_name.get()
            self.template.description = self.txt_description.get(
                '1.0', 'end-1c')
            if self.template.id == 0:  # Creating a template
                self.directive = Message(action=36,
                                         information=[
                                             self.template.name,
                                             self.template.description, [], [],
                                             []
                                         ])
                for item in self.trv_selected_sections.get_children():
                    values = self.trv_selected_sections.item(item)['values']
                    self.directive.information[2].append(
                        int(self.trv_selected_sections.item(item)['text']))
                    self.directive.information[3].append(values[4])
                    if values[4] != '':
                        self.directive.information[4].append('✓')
                    else:
                        self.directive.information[4].append(values[3])
            else:  # Updating a template
                self.directive = Message(action=38,
                                         information=[
                                             self.template.id,
                                             self.template.name,
                                             self.template.description, [], [],
                                             []
                                         ])
                for item in self.trv_selected_sections.get_children():
                    values = self.trv_selected_sections.item(item)['values']
                    self.directive.information[3].append(
                        int(self.trv_selected_sections.item(item)['text']))
                    self.directive.information[4].append(values[4])
                    if values[4] != '':
                        self.directive.information[5].append('✓')
                    else:
                        self.directive.information[5].append(values[3])
            self.connection = self.directive.send_directive(self.connection)
            self.clear_fields()
            self.frm_child_crud.grid_forget()
            self.show_frm()

    def click_cancel(self):
        decision = True
        if self.txt_name.get() != self.template.name or \
                self.txt_description.get('1.0', 'end-1c') != self.template.description or \
                len(self.trv_selected_sections.get_children()) != len(self.template.sections):
            decision = messagebox.askyesno(
                parent=self.frm_child_crud,
                title='Cancel',
                message='Are you sure you want to cancel?')
        if decision:
            self.click_back()

    def click_back(self):
        self.clear_fields()
        self.frm_child_crud.grid_forget()
        self.show_frm()

    def validate_fields(self):
        text_section = False
        if len(self.txt_name.get()) == 0:
            messagebox.showwarning(parent=self.frm_child_crud,
                                   title='Missing information',
                                   message='You must provide a name')
            return False
        if len(self.txt_description.get('1.0', 'end-1c')) == 0:
            messagebox.showwarning(parent=self.frm_child_crud,
                                   title='Missing information',
                                   message='You must provide a description')
            return False
        if len(self.trv_selected_sections.get_children()) == 0:
            messagebox.showwarning(
                parent=self.frm_child_crud,
                title='Missing information',
                message='You must select at least one section')
            return False
        for item in self.trv_selected_sections.get_children():
            values = self.trv_selected_sections.item(item)['values']
            if values[2] == 'Text' or values[2] == 'Classification':
                text_section = True
                break
        if not text_section:
            messagebox.showwarning(
                parent=self.frm_child_crud,
                title='Missing information',
                message='At least one section has to be of text type')
            return False
        for item in self.trv_selected_sections.get_children():
            values = self.trv_selected_sections.item(item)['values']
            if values[4] == '✓':
                if values[2] == 'Text' or values[2] == 'Classification':
                    return True
                else:
                    messagebox.showwarning(
                        parent=self.frm_child_crud,
                        title='Main section',
                        message=
                        'The main section has to be of text or classification type'
                    )
                    return False
        messagebox.showwarning(
            parent=self.frm_child_crud,
            title='Main section',
            message='You must set one of the selected section as main')
        return False

    def clear_fields(self):
        self.txt_name['state'] = NORMAL
        self.txt_description['state'] = NORMAL
        self.txt_name['bg'] = ENABLED_COLOR
        self.txt_description['bg'] = ENABLED_COLOR
        self.txt_name.delete(0, END)
        self.txt_description.delete('1.0', 'end-1c')
        self.btn_save.grid_forget()
        self.btn_cancel.grid_forget()
        self.btn_back.grid_forget()
        self.lbl_note_optional.grid_forget()

    def click_switch_mandatory(self, event):
        if not self.view_decision:  # Only if not viewing a template
            # Make sure only one item in 'selected sections' is selected
            if len(self.trv_selected_sections.selection()) == 1 and len(
                    self.trv_available_sections.selection()) == 0:
                values = self.trv_selected_sections.item(
                    self.trv_selected_sections.focus())['values']
                if values[3] == '':
                    self.trv_selected_sections.item(
                        self.trv_selected_sections.focus(),
                        values=(values[0], values[1], values[2], '✓',
                                values[4]))
                else:
                    self.trv_selected_sections.item(
                        self.trv_selected_sections.focus(),
                        values=(values[0], values[1], values[2], '',
                                values[4]))
Exemple #9
0
class TreeType(Frame):
    def __init__(self, parent, item, dialog):
        Frame.__init__(self, parent)
        self.parent = parent
        self.item = item
        self.numTypeAdded = 0
        self.dialogParent = dialog
        self.listType = item.type
        self.data = Database()
        self.drawScreen()

        self.linkAccelerator()

        for typeItem in self.listType:
            self.addTypeIntoTree(typeItem)

    def linkAccelerator(self):
        self.bind_all("<Shift-Delete>", self.onDeleteItem)
        self.bind_all("<Control-N>", self.onAddType)
        self.bind_all("<Delete>", self.onDeleteType)

    def drawScreen(self):
        # self.pack(fill = BOTH, anchor = "w", expand = True)

        ########## Listing all Bills which has been created ###########
        layoutList = Frame(self)

        self.drawTreeOfType(layoutList)

        layoutList.pack(side=LEFT, fill=BOTH, expand=True)

        self.menu = Menu(self)
        self.dialogParent.config(menu=self.menu)
        self.drawMenu(self.menu)

    def drawTreeOfType(self, parent):
        self.pack(fill=BOTH, expand=True)

        listAttribute = ["Loại hàng", "Số lượng tồn", "Đơn giá"]

        yScrollTree = Scrollbar(parent, orient=VERTICAL)
        xScrollTree = Scrollbar(parent, orient=HORIZONTAL)

        # Create Delete, Add and Edit Button
        layoutButton = Frame(self)

        btnAddType = Button(layoutButton,
                            text="+",
                            fg="green",
                            command=self.onAddType)
        btnDelType = Button(layoutButton,
                            text="-",
                            fg="red",
                            command=self.onDeleteType)
        btnRefresh = Button(layoutButton,
                            text="Refresh",
                            fg="blue",
                            command=self.dialogParent.onRefresh)
        btnAddType.pack(side=LEFT)
        btnDelType.pack(side=LEFT)
        btnRefresh.pack(side=RIGHT)

        layoutButton.pack(side=TOP, fill=X, anchor="w")

        # Create Tree View
        self.treeType = Treeview(parent,
                                 column=listAttribute,
                                 yscrollcommand=yScrollTree.set,
                                 xscrollcommand=xScrollTree.set)
        self.treeType.bind(sequence="<Double-Button-1>", func=self.onEditType)

        self.treeType.heading(column="#0", text="ID")
        self.treeType.column(column="#0", width=100, minwidth=100)
        for nameAttr in listAttribute:
            self.treeType.heading(column=nameAttr, text=nameAttr)
            self.treeType.column(column=nameAttr, width=100, minwidth=100)

        #Create Scrollbar for tree view
        yScrollTree.pack(side=RIGHT, fill=Y)
        xScrollTree.pack(side=BOTTOM, fill=X)

        self.treeType.pack(side=TOP, anchor="w", fill=BOTH, expand=True)
        yScrollTree.config(command=self.treeType.yview)
        xScrollTree.config(command=self.treeType.xview)

    def drawMenu(self, parent):
        # Item Menu
        itemMenu = Menu(parent)
        itemMenu.add_command(label="Thêm mặt hàng",
                             command=self.dialogParent.onAddItem,
                             accelerator="Ctrl+N")
        itemMenu.add_command(label="Xóa mặt hàng",
                             command=self.onDeleteItem,
                             accelerator="Shift+Delete")
        parent.add_cascade(label="Mặt hàng", menu=itemMenu)

        # Type Menu
        typeMenu = Menu(parent)
        typeMenu.add_command(label="Thêm loại hàng",
                             command=self.onAddType,
                             accelerator="Ctrl+Shift+N")
        typeMenu.add_command(label="Xóa loại hàng",
                             command=self.onDeleteType,
                             accelerator="Delete")
        parent.add_cascade(label="Loại hàng", menu=typeMenu)

    def addTypeIntoTree(self, typeInput):
        temp = typeInput

        # if not self.treeType.exists(temp.idParent) :
        #     self.treeType.insert("","end", str(temp.idParent), text = temp.idParent)

        # self.treeType.item(temp.idParent, open = True)

        self.treeType.insert("", "end", str(temp.idType), text=temp.idType)
        self.treeType.set(temp.idType, "Loại hàng", temp.name)
        self.treeType.set(temp.idType, "Số lượng tồn", temp.amount)
        self.treeType.set(temp.idType, "Đơn giá", int(temp.unitPrice))

    def onEditType(self, event=None):
        listTemp = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]

        curItem = self.treeType.item(self.treeType.focus())
        col = self.treeType.identify_column(event.x)
        print(curItem)
        print(col)

        if curItem["text"] in listTemp and col != "#0":
            if col != "#0":
                messagebox.showinfo(
                    "Thêm loại",
                    "Sửa ID loại tạm này để lưu lại vào Database",
                    parent=self)
                return

        cellValue = None

        if col == "#0":
            temp = simpledialog.askstring("Đổi ID", "Nhập ID mới", parent=self)

            if temp != None:
                if len(temp) > 0:
                    cellValue = curItem["text"]
                    try:
                        if cellValue in listTemp:
                            idTab = self.parent.select()
                            frameChosen = self.parent._nametowidget(idTab)
                            typeItem = TypeItem(curItem["values"][0], 0, 0,
                                                temp, frameChosen.item.id)
                            self.data.insertTypeItem(typeItem)
                            frameChosen.treeType.update()
                            print("Sau khi sua:",
                                  self.treeType.item(self.treeType.focus()))
                        else:
                            self.data.updateIdOfType(cellValue, temp)
                    except (sqlite3.IntegrityError, TclError):
                        messagebox.showwarning(
                            "Opps !!!!",
                            message="ID bạn nhập đã tồn tại !!!!",
                            parent=self)
                        return

                    if cellValue in listTemp:
                        frameChosen.numTypeAdded -= 1

                    self.treeType.update()
                else:
                    messagebox.showwarning("Empty !!!",
                                           "ID không thể để trống",
                                           parent=self)

                self.treeType.insert(
                    "",
                    str(self.treeType.index(self.treeType.focus())),
                    temp,
                    text=temp,
                    values=curItem["values"])
                self.treeType.delete(self.treeType.focus())

            return

        if col == "#1":

            temp = simpledialog.askstring("Đổi tên loại hàng",
                                          "Nhập tên mới",
                                          parent=self)

            if temp != None:
                if len(temp) > 0:
                    cellValue = curItem["values"][0]
                    self.data.updateNameOfType(curItem["text"], temp)
                    curItem["values"][0] = temp
                    self.treeType.item(curItem["text"],
                                       values=curItem["values"])
                    self.treeType.update()
                else:
                    messagebox.showwarning("Empty !!!",
                                           "Tên loại hàng không thể để trống",
                                           parent=self)

            return

        if col == "#2":
            temp = simpledialog.askinteger("Đổi số lượng tồn",
                                           "Nhập số lượng tồn mới: ",
                                           parent=self)
            if temp != None:
                if temp >= 0:
                    cellValue = curItem["values"][1]
                    self.data.updateAmountOfType(curItem["text"], temp)
                    curItem["values"][1] = temp
                    print(curItem["text"])
                    self.treeType.item(curItem["text"],
                                       values=curItem["values"])
                    self.treeType.update()
                else:
                    messagebox.showwarning("Empty !!!",
                                           "Số lượng không thể là số âm",
                                           parent=self)

            return

        if col == "#3":
            temp = simpledialog.askfloat("Đổi đơn giá",
                                         "Nhập đơn giá mới: ",
                                         parent=self)
            if temp != None:
                if temp >= 0:
                    cellValue = curItem["values"][2]
                    self.data.updateUnitOfType(curItem["text"], temp)
                    curItem["values"][2] = temp
                    print(curItem["text"])
                    self.treeType.item(curItem["text"],
                                       values=curItem["values"])
                    self.treeType.update()
                else:
                    messagebox.showwarning("Empty !!!",
                                           "Đơn giá không thể là số âm",
                                           parent=self)

            return
        print("Cell Values = ", cellValue)

    def onAddType(self, event=None):
        idTab = self.parent.select()
        frameChosen = self.parent._nametowidget(idTab)
        if frameChosen.numTypeAdded > 9:
            messagebox.showwarning(
                "Warning",
                "Bạn chỉ có thể thêm tạm 10 loại \n Hãy Sửa ID để lưu lại những loại tạm trên",
                parent=self)
            return

        typeItem = TypeItem(name=frameChosen.numTypeAdded,
                            amount=0,
                            unitPrice=0,
                            idType="#",
                            idParent=frameChosen.item.id)
        frameChosen.addTypeIntoTree(typeItem)
        frameChosen.numTypeAdded += 1

    def onDeleteType(self, event=None):
        idTab = self.parent.select()
        frameChosen = self.parent._nametowidget(idTab)
        curItem = frameChosen.treeType.selection()

        accept = messagebox.askokcancel(
            "Xóa loại hàng này",
            "Bạn thật sự muốn xóa!!! Bạn không thể hoàn tác hành động này",
            parent=self)
        if accept == True:
            if len(curItem) == 0:
                messagebox.showwarning(
                    title="Empty !!!",
                    message="Xin hãy chọn loại hàng bạn muốn xóa",
                    parent=self)
                return

            for choose in curItem:
                treeItem = frameChosen.treeType.item(choose)
                self.data.deleteType(treeItem["values"][0])
                frameChosen.treeType.delete(treeItem["text"])

    def onDeleteItem(self, event=None):
        idTab = self.parent.select()
        frameChosen = self.parent._nametowidget(idTab)
        accept = messagebox.askokcancel(
            "Xóa loại hàng này",
            "Bạn thật sự muốn xóa!!! Bạn không thể hoàn tác hành động này",
            parent=self)
        if accept == True:
            idTab = self.parent.select()
            frameChosen = self.parent._nametowidget(idTab)

            self.data.deleteItem(frameChosen.item.id)
            self.parent.forget(idTab)
            self.parent.update()

    def onDestroy(self, event):
        # ask = """
        # Bạn thực sự muốn đóng ứng dụng
        # """
        # messagebox.askokcancel("Closing!!!",)
        self.dialogParent.destroy()
Exemple #10
0
class EventScheduler(Tk):
    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()

    def _setup_style(self):
        # scrollbars
        for widget in ['Events', 'Tasks']:
            bg = CONFIG.get(widget, 'background', fallback='gray10')
            fg = CONFIG.get(widget, 'foreground', fallback='white')

            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.putalpha(self._slider_alpha)
            slider_vert_active = Image.new('RGBA', (13, 28), widget_fg)
            slider_vert_active.putalpha(self._slider_alpha)
            slider_vert_prelight = Image.new('RGBA', (13, 28), active_bg2)
            slider_vert_prelight.putalpha(self._slider_alpha)

            self._im_trough[widget].put(" ".join(
                ["{" + " ".join([bg] * 15) + "}"] * 15))
            self._im_slider_vert_active[widget].paste(slider_vert_active)
            self._im_slider_vert[widget].paste(slider_vert)
            self._im_slider_vert_prelight[widget].paste(slider_vert_prelight)

        for widget in self.widgets.values():
            widget.update_style()

    def report_callback_exception(self, *args):
        err = ''.join(traceback.format_exception(*args))
        logging.error(err)
        showerror('Exception', str(args[1]), err, parent=self)

    def save(self):
        logging.info('Save event database')
        data = [ev.to_dict() for ev in self.events.values()]
        with open(DATA_PATH, 'wb') as file:
            pick = Pickler(file)
            pick.dump(data)

    def update_date(self, *args):
        """Update Calendar's selected day and Events' list."""
        self.widgets['Calendar'].update_date()
        self.widgets['Events'].display_evts()
        self.update_idletasks()

    # --- bindings
    def _select(self, event):
        if not self.tree.identify_row(event.y):
            self.tree.selection_remove(*self.tree.selection())

    def _edit_on_click(self, event):
        sel = self.tree.selection()
        if sel:
            sel = sel[0]
            self.edit(sel)

    # --- class bindings
    @staticmethod
    def clear_selection(event):
        combo = event.widget
        combo.selection_clear()

    @staticmethod
    def select_all(event):
        event.widget.selection_range(0, "end")
        return "break"

    # --- show / hide
    def _menu_widgets_trace(self, item):
        self.menu_widgets.set_item_value(_(item),
                                         self.widgets[item].variable.get())

    def display_hide_widget(self, item):
        value = self.menu_widgets.get_item_value(_(item))
        if value:
            self.widgets[item].show()
        else:
            self.widgets[item].hide()

    def hide(self):
        self._visible.set(False)
        self.withdraw()
        self.save()

    def show(self):
        self._visible.set(True)
        self.deiconify()

    def _visibility_trace(self, *args):
        self.icon.menu.set_item_value(_('Manager'), self._visible.get())

    def display_hide(self, toggle=False):
        value = self.icon.menu.get_item_value(_('Manager'))
        if toggle:
            value = not value
            self.icon.menu.set_item_value(_('Manager'), value)
        self._visible.set(value)
        if not value:
            self.withdraw()
            self.save()
        else:
            self.deiconify()

    # --- event management
    def event_add(self, event):
        self.nb += 1
        iid = str(self.nb)
        self.events[iid] = event
        self.tree.insert('', 'end', iid, values=event.values())
        self.tree.item(iid, tags=str(self.tree.index(iid) % 2))
        self.widgets['Calendar'].add_event(event)
        self.widgets['Events'].display_evts()
        self.widgets['Tasks'].display_tasks()
        self.save()

    def event_configure(self, iid):
        self.tree.item(iid, values=self.events[iid].values())
        self.widgets['Calendar'].add_event(self.events[iid])
        self.widgets['Events'].display_evts()
        self.widgets['Tasks'].display_tasks()
        self.save()

    def add(self, date=None):
        iid = str(self.nb + 1)
        if date is not None:
            event = Event(self.scheduler, iid=iid, Start=date)
        else:
            event = Event(self.scheduler, iid=iid)
        Form(self, event, new=True)

    def delete(self, iid):
        index = self.tree.index(iid)
        self.tree.delete(iid)
        for k, item in enumerate(self.tree.get_children('')[index:]):
            tags = [
                t for t in self.tree.item(item, 'tags') if t not in ['1', '0']
            ]
            tags.append(str((index + k) % 2))
            self.tree.item(item, tags=tags)

        self.events[iid].reminder_remove_all()
        self.widgets['Calendar'].remove_event(self.events[iid])
        del (self.events[iid])
        self.widgets['Events'].display_evts()
        self.widgets['Tasks'].display_tasks()
        self.save()

    def edit(self, iid):
        self.widgets['Calendar'].remove_event(self.events[iid])
        Form(self, self.events[iid])

    def check_outdated(self):
        """Check for outdated events every 15 min."""
        now = datetime.now()
        for iid, event in self.events.items():
            if not event['Repeat'] and event['Start'] < now:
                tags = list(self.tree.item(iid, 'tags'))
                if 'outdated' not in tags:
                    tags.append('outdated')
                self.tree.item(iid, tags=tags)
        self.after_id = self.after(15 * 60 * 1000, self.check_outdated)

    def delete_outdated_events(self):
        now = datetime.now()
        outdated = []
        for iid, prop in self.events.items():
            if prop['End'] < now:
                if not prop['Repeat']:
                    outdated.append(iid)
                elif prop['Repeat']['Limit'] != 'always':
                    end = prop['End']
                    enddate = datetime.fromordinal(
                        prop['Repeat']['EndDate'].toordinal())
                    enddate.replace(hour=end.hour, minute=end.minute)
                    if enddate < now:
                        outdated.append(iid)
        for item in outdated:
            self.delete(item)
        logging.info('Deleted outdated events')

    def refresh_reminders(self):
        """
        Reschedule all reminders.

        Required when APScheduler is updated.
        """
        for event in self.events.values():
            reminders = [date for date in event['Reminders'].values()]
            event.reminder_remove_all()
            for date in reminders:
                event.reminder_add(date)
        logging.info('Refreshed reminders')

    # --- sorting
    def _move_item(self, item, index):
        self.tree.move(item, "", index)
        tags = [t for t in self.tree.item(item, 'tags') if t not in ['1', '0']]
        tags.append(str(index % 2))
        self.tree.item(item, tags=tags)

    @staticmethod
    def to_datetime(date):
        date_format = get_date_format("short", CONFIG.get("General",
                                                          "locale")).pattern
        dayfirst = date_format.startswith("d")
        yearfirst = date_format.startswith("y")
        return parse(date, dayfirst=dayfirst, yearfirst=yearfirst)

    def _sort_by_date(self, col, reverse):
        l = [(self.to_datetime(self.tree.set(k, col)), k)
             for k in self.tree.get_children('')]
        l.sort(reverse=reverse)

        # rearrange items in sorted positions
        for index, (val, k) in enumerate(l):
            self._move_item(k, index)

        # reverse sort next time
        self.tree.heading(col,
                          command=lambda: self._sort_by_date(col, not reverse))

    def _sort_by_desc(self, col, reverse):
        l = [(self.tree.set(k, col), k) for k in self.tree.get_children('')]
        l.sort(reverse=reverse, key=lambda x: x[0].lower())

        # rearrange items in sorted positions
        for index, (val, k) in enumerate(l):
            self._move_item(k, index)

        # reverse sort next time
        self.tree.heading(col,
                          command=lambda: self._sort_by_desc(col, not reverse))

    # --- filter
    def update_filter_val(self, event):
        col = self.filter_col.get()
        self.filter_val.set("")
        if col:
            l = set()
            for k in self.events:
                l.add(self.tree.set(k, col))

            self.filter_val.configure(values=tuple(l))
        else:
            self.filter_val.configure(values=[])
            self.apply_filter(event)

    def apply_filter(self, event):
        col = self.filter_col.get()
        val = self.filter_val.get()
        items = list(self.events.keys())
        if not col:
            for item in items:
                self._move_item(item, int(item))
        else:
            i = 0
            for item in items:
                if self.tree.set(item, col) == val:
                    self._move_item(item, i)
                    i += 1
                else:
                    self.tree.detach(item)

    # --- manager's menu
    def _post_menu(self, event):
        self.right_click_iid = self.tree.identify_row(event.y)
        self.tree.selection_remove(*self.tree.selection())
        self.tree.selection_add(self.right_click_iid)
        if self.right_click_iid:
            try:
                self.menu.delete(_('Progress'))
            except TclError:
                pass
            state = self.events[self.right_click_iid]['Task']
            if state:
                self._task_var.set(state)
                if '%' in state:
                    self._img_dot = PhotoImage(master=self, file=IM_DOT)
                else:
                    self._img_dot = tkPhotoImage(master=self)
                self.menu_task.entryconfigure(1, image=self._img_dot)
                self.menu.insert_cascade(0,
                                         menu=self.menu_task,
                                         label=_('Progress'))
            self.menu.tk_popup(event.x_root, event.y_root)

    def _delete_menu(self):
        if self.right_click_iid:
            self.delete(self.right_click_iid)

    def _edit_menu(self):
        if self.right_click_iid:
            self.edit(self.right_click_iid)

    def _set_progress(self):
        if self.right_click_iid:
            self.events[self.right_click_iid]['Task'] = self._task_var.get()
            self.widgets['Tasks'].display_tasks()
            if '%' in self._task_var.get():
                self._img_dot = PhotoImage(master=self, file=IM_DOT)
            else:
                self._img_dot = tkPhotoImage(master=self)
            self.menu_task.entryconfigure(1, image=self._img_dot)

    # --- icon menu
    def exit(self):
        self.save()
        rep = self.widgets['Pomodoro'].stop(self.widgets['Pomodoro'].on)
        if not rep:
            return
        self.menu_eyes.quit()
        self.after_cancel(self.after_id)
        try:
            self.scheduler.shutdown()
        except SchedulerNotRunningError:
            pass
        self.destroy()

    def settings(self):
        splash_supp = CONFIG.get('General', 'splash_supported', fallback=True)
        dialog = Settings(self)
        self.wait_window(dialog)
        self._setup_style()
        if splash_supp != CONFIG.get('General', 'splash_supported'):
            for widget in self.widgets.values():
                widget.update_position()

    # --- week schedule
    def get_next_week_events(self):
        """Return events scheduled for the next 7 days """
        locale = CONFIG.get("General", "locale")
        next_ev = {}
        today = datetime.now().date()
        for d in range(7):
            day = today + timedelta(days=d)
            evts = self.widgets['Calendar'].get_events(day)
            if evts:
                evts = [self.events[iid] for iid in evts]
                evts.sort(key=lambda ev: ev.get_start_time())
                desc = []
                for ev in evts:
                    if ev["WholeDay"]:
                        date = ""
                    else:
                        date = "%s - %s " % (
                            format_time(ev['Start'], locale=locale),
                            format_time(ev['End'], locale=locale))
                    place = "(%s)" % ev['Place']
                    if place == "()":
                        place = ""
                    desc.append(("%s%s %s\n" % (date, ev['Summary'], place),
                                 ev['Description']))
                next_ev[day.strftime('%A')] = desc
        return next_ev

    # --- tasks
    def get_tasks(self):
        # TODO: find events with repetition in the week
        # TODO: better handling of events on several days
        tasks = []
        for event in self.events.values():
            if event['Task']:
                tasks.append(event)
        return tasks