class MainWindow(object): db_tree_data = list() cached_tree_data = list() entry_window = object def __init__(self, root): self.events_manager = EventsManager() self.root = root # Base layer self.cachedTreeFrame = ttk.Frame(root, borderwidth=2, relief=GROOVE) self.cachedTreeFrame.grid(row=0, column=0, sticky=W + N + E + S) self.dbTreeFrame = ttk.Frame(root, borderwidth=2, relief=GROOVE) self.dbTreeFrame.grid(row=0, column=2, sticky=N + W + E + S) self.buttonsFrame = ttk.Frame(root, borderwidth=2) self.buttonsFrame.grid(row=1, column=0, sticky=W) # Buttons self.buttonsFrame.addButton = ttk.Button(self.buttonsFrame, text='+', command=self.add_node) self.buttonsFrame.addButton.grid(row=1, column=1) self.buttonsFrame.getButton = ttk.Button(self.root, text='<<<', command=self.get_node_from_db) self.buttonsFrame.getButton.grid(row=0, column=1) self.buttonsFrame.deleteButton = ttk.Button(self.buttonsFrame, text='-', command=self.delete_node) self.buttonsFrame.deleteButton.grid(row=1, column=2) self.buttonsFrame.resetButton = ttk.Button(self.buttonsFrame, text='Reset', command=self.reset) self.buttonsFrame.resetButton.grid(row=2, column=3) self.buttonsFrame.editButton = ttk.Button(self.buttonsFrame, text='Edit', command=self.edit) self.buttonsFrame.editButton.grid(row=1, column=3) self.buttonsFrame.applyButton = ttk.Button(self.buttonsFrame, text='Apply', command=self.apply) self.buttonsFrame.applyButton.grid(row=2, column=1) self.buttonsFrame.applyEventsButton = ttk.Button( self.buttonsFrame, text='Apply events', command=self.apply_events) # self.buttonsFrame.applyEventsButton.grid(row=3, column=1) # Trees self.cachedTreeFrame.cachedTree = ttk.Treeview(self.cachedTreeFrame) self.cachedTreeFrame.cachedTree.heading('#0', text='Cached tree') self.cachedTreeFrame.cachedTree.pack(fill=BOTH, expand=1) self.dbTreeFrame.dbTree = ttk.Treeview(self.dbTreeFrame) self.dbTreeFrame.dbTree.heading('#0', text='Database tree') self.dbTreeFrame.dbTree.pack(fill=BOTH, expand=1) # Initialize self.root.after(1000, self.load_db_data) def popup(self, node_name): self.entry_window = PopupWindow(self.root, node_name) self.buttonsFrame.addButton["state"] = "disabled" self.root.wait_window(self.entry_window.top) self.buttonsFrame.addButton["state"] = "normal" @run_in_thread def load_db_data(self): requests.get(server + 'tree/clear') with open('testdb.json') as data_file: data = json.load(data_file) update([data]) self.db_tree_data = get_all_tree() self.redraw_db() def _redraw(self, subtree, widget_tree): """ Clear tree and render :param subtree: :param widget_tree: :return: """ for i in widget_tree.get_children(): widget_tree.delete(i) self.render_tree(subtree, widget_tree) def redraw_cached(self): self._redraw(self.cached_tree_data, self.cachedTreeFrame.cachedTree) def redraw_db(self): self._redraw(self.db_tree_data, self.dbTreeFrame.dbTree) def render_tree(self, subtree, widget_tree, root_node_uuid=""): """ Render tree :param subtree: :param widget_tree: :type widget_tree: ttk.Treeview :param root_node_uuid: :return: """ for child in subtree: if 'uuid' not in child: child['uuid'] = str(uuid.uuid4()) node = child['node'] tag = 'normal' if node['deleted'] else 'deleted' if child['node']['id'] is None: child['node']['id'] = child['uuid'] child['new'] = True widget_tree.insert( root_node_uuid, 0, child["uuid"], text=node[ 'name'], # + " - " + child['uuid'] + " - " + str(child['node']['id']), values=child["uuid"], open=True, tag=tag) widget_tree.tag_configure('deleted', font='Times 10 normal') widget_tree.tag_configure('normal', font='Times 10 italic') widget_tree = self.render_tree(child['children'], widget_tree, root_node_uuid=child["uuid"]) return widget_tree def append_to_cache(self, node): rnid = node['root'] nid = node['id'] children = list() to_delete = list() for child in self.cached_tree_data: if child['node']['root'] == nid and child['node'][ 'root'] is not None: to_delete.append(child) children.append(child) for ditem in to_delete: self.cached_tree_data.pop(self.cached_tree_data.index(ditem)) if nid is not None and self.find_node_data_by_id( nid, self.cached_tree_data) is not None: return cached_root_node = self.find_node_data_by_id(rnid, self.cached_tree_data) if cached_root_node: cached_root_node['children'].append({ 'node': node, 'children': children }) if cached_root_node['node']['deleted'] and not node['deleted']: self.delete_subtree({'node': node, 'children': children}) else: self.cached_tree_data.append({'node': node, 'children': children}) self.redraw_cached() def find_node_data_by_uuid(self, node_uuid, subtree): res = None for child in subtree: if child['uuid'] == node_uuid: res = child break else: res = self.find_node_data_by_uuid(node_uuid, child['children']) if res is not None: break return res def find_node_data_by_id(self, node_id, subtree): res = None for child in subtree: if child['node']['id'] == node_id: res = child break else: res = self.find_node_data_by_id(node_id, child['children']) if res is not None: break return res def node_exists(self, node_id, subtree): """ Check if node exists in tree :param node_id: :param subtree: :return: """ return False if self.find_node_data_by_id(node_id, subtree) is None else True def delete_subtree(self, node): node['node']['deleted'] = True res = dict() res['node'] = node['node'] res['children'] = list() for child in node['children']: if not child['node']['deleted']: res['children'].append(self.delete_subtree(child)) return res def add_node(self): cache_selected_items = self.cachedTreeFrame.cachedTree.selection() if len(cache_selected_items) != 0: self.popup('') if len(self.entry_window.value) == 0: return item = self.cachedTreeFrame.cachedTree.item( cache_selected_items[0]) node_name = self.entry_window.value node_id = str(uuid.uuid4()) root_node_data = self.find_node_data_by_uuid( item['values'][0], self.cached_tree_data) self.events_manager.append_event( AddEvent(node_id, root_node_data['node']['id'], node_name)) if not root_node_data['node']['deleted']: node_data = { 'name': node_name, 'root': root_node_data['node']['id'], 'id': None, 'deleted': False } self.append_to_cache(node_data) @run_in_thread def get_node_from_db(self): db_selected_items = self.dbTreeFrame.dbTree.selection() if len(db_selected_items) != 0: item = self.dbTreeFrame.dbTree.item(db_selected_items[0]) db_node_data = self.find_node_data_by_uuid( item['values'][0], self.db_tree_data)['node'] if not db_node_data['deleted']: nid = db_node_data['id'] if not self.node_exists(nid, self.cached_tree_data): res_node = get_node(nid) self.append_to_cache(res_node) def delete_node(self): cache_selected_items = self.cachedTreeFrame.cachedTree.selection() if len(cache_selected_items) == 0: return item = self.cachedTreeFrame.cachedTree.item(cache_selected_items[0]) node_data = self.find_node_data_by_uuid(item['values'][0], self.cached_tree_data) self.events_manager.append_event(DeleteEvent(node_data['node']['id'])) if not node_data['node']['deleted']: self.delete_subtree(node_data) self.redraw_cached() def reset(self): self.load_db_data() self.cached_tree_data = list() self.redraw_cached() def edit(self): cache_selected_items = self.cachedTreeFrame.cachedTree.selection() if len(cache_selected_items) != 0: item = self.cachedTreeFrame.cachedTree.item( cache_selected_items[0]) node_data = self.find_node_data_by_uuid(item['values'][0], self.cached_tree_data) if node_data['node']['deleted'] is True: return self.popup(item['text']) if len(self.entry_window.value) == 0: return node_name = self.entry_window.value node_data['node']['name'] = node_name self.events_manager.append_event( EditEvent(node_data['node']['id'], node_name)) self.redraw_cached() def _refill_new(self, children): for child in children: if 'new' in child: child['node']['id'] = None child['node']['root'] = None self._refill_new(child['children']) @run_in_thread def apply(self): self._refill_new(self.cached_tree_data) self.cached_tree_data = update(self.cached_tree_data) self.db_tree_data = get_all_tree() self.redraw_db() self.redraw_cached() @run_in_thread def apply_events(self): events = self.events_manager.serialize_events() update_events(events) self.db_tree_data = get_all_tree() self.redraw_db() self.redraw_cached() self.events_manager.clear()