Exemple #1
0
class _List(WidgetBase):
    def __init__(self, master, **options):
        super().__init__(master)
        from tkinter.ttk import Treeview
        self.widget = Treeview(self.master, show="tree", **options)
        self.widget.bind("<<TreeviewSelect>>", self.callback)
        self.value = ()

    def set_header(self, column, text):
        self.widget.heading("#" + str(column), text=text)

    def add_item(self,
                 parent="",
                 index=0,
                 id=None,
                 label="",
                 values=None,
                 **options):
        if not id is None:
            options.update(iid=id)
        if not values is None:
            options.update(values=values)
        self.widget.insert(parent=parent, index=index, text=label, **options)

    def callback(self, event):
        self.value = self.widget.selection()

    def set_selection(self, items):
        self.value = items
        self.widget.selection_set(items)

    def exist_item(self, id):
        return self.widget.exists(id)
Exemple #2
0
class Window(Tk):
    def __init__(self):
        super().__init__()
        self.title('Treepace Tree Transformation GUI Demo')
        self.geometry('640x400')
        self.resizable(False, False)
        self.tree_frame = Frame()
        self.tree_button = Button(self.tree_frame, text="Load tree",
                                  command=self.load)
        self.tree_button.pack(expand=True, fill=BOTH)
        self.tree_text = Text(self.tree_frame, width=20)
        self.tree_text.pack(expand=True, fill=BOTH)
        self.tree_text.insert('1.0', DEFAULT_TREE)
        self.tree_frame.pack(side=LEFT, expand=True, fill=BOTH)
        
        self.program_frame = Frame()
        self.program_button = Button(self.program_frame, text="Transform",
                                     command=self.transform)
        self.program_button.pack(expand=True, fill=BOTH)
        self.program_text = Text(self.program_frame, width=60, height=8)
        self.program_text.pack(expand=True, fill=BOTH)
        self.program_text.insert('1.0', DEFAULT_PROGRAM)
        self.tv = Treeview(self.program_frame)
        self.tv.pack(expand=True, fill=BOTH)
        self.program_frame.pack(side=LEFT, expand=True, fill=BOTH)

        GuiNode.tv = self.tv
        self.load()
    
    def load(self):
        if self.tv.exists('root'):
            self.tv.delete('root')
        program = self.tree_text.get('1.0', END)
        self.tree = Tree.load(program, IndentedText, GuiNode)
    
    def transform(self):
        try:
            self.tree.transform(self.program_text.get('1.0', END))
        except Exception as e:
            messagebox.showerror("Transformation error", e)
class TreeViewControl():
    '''
    
    '''
    PARENTLESS_ROOT_ID: str = 'parentless_root_id'
    PARENTLESS_ROOT_KEY: str = 'Parentless'

    def __init__(self,
                 master: tk.Frame,
                 treeview_config: TreeViewConfiguration = None):
        '''
        
        :param tree_config: TreeViewConfiguration
        '''
        log_enter_func('TreeViewControl', '__init__', {
            'master': master,
            'tree_config': treeview_config
        })

        #Contains all TreeViewItems added to the TreeView
        self.__added_treeview_items = []
        #Is the item which is currently about to be added to the tree.
        self.__current_treeview_item = None
        #Contains the error message if the state is set to ERROR.
        #This is optional! It is not said that there is an message if an error occurs.
        self.__error_message = None
        #State of the TreeViewControl
        self.__treeview_state = TreeViewState.OK
        #Contains all registered observers.
        self.__observers = []
        #This array contains all TreeViewItem for which the parent element was not added
        #at the time as they were added.
        self.__parentless_items = []
        #UI Treeview element.
        self.__treeview = None
        #Contains the mapping of the treeview_id and the corresponding TreeViewItem.
        self.__treeview_id_dict = {}
        #build the treeview
        self.__build_tree(master, treeview_config)

        log_leave_func('TreeViewControl', '__init__')

    ### public methods ###################################################################
    def insert(self, treeview_item: TreeViewItem) -> None:
        '''
        Add the given treeview_item to the TreeViewcontrol.
        If the parent_id of the treeview_item is set, the given treeview_item will inserted
        under TreeViewItem with the corresponding ID.
        If the parent_id of the treeview_item is set, but there is no item with that ID
        in the TreeViewControl, the given treeview_item will be inserted under the "Parentless"
        top level element.
        As soon as the parent is added the parentless TreeViewItem will be moved from the 
        "Parentless" element to its actual parent.
        :param treeview_item:TreeViewItem the item to be added to the TreeViewcontrol
        '''
        log_enter_func('TreeViewControl', 'insert',
                       {'treeview_item': treeview_item})

        self.__current_treeview_item = treeview_item

        new_treeview_id = self.__create_treeview_id()

        self.__treeview_id_dict[new_treeview_id] = treeview_item
        self.__added_treeview_items.append(treeview_item)

        self.__validate_input()

        parent_id = ''  #This would insert the item as root element

        if self.__treeview_state == TreeViewState.OK:
            parent_id = self.__get_treeview_id(treeview_item.parent_id)
        elif self.__treeview_state == TreeViewState.PARENT_NOT_EXISTS:
            #Add current_treeview_item to parentless items
            self.__parentless_items.append(treeview_item)
            parent_id = TreeViewControl.PARENTLESS_ROOT_ID

            #Check if parentless node exists
            if not self.__treeview.exists(parent_id):
                #If not add it.
                self.__treeview.insert(
                    '',
                    'end',
                    parent_id,
                    text=TreeViewControl.PARENTLESS_ROOT_KEY)

        values = ['']

        if not treeview_item.value == None:
            values = [treeview_item.value]

        self.__treeview.insert(parent_id,
                               'end',
                               new_treeview_id,
                               text=treeview_item.key,
                               values=values)

        self.__revise_parentlesses()

        log_leave_func('TreeViewControl', 'insert')

    def remove(self, treeview_item: TreeViewItem = None):
        '''
        Removes the given treeview_item and all its children from the TreeViewControl.
        If treeview_item == None, then the whole TreeViewControl will be cleared.
        :param treeview_item: TreeViewItem to be deleted form tree.
        '''
        log_enter_func('TreeViewControl', 'remove',
                       {'treeview_item': treeview_item})

        if treeview_item == None:
            # Clear the whole tree
            self.__treeview.delete(*self.__treeview_id_dict.keys())
            self.__added_treeview_items.clear()
            self.__treeview_id_dict.clear()
            self.__parentless_items.clear()

        else:
            #Remove the given item and its children from the tree.
            for child in self.__find_children(treeview_item.parent_id):
                self.remove(child)

            treeview_id = self.__get_treeview_id(treeview_item.id)

            self.__treeview.delete(treeview_id)
            self.__added_treeview_items.remove(treeview_item)
            del self.__treeview_id_dict[treeview_id]
            self.__parentless_items.remove(treeview_item)

        log_leave_func('TreeViewControl', 'remove')

    def select(self, treeview_item: TreeViewItem) -> None:
        '''
        Removes the focus and the selection form the currently selected treeview item.
        If treeview_item and the id in the treeview_itme is NOT None
        then the corresponding item in the tree will be selected and focused.
        If treeview_item or its attribute is None, then after removing the focus
        and the selection the method will be left.
        :param treeview_item: The treeview item to be selected.
        '''
        log_enter_func('TreeViewControl', 'select',
                       {'treeview_item': treeview_item})

        if len(self.__treeview) > 0:
            self.__treeview.selection_remove(self.__treeview.selection()[0])

        self.__treeview.focus('')

        if not treeview_item == None and not treeview_item.id == None:
            self.__treeview.focus(treeview_item.id)
            self.__treeview.selection_set(treeview_item.id)

        log_leave_func('TreeViewControl', 'select')

    ### Public Observer methods ##########################################################

    def remove_observer(self, observer: TreeViewObserver) -> None:
        '''
        Removes the given TreeViewObserver from the TreeViewControl.
        :param observer: TreeViewObserver to be removed.
        '''
        log_enter_func('TreeViewControl', 'remove_observer',
                       {'observer': observer})

        self.__observers.remove(observer)

        log_leave_func('TreeViewControl', 'remove_observer')

    def clear_observers(self) -> None:
        '''
        Removes all registered observers.
        '''
        log_enter_func('TreeViewControl', 'clear_observers')

        self.__observers.clear()

        log_leave_func('TreeViewControl', 'clear_observers')

    def add_observer(self, observer: TreeViewObserver) -> None:
        '''
        Adds the given observer to the TreeViewcontrol.
        :param observer: TreeViewObserver to be added.
        '''
        log_enter_func('TreeViewControl', 'add_observer',
                       {'observer': observer})

        self.__observers.append(observer)

        log_leave_func('TreeViewControl', 'add_observer')

    ######################################################################################

    ### private methods ##################################################################
    def __find_children(self, parent_id):
        '''
        Searches the __added_treeview_items array for items with item.parent_id == parent_id
        :param parent_id: Id (not treeview_id) of the parent.
        '''
        log_enter_func('TreeViewControl', '__find_children',
                       {'parent_id': parent_id})

        children = []

        for treeview_item in self.__added_treeview_items:
            if treeview_item.parent_id == parent_id:
                children.append(treeview_item)

        log_leave_func('TreeViewControl', '__find_children', children)

    def __build_tree(self, master, treeview_config):
        '''
        Build the basic setup of the treeview by considering treeview_config.
        :param treeview_config:
        '''
        log_enter_func('TreeViewControl', '__build_tree', {
            'master': master,
            'tree_config': treeview_config
        })

        self.__treeview = Treeview(master=master,
                                   columns=('#%s' %
                                            (treeview_config.column_count)))
        self.__treeview.column('#0', stretch=tk.NO)

        for idx, col_name in enumerate(treeview_config.column_names):
            self.__treeview.heading('#%s' % (idx), text=col_name, anchor=tk.W)

        self.__treeview.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)

        self.__treeview.bind('<<TreeviewSelect>>', self.__notify_on_select)
        self.__treeview.bind('<Double-1>', self.__notify_on_double_click)

        log_leave_func('TreeViewControl', '__build_tree')

    def __create_treeview_id(self):
        '''
        Creates the temporary id and sets it in the current TreeViewItem.
        '''
        log_enter_func('TreeViewControl', '__create_treeview_id')

        treeview_id = uuid.uuid1()

        log_leave_func('TreeViewControl', '__create_treeview_id', treeview_id)

        return str(treeview_id)

    def __get_treeview_id(self, _id):
        '''
        Searches the __treeview_id_dict mapping for the TreeViewItem (value) for 
        item with TreeViewItem.id == __id, and, if found, returns the key of the
        mapping entry.
        :param _id: The id of the TreeViewItem 
        '''
        log_enter_func('TreeViewControl', '__get_treeview_id', {'_id': _id})

        searched_treeview_id = None

        for treeview_id, treeview_item in self.__treeview_id_dict.items():
            if treeview_item.id == _id:
                searched_treeview_id = treeview_id
                break

        log_leave_func('TreeViewControl', '__get_treeview_id',
                       searched_treeview_id)

        return searched_treeview_id

    def __notify_on_double_click(self, event):
        '''
        Event handler for the double click event.
        Calls the just the __notify_on_selection_event with is_double_click == True.
        :param event: Double click event, contains the treeview_id of the double clicked TreeViewItem
        '''
        log_enter_func('TreeViewControl', '__notify_on_double_click',
                       {'event': event})

        self.__notify_on_selection_event(event, True)

        log_leave_func('TreeViewControl', '__notify_on_double_click')

    def __notify_on_error(self):
        '''
        Notifies all registered observers that an error has occurred.
        '''
        log_enter_func('TreeViewControl', '__notify_on_error')

        for observer in self.__observers:
            observer.on_error(self.__error_message)

        log_leave_func('TreeViewControl', '__notify_on_error')

    def __notify_on_select(self, event):
        '''
        Event handler for the select event.
        Calls the just the __notify_on_selection_event with is_double_click == False.
        :param event: Select event, contains the treeview_id of the selected TreeViewItem
        '''
        log_enter_func('TreeViewControl', '__notify_on_select',
                       {'event': event})

        self.__notify_on_selection_event(event, False)

        log_leave_func('TreeViewControl', '__notify_on_select')

    def __notify_on_selection_event(self, event, is_double_click):
        '''
        Calls for the given event the corresponding observer method.
        is_double_click == True -> on_double_click
        is_double_click == False -> on_select
        :param event: Select or the double click event
        :param is_double_click: is True if the event was a double click event, otherwise False.
        '''
        log_enter_func('TreeViewControl', '__notify_on_selection_event', {
            'event': event,
            'is_double_click': is_double_click
        })

        selected_treeview_item = self.__treeview_id_dict[
            event.widget.selection()[0]]

        for observer in self.__observers:
            if is_double_click and selected_treeview_item.is_double_clickable:
                observer.on_double_click(selected_treeview_item)
            else:
                if selected_treeview_item.is_selectable:
                    observer.on_select(selected_treeview_item)

        log_leave_func('TreeViewControl', '__notify_on_selection_event')

    def __revise_parentlesses(self):
        '''
        Checks for each parentless TreeViewItem if now the parent is in the tree.
        If the parentless TreeViewItem will be move under the proper superior element
        it will be removed from __parentless_items array. 
        '''
        log_enter_func('TreeViewControl', '__revise_parentlesses')

        for parentless_item in self.__parentless_items:
            self.__current_treeview_item = parentless_item
            self.__validate_input()
            if self.__treeview_state == TreeViewState.OK:
                treeview_id = self.__get_treeview_id(parentless_item.id)
                parent_id = self.__get_treeview_id(parentless_item.parent_id)

                self.__properties.move(treeview_id, parent_id, 'end')
                self.__parentless_items.remove(parentless_item)

        if len(self.__parentless_items) == 0 and self.__treeview.exists(
                TreeViewControl.PARENTLESS_ROOT_ID):
            self.__treeview.delete(TreeViewControl.PARENTLESS_ROOT_ID)

        log_leave_func('TreeViewControl', '__revise_parentlesses')

    def __search_parent(self):
        '''
        Searches in the __added_treeview_items for the parent of the element
        which is currently assigned to the __current_treeview_item. 
        If found it'll return the parent TreeViewItem otherwise None.
        '''
        log_enter_func('TreeViewControl', '__search_parent')

        parent = None

        for treeview_item in self.__added_treeview_items:
            if treeview_item.id == self.__current_treeview_item.parent_id:
                parent = treeview_item
                break

        log_leave_func('TreeViewControl', '__search_parent', parent)

        return parent

    def __set_error_state(self, error_message):
        '''
        Sets the state and the error message of the TreeViewControl and triggers the error notification of observers.
        :param error_message: The error message
        '''
        log_enter_func('TreeViewControl', '__set_error_state',
                       {'error_message': error_message})

        self.__error_message = error_message
        self.__treeview_state = TreeViewState.ERROR
        self.__notify_on_error()

        log_leave_func('TreeViewControl', '__set_error_state')

    def __validate_input(self):
        '''
        Set the treview_state to TreeViewState.OK.
        Checks if the parent id of the __current_treeview_item is set and if so it searches
        for the parent in the __added_treeview_items array. If it finds the item the treeview_state remains unchanged.
        If the parent couldn't be found the treeview_state will be changed to TreeViewState.PARENT_NOT_EXISTS.
        If the parent id of the __current_treeview_item is not set the __treeview_state is set to TreeViewState.NO_PARENT.
        '''
        log_enter_func('TreeViewControl', '__validate_input')

        self.__treeview_state = TreeViewState.OK

        if not self.__current_treeview_item.parent_id == None:

            parent_item = self.__search_parent()

            if parent_item == None:
                self.__treeview_state = TreeViewState.PARENT_NOT_EXISTS

        else:
            self.__treeview_state = TreeViewState.NO_PARENT

        log_set_var('TreeViewControl', '__validate_input', '__treeview_state',
                    self.__treeview_state)

        log_leave_func('TreeViewControl', '__validate_input')
Exemple #4
0
class EasyGridTree(EasyGrid):
    def __init__(self, structure, widths: list[int]):
        EasyGrid.__init__(self, structure)
        self.data = None
        self.modified_data = {}
        self.current_id = None
        self.original_row = None
        self.modified_row = None
        self.window = None
        self.eg = None
        self.eg_save_button = None
        self.eg_reset_button = None
        self.tree_view = None
        self.widths = widths
        self.sample_create = None
        self.creating = False
        self.filters = None
        self.filtering_by = None

    def is_modified(self):
        return not (self.original_row == self.modified_row)

    def modified(self):
        if self.creating:
            self.eg_save_button['state'] = "normal"
        else:
            if self.is_modified():
                self.eg_save_button['state'] = "normal"
            else:
                self.eg_save_button['state'] = "disable"

        if self.creating:
            if self.original_row == self.modified_row:
                self.eg_reset_button['state'] = "disabled"
            else:
                self.eg_reset_button['state'] = "normal"
        else:
            if self.data.get(self.current_id):
                if self.data[self.current_id] == self.modified_row:
                    self.eg_reset_button['state'] = "disabled"
                else:
                    self.eg_reset_button['state'] = "normal"
            else:
                if self.modified_data[self.current_id] == self.modified_row:
                    self.eg_reset_button['state'] = "disabled"
                else:
                    self.eg_reset_button['state'] = "normal"

    def on_add(self):
        self.creating = True
        self.eg_save_button['text'] = 'Create'

        self.current_id = 'X\n\n' + str(random.random())
        self.original_row = copy.deepcopy(self.sample_create)
        self.modified_row = copy.deepcopy(self.sample_create)
        self.eg_reset_button['state'] = "disabled"

        self.eg_save_button['state'] = "disable"

        self.eg.update(EasyData(self.modified_row, self.modified))
        self.modified()

        center(self.window)
        self.window.grab_set()
        self.window.deiconify()

    def on_remove(self):
        if not self.tree_view.selection():
            return

        index = self.tree_view.selection()[0]
        self.current_id = str(
            self.tree_view.item(index)['values'][0]).split('*')[0]
        if not self.current_id.startswith('X'):
            messagebox.showinfo('Delete',
                                'You cannot delete a consolidated element!')
            return

        self.original_row = self.modified_data.get(self.current_id, None)
        if self.original_row is None:
            self.original_row = self.data[self.current_id]
        name = self.original_row['Name']
        if messagebox.askyesno('Delete',
                               f"Are you sure you want to delete {name}?"):
            new_row = {'Name': name, 'INTERNAL_DELETED': True}
            if self.current_id in self.data:
                self.modified_data[self.current_id] = new_row
            else:
                del self.modified_data[self.current_id]
            self.update_row(self.current_id, new_row)

    def on_double_click(self, _):
        if not self.tree_view.selection():
            return

        self.creating = False
        self.eg_save_button['text'] = 'Save'

        index = self.tree_view.selection()[0]
        self.current_id = str(
            self.tree_view.item(index)['values'][0]).split('*')[0]
        self.original_row = self.modified_data.get(self.current_id, None)
        if self.original_row is None:
            self.original_row = self.data[self.current_id]
        self.modified_row = copy.deepcopy(self.original_row)

        self.modified()

        self.eg_save_button['state'] = "disable"

        self.eg.update(EasyData(self.modified_row, self.modified))

        center(self.window)
        self.window.grab_set()
        self.window.deiconify()

    def inp_upd_serve(self, i):
        def inp_upd(x):
            self.filtering_by[i] = x.lower()
            self.update()
            return True

        return inp_upd

    def build(self, parent):
        self.frame = Frame(parent)

        self.filtering_by = [None] * len(self.structure)

        self.filters = Frame(self.frame)

        for i in range(len(self.structure)):
            element = self.structure[i]
            if isinstance(element.easy, EasyJSONKey) or isinstance(
                    element.easy, EasyJSONStats):
                continue
            text = Label(self.filters, text=element.name)
            text.pack(side=LEFT, padx=5)
            if isinstance(element.easy, EasyJSONEnum):
                inp = OptionMenu(self.filters,
                                 StringVar(),
                                 '',
                                 *[x.get_name() for x in element.easy.enum],
                                 command=self.inp_upd_serve(i))
                inp.configure(width=element.field_width)
                inp.pack(side=LEFT, pady=5)
            else:
                inp = Entry(self.filters)
                inp.configure(width=element.field_width)
                validation_command = self.frame.register(self.inp_upd_serve(i))
                inp.configure(validate='all',
                              validatecommand=(validation_command, '%P'))
                inp.pack(side=LEFT, pady=5)

        self.filters.grid(row=0, column=0, columnspan=2, sticky='ew')

        self.tree_view = Treeview(self.frame,
                                  selectmode='browse',
                                  column=tuple(
                                      [ei.name for ei in self.structure]),
                                  show='headings')
        for i in range(len(self.structure)):
            ei = self.structure[i]
            self.tree_view.heading(f'#{i + 1}', text=ei.name, anchor=CENTER)
            self.tree_view.column(f'#{i + 1}', anchor=W)
        self.tree_view.bind("<Double-1>", self.on_double_click)
        self.tree_view.bind("<Return>", self.on_double_click)
        self.tree_view.grid(row=1, column=0, sticky='news')

        scroll = Scrollbar(self.frame,
                           orient="vertical",
                           command=self.tree_view.yview)
        scroll.grid(row=1, column=1, sticky='ns')

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

        for i in range(len(self.widths)):
            val = self.widths[i]
            if val:
                self.tree_view.column(f'#{i + 1}',
                                      minwidth=val,
                                      width=val,
                                      stretch=NO)

        self.frame.grid_rowconfigure(0, weight=1)
        self.frame.grid_rowconfigure(1, weight=0)
        self.frame.grid_columnconfigure(0, weight=1)
        self.frame.grid_columnconfigure(1, weight=0)

        self.frame.grid(row=2, column=0, columnspan=2, sticky='nswe')

        self.frame.rowconfigure(0, weight=0)
        self.frame.rowconfigure(1, weight=1)

        # Window

        self.window = Toplevel(self.tree_view)
        self.window.title("JSON Editor")
        self.window.resizable(False, False)
        self.window.withdraw()

        self.eg = EasyGrid(self.structure, self.weights)
        self.eg.build(self.window)
        self.eg.grid(0, 0)

        save_frame = Frame(self.window)

        self.eg_save_button = Button(save_frame, text="Save")
        self.eg_save_button.pack(side=LEFT, padx=5)
        self.eg_reset_button = Button(save_frame, text="Reset")
        self.eg_reset_button.pack(side=RIGHT, padx=5)

        def recur_diff(a, b):
            for k in a.keys():
                if a[k] == b.get(k, None):
                    if type(a[k]) == dict:
                        diff = recur_diff(a[k], b.get(k, None))
                        if diff:
                            return diff
                else:
                    return k
            return None

        def focus_set(_):
            last_modification = recur_diff(self.original_row,
                                           self.modified_row)
            self.eg_save_button.focus_set()

            def after_focus():
                if self.creating:
                    self.window.withdraw()
                    self.window.grab_release()
                    self.modified_data[self.current_id] = self.modified_row
                    total = self.original_row.copy()
                    total.update(self.modified_row)
                    self.modified_row = None
                    self.update_row(self.current_id, total)
                    messagebox.showinfo("Created", "Created successfully!")
                else:
                    if last_modification and (not self.is_modified()):
                        messagebox.showinfo(
                            "Error",
                            f"Could not save: field '{last_modification}' was wrong!"
                        )
                    elif self.is_modified():
                        self.window.withdraw()
                        self.window.grab_release()
                        self.modified_data[self.current_id] = self.modified_row
                        total = self.original_row.copy()
                        total.update(self.modified_row)
                        current_data = self.data.get(self.current_id)
                        if (current_data is not None
                            ) and current_data == self.modified_row:
                            del self.modified_data[self.current_id]
                        self.modified_row = None
                        self.update_row(self.current_id, total)

            self.tree_view.after(0, after_focus)

        self.eg_save_button.bind("<1>", focus_set)
        self.eg_save_button['state'] = "disabled"

        def reset_focus_set(_):
            self.eg_reset_button.focus_set()

            def after_reset_focus():
                if messagebox.askyesno(
                        'Reset',
                        'Are you sure you want to reset modified data?'):
                    if (self.current_id
                            in self.modified_data) and (self.current_id
                                                        in self.data):
                        del self.modified_data[self.current_id]
                    if self.creating:
                        self.original_row = self.sample_create
                        self.modified_row = copy.deepcopy(self.sample_create)
                    else:
                        self.original_row = self.data.get(self.current_id)
                        if self.original_row is None:
                            self.original_row = self.modified_data[
                                self.current_id]
                            self.modified_row = copy.deepcopy(
                                self.original_row)
                            self.update_row(
                                self.current_id,
                                self.modified_data[self.current_id])
                        else:
                            self.original_row = self.modified_data[
                                self.current_id]
                            self.modified_row = copy.deepcopy(
                                self.original_row)
                            self.update_row(self.current_id,
                                            self.modified_row[self.current_id])
                    self.eg.update(EasyData(self.modified_row, self.modified))
                    self.modified()

            self.tree_view.after(0, after_reset_focus)

        self.eg_reset_button.bind("<1>", reset_focus_set)
        self.eg_reset_button['state'] = "disabled"

        save_frame.grid(row=1, column=0, sticky='s', pady=(15, 15))

        def try_close():
            if self.is_modified():
                if messagebox.askyesno(
                        "Quit",
                        "Are you sure you want to exit without saving?"):
                    self.window.grab_release()
                    self.window.withdraw()
            else:
                self.window.grab_release()
                self.window.withdraw()

        self.window.protocol("WM_DELETE_WINDOW", try_close)

    def update_row(self, row_id, row_data):
        to_show = []
        if row_data.get('INTERNAL_DELETED') is not None:
            if self.tree_view.exists(row_id):
                self.tree_view.delete(row_id)
            return
        for ei in self.structure:
            if isinstance(ei.easy, EasyJSON):
                ej = cast(EasyJSON, ei.easy)
                to_show.append(ej.show(row_data, row_id))
        if self.tree_view.exists(row_id):
            got_data = self.data.get(row_id)
            if got_data is None:
                got_data = self.modified_data[row_id]
            if got_data == row_data:
                self.tree_view.item(row_id, values=tuple(to_show))
            else:
                to_show[0] += '*'
                self.tree_view.item(row_id, values=tuple(to_show))
        else:
            self.tree_view.insert('', END, row_id, values=tuple(to_show))

    @staticmethod
    def sort(x: str):
        if x.isnumeric():
            return int(x)
        else:
            return -1

    def update(self, data=None):
        if data:
            self.data = data
        else:
            data = copy.deepcopy(self.data)
            data.update(self.modified_data)
        self.tree_view.delete(*self.tree_view.get_children())
        for k in sorted(data.keys(), key=EasyGridTree.sort):
            correct = True
            for i in range(len(self.structure)):
                if self.filtering_by[i]:
                    if self.structure[i].name in data[k]:
                        if self.filtering_by[i] not in str(
                                data[k][self.structure[i].name]).lower():
                            correct = False
                            break
            if correct:
                self.update_row(k, data[k])
Exemple #5
0
class ConfigurationControl(object):
    '''
    classdocs
    '''
    __root_id: str = ''

    __observers: List[ConfigurationControlObserver] = []

    __configuration: Treeview = None

    __inserted: List[str] = []

    def __init__(self, master: Frame):
        '''
        Constructor
        '''

        log_enter_func('ConfigurationControl', '__init__', {'master': master})

        self.__configuration = Treeview(master=master)
        self.__configuration.heading('#0',
                                     text='Configuration',
                                     anchor=tkinter.W)
        self.__configuration.pack(fill=BOTH, side=tkinter.LEFT, expand=True)
        self.__configuration.bind('<<TreeviewSelect>>',
                                  self.__notifiy_observer)

        log_leave_func('ConfigurationControl', '__init__')

    def select(self, _id: str):

        log_enter_func('ConfigurationControl', 'select', {'_id': _id})

        if not _id == None:
            self.__configuration.focus(_id)
            self.__configuration.selection_set(_id)

        log_leave_func('ConfigurationControl', 'select')

    def insert_conf(self,
                    conf_id: str,
                    conf_name: str,
                    parent: str = 'confs',
                    pos: str = 'end'):

        log_enter_func(
            'ConfigurationControl', 'insert_conf', {
                'conf_id': conf_id,
                'conf_name': conf_name,
                'parent': parent,
                'pos': pos
            })

        if parent == None:
            parent = self.__root_id

        log_set_var('ConfigurationControl', 'insert_conf', 'parent', parent)

        self.__inserted.append(conf_id)
        self.__configuration.insert(parent, pos, conf_id, text=conf_name)
        self.__configuration.item(conf_id, open=True)

        log_leave_func('ConfigurationControl', 'insert_conf')

    def __remove(self, conf_id):

        log_enter_func('ConfigurationControl', '__remove',
                       {'conf_id': conf_id})

        self.__configuration.delete(conf_id)

        log_leave_func('ConfigurationControl', '__remove')

    def remove_all(self):

        log_enter_func('ConfigurationControl', 'remove_all')

        for conf_id in self.__inserted:
            if self.__configuration.exists(conf_id):
                self.__remove(conf_id)

        self.__inserted.clear()

        log_leave_func('ConfigurationControl', 'remove_all')

    def add_obeserver(self, observer: ConfigurationControlObserver):

        log_enter_func('ConfigurationControl', 'add_obeserver',
                       {'observer': observer})

        self.__observers.append(observer)

        log_leave_func('ConfigurationControl', 'add_obeserver')

    def remove_observer(self, observer: ConfigurationControlObserver):

        log_enter_func('ConfigurationControl', 'remove_observer',
                       {'observer': observer})

        self.__observers.remove(observer)

        log_leave_func('ConfigurationControl', 'remove_observer')

    def __notifiy_observer(self, event):  # @UnusedVariable

        log_enter_func('ConfigurationControl', '__notifiy_observer',
                       {'event': event})

        conf_id = self.__configuration.focus()

        if conf_id == self.__root_id or conf_id == '':
            conf_id = None

        log_set_var('ConfigurationControl', '__notifiy_observer', 'conf_id',
                    conf_id)

        for observer in self.__observers:
            observer.on_conf_selected(conf_id)

        log_leave_func('ConfigurationControl', '__notifiy_observer')
Exemple #6
0
class ShowBillDialog:
    def __init__(self, parent):
        self.parent = parent
        self.data = Database()
        self.listBill = self.data.getBillList()
        self.dialog = Toplevel(parent)
        self.dialog.title("Bảng thống kê Hóa đơn")
        self.dialog.geometry("1000x400")

        self.drawDialog(self.dialog)

    def drawDialog(self, parent):

        layoutTree = Frame(parent)

        self.drawTreeBill(layoutTree)

        for bill in self.listBill:
            self.addBillIntoTree(bill)

        layoutTree.pack(side=TOP, fill=BOTH, expand=True)

    def drawTreeBill(self, parent):
        listAttribute = [
            "Mặt hàng", "Loại hàng", "ID", "Số lượng xuất", "Thành giá",
            "Người tạo"
        ]

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

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

        # btnAddBill = Button(layoutButton, text = "+", fg = "green", command = self.onAddBill)
        # btnDelBill = Button(layoutButton, text = "-", fg = "red", command = self.onDeleteBill)
        # btnEditBill = Button(layoutButton, text = "Edit", fg = "blue")

        # btnAddBill.pack(side = LEFT)
        # btnDelBill.pack(side = LEFT)
        # btnEditBill.pack(side = LEFT)

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

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

        self.treeBill.column(column="#0", width=100, minwidth=100)
        for nameAttr in listAttribute:
            self.treeBill.heading(column=nameAttr, text=nameAttr)
            self.treeBill.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.treeBill.pack(side=TOP, anchor="w", fill=BOTH, expand=True)
        yScrollTree.config(command=self.treeBill.yview)
        xScrollTree.config(command=self.treeBill.xview)

    def addBillIntoTree(self, billInput):
        bill = billInput

        if not self.treeBill.exists(bill.CreatedDate):
            self.treeBill.insert("",
                                 "end",
                                 str(bill.CreatedDate),
                                 text=bill.CreatedDate,
                                 tags="parents")

        self.treeBill.item(bill.CreatedDate, open=True)

        self.treeBill.insert(bill.CreatedDate,
                             END,
                             bill.CreatedTime,
                             text=bill.CreatedTime,
                             tags="childs")
        self.treeBill.set(bill.CreatedTime, "Mặt hàng", bill.name)
        self.treeBill.set(bill.CreatedTime, "Loại hàng", bill.type)
        self.treeBill.set(bill.CreatedTime, "ID", bill.id)
        self.treeBill.set(bill.CreatedTime, "Số lượng xuất", bill.amount)
        self.treeBill.set(bill.CreatedTime, "Thành giá", int(bill.price))
        self.treeBill.set(bill.CreatedTime, "Người tạo", bill.CreatedUser)
Exemple #7
0
class HomeScreen(Frame):
    def __init__(self, parent, username):
        Frame.__init__(self, parent)
        self.parent = parent
        self.data = Database()
        self.userCreate = username
        self.drawScreen()

        self.linkAccelerator()

        date = datetime.datetime.now().date().strftime("%d-%m-%Y")
        self.listBill = self.data.getListBillAtDate(date)
        for bill in self.listBill:
            self.addBillIntoTree(bill)

    def linkAccelerator(self):
        if self.userCreate == "root":
            self.bind_all("<Control-U>", self.onViewListUser)
            self.bind_all("<Control-l>", self.onViewActivityLog)

        self.bind_all("<Control-n>", self.onNewItem)
        self.bind_all("<Control-N>", self.onNewType)
        self.bind_all("<Control-q>", self.onQuit)
        self.bind_all("<Control-L>", self.onLogOut)

        self.bind_all("<Delete>", self.onDeleteBill)
        self.bind_all("<Control-A>", self.onAddBill)
        self.bind_all("<Control-v>", self.onViewItemTable)
        self.bind_all("<Control-V>", self.onViewBillTable)

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

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

        self.drawTreeOfBill(layoutList)

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

        ########## Create Menu for HomePage ##########

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

    def drawMenu(self, parent):
        # File Menu
        fileMenu = Menu(parent)

        newPopupMenu = Menu(fileMenu)
        newPopupMenu.add_command(label="Thêm mặt hàng ...",
                                 command=self.onNewItem,
                                 accelerator="Ctrl+N")
        newPopupMenu.add_command(label="Thêm loại hàng ...",
                                 command=self.onNewType,
                                 accelerator="Ctrl+Shift+N")
        fileMenu.add_cascade(label="Thêm", menu=newPopupMenu)

        fileMenu.add_separator()

        exportPopupMenu = Menu(fileMenu)
        exportPopupMenu.add_command(label="Xuất File Mặt Hàng",
                                    command=self.onExportExcelFile)
        exportPopupMenu.add_command(label="Xuất File Đơn Hàng",
                                    command=self.onExportExcelBillFile)

        fileMenu.add_cascade(label="Xuất File Excel", menu=exportPopupMenu)

        statPopupMenu = Menu(fileMenu)
        statPopupMenu.add_command(label="Thống Kê Mặt Hàng",
                                  command=self.onViewItemTable,
                                  accelerator="Ctrl+V")
        statPopupMenu.add_command(label="Thống Kê Hóa Đơn",
                                  command=self.onViewBillTable,
                                  accelerator="Ctrl+Shift+V")
        fileMenu.add_cascade(label="Thống kê", menu=statPopupMenu)
        if self.userCreate == "root":
            fileMenu.add_command(label="Xem nhật kí hoạt động",
                                 command=self.onViewActivityLog)

        fileMenu.add_separator()

        fileMenu.add_command(label="Thoát",
                             command=self.onQuit,
                             accelerator="Ctrl+Q")
        parent.add_cascade(label="Tệp", menu=fileMenu, underline=0)

        # User Menu
        userMenu = Menu(parent)
        userMenu.add_command(label="Xem thông tin User",
                             command=self.onViewInfo)
        if self.userCreate == "root":
            userMenu.add_command(label="Danh sách các User",
                                 command=self.onViewListUser)

        userMenu.add_separator()

        userMenu.add_command(label="Đăng xuất",
                             command=self.onLogOut,
                             accelerator="Ctrl+Shift+L")
        parent.add_cascade(label="User", menu=userMenu, underline=0)

        # About Menu
        windowMenu = Menu(parent)
        windowMenu.add_command(label="About", command=self.onAbout)
        parent.add_cascade(label="Windows", menu=windowMenu, underline=0)

    def drawTreeOfBill(self, parent):
        listAttribute = [
            "Mặt hàng", "Loại hàng", "ID", "Số lượng xuất", "Thành giá",
            "Người tạo"
        ]

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

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

        btnAddBill = Button(layoutButton,
                            text="+",
                            fg="green",
                            command=self.onAddBill)
        btnDelBill = Button(layoutButton,
                            text="-",
                            fg="red",
                            command=self.onDeleteBill)
        # btnEditBill = Button(layoutButton, text = "Edit", fg = "blue")

        btnAddBill.pack(side=LEFT)
        btnDelBill.pack(side=LEFT)
        # btnEditBill.pack(side = LEFT)

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

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

        self.treeBill.column(column="#0", width=100, minwidth=100)
        for nameAttr in listAttribute:
            self.treeBill.heading(column=nameAttr, text=nameAttr)
            self.treeBill.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.treeBill.pack(side=TOP, anchor="w", fill=BOTH, expand=True)
        yScrollTree.config(command=self.treeBill.yview)
        xScrollTree.config(command=self.treeBill.xview)

    def addBillIntoTree(self, billInput):
        bill = billInput

        if not self.treeBill.exists(bill.CreatedDate):
            self.treeBill.insert("",
                                 "end",
                                 str(bill.CreatedDate),
                                 text=bill.CreatedDate,
                                 tags="parents")

        self.treeBill.item(bill.CreatedDate, open=True)

        self.treeBill.insert(bill.CreatedDate,
                             END,
                             bill.CreatedTime,
                             text=bill.CreatedTime,
                             tags="childs")
        self.treeBill.set(bill.CreatedTime, "Mặt hàng", bill.name)
        self.treeBill.set(bill.CreatedTime, "Loại hàng", bill.type)
        self.treeBill.set(bill.CreatedTime, "ID", bill.id)
        self.treeBill.set(bill.CreatedTime, "Số lượng xuất", bill.amount)
        self.treeBill.set(bill.CreatedTime, "Thành giá", int(bill.price))
        self.treeBill.set(bill.CreatedTime, "Người tạo", bill.CreatedUser)

    def onAddBill(self, event=None):
        AddBillDialog(self)

    def onEditBill(self, event):
        pass

    def onDeleteBill(self, event=None):
        # if len(self.treeBill.selection()) == 0:
        #     messagebox.showwarning(title = "Empty !!!", message = "Xin hãy chọn Hóa đơn bạn muốn xóa")
        #     return

        # for choose in self.treeBill.selection():
        #     treeItem = self.treeBill.item(choose)

        #     if treeItem["tags"][0] == "childs":
        #         self.data.deleteBillAtTime(treeItem["text"])
        #     elif treeItem["tags"][0] == "parents":
        #         self.data.deleteAllBillAtDate(treeItem["text"])

        #     self.treeBill.delete(treeItem["text"])
        messagebox.showerror("Opps !!!!",
                             "Bạn không thể xóa !!!!",
                             parent=self)

    ######### Menu Function #####################
    def onExportExcelFile(self):
        self.filePath = filedialog.asksaveasfile(
            title="Choose where you want to save File",
            filetypes=(("Microsoft Excel", "*.xlsx"), ("All Files", "*.*")))
        if self.filePath == None:
            return
        wb = Workbook()
        ws = wb.active

        data = Database()
        listItem = data.getItemList()

        for item in listItem:
            ws = createSheetAsItem(wb, item)
            createTitleTypeItem(ws, item)
            adjustColumn(ws)
            addTypeIntoSheet(ws, item.type)

        wb.remove_sheet(wb.active)
        wb.save(self.filePath.name)

    def onExportExcelBillFile(self):
        self.filePath = filedialog.asksaveasfile(
            title="Choose where you want to save File",
            filetypes=(("Microsoft Excel", "*.xlsx"), ("All Files", "*.*")))
        if self.filePath == None:
            return
        wb = Workbook()
        ws = wb.active

        data = Database()
        listBill = data.getBillList()

        listDate = [listBill[0].CreatedDate]

        for bill in listBill:
            if bill.CreatedDate not in listDate:
                listDate.append(bill.CreatedDate)

        for date in listDate:
            listBillAtDate = data.getListBillAtDate(date)
            ws = createSheetAsDate(wb, date)
            createTitleBillInDate(ws, date)
            adjustColumn(ws)
            addBillIntoSheet(ws, listBillAtDate)

        wb.remove_sheet(wb.active)
        wb.save(self.filePath.name)

    def onViewItemTable(self, event=None):
        ShowDataWindow(self)

    def onViewBillTable(self, event=None):
        ShowBillDialog(self)

    def onQuit(self, event=None):
        exit()

    def onViewInfo(self):
        UserInfoDialog(self, self.userCreate)

    def onLogOut(self, event=None):
        self.destroy()
        self.menu.destroy()
        self.parent.geometry("400x400")
        self.parent.backToPreviousFrame()

    def onNewType(self, event=None):
        NewTypeDialog(self)

    def onNewItem(self, event=None):
        NewItemDialog(self)

    def onViewListUser(self, event=None):
        ShowListUserDialog(self)

    def onViewActivityLog(self, event=None):
        pass

    def onAbout(self):
        about = """
        Developer: Trần Dũng
        Tên Ứng dụng: Managing Selling App
        Version: 1.0
        Release: 06-01-2019
        """
        popup = Toplevel(self)
        popup.title("About")
        layoutAbout = Frame(popup)

        lblAbout = Label(layoutAbout, text=about)
        lblAbout.pack(side=TOP, fill=BOTH, expand=True)

        layoutAbout.pack(side=TOP, fill=BOTH, expand=True, padx=10, pady=10)