def get_tasks_tree(self): '''This create a liblarch tree suitable for tasks, including default filters For tags, filter are dynamically created at Tag insertion. ''' tasktree = Tree() f_dic = { 'workview': [self.workview], 'active': [self.active], 'closed': [self.closed, {'flat': True}], 'notag': [self.notag], 'workable': [self.is_workable], 'started': [self.is_started], 'workdue': [self.workdue], 'workstarted': [self.workstarted], 'worktostart': [self.worktostart], 'worklate': [self.worklate], 'no_disabled_tag': [self.no_disabled_tag], } for f in f_dic: filt = f_dic[f] if len(filt) > 1: param = filt[1] else: param = None tasktree.add_filter(f, filt[0], param) self.tasktree = tasktree return tasktree
def get_tasks_tree(self): '''This create a liblarch tree suitable for tasks, including default filters For tags, filter are dynamically created at Tag insertion. ''' tasktree = Tree() f_dic = { 'workview': [self.workview], 'active': [self.active], 'closed': [self.closed, { 'flat': True }], 'notag': [self.notag], 'workable': [self.is_workable], 'started': [self.is_started], 'workdue': [self.workdue], 'workstarted': [self.workstarted], 'worktostart': [self.worktostart], 'worklate': [self.worklate], 'no_disabled_tag': [self.no_disabled_tag], } for f in f_dic: filt = f_dic[f] if len(filt) > 1: param = filt[1] else: param = None tasktree.add_filter(f, filt[0], param) self.tasktree = tasktree return tasktree
def get_tasks_tree(self): """This create a liblarch tree suitable for tasks, including default filters For tags, filter are dynamically created at Tag insertion. """ tasktree = Tree() f_dic = { "workview": [self.workview], "active": [self.active], "closed": [self.closed, {"flat": True}], "notag": [self.notag], "workable": [self.is_workable], "started": [self.is_started], "workdue": [self.workdue], "workstarted": [self.workstarted], "worktostart": [self.worktostart], "worklate": [self.worklate], "no_disabled_tag": [self.no_disabled_tag], } for f in f_dic: filt = f_dic[f] if len(filt) > 1: param = filt[1] else: param = None tasktree.add_filter(f, filt[0], param) self.tasktree = tasktree return tasktree
def get_tags_tree(self, req): """This create a liblarch tree suitable for tags, including the all_tags_tag and notag_tag. """ tagtree = Tree() # Build the "all tasks tag" alltag = tag.Tag(tag.ALLTASKS_TAG, req=req) alltag.set_attribute("special", "all") alltag.set_attribute("label", "%s" % _("All tasks")) alltag.set_attribute("icon", "emblem-documents-symbolic") alltag.set_attribute("order", 0) tagtree.add_node(alltag) p = {} self.tasktree.add_filter(tag.ALLTASKS_TAG, self.alltag, parameters=p) # Build the "without tag tag" notag_tag = tag.Tag(tag.NOTAG_TAG, req=req) notag_tag.set_attribute("special", "notag") notag_tag.set_attribute("label", "%s" % _("Tasks with no tags")) notag_tag.set_attribute("icon", "task-past-due-symbolic") notag_tag.set_attribute("order", 2) tagtree.add_node(notag_tag) p = {} self.tasktree.add_filter(tag.NOTAG_TAG, self.notag, parameters=p) # Build the search tag search_tag = tag.Tag(tag.SEARCH_TAG, req=req) search_tag.set_attribute("special", "search") search_tag.set_attribute("label", _("Saved searches")) search_tag.set_attribute("icon", "system-search-symbolic") search_tag.set_attribute("order", 1) tagtree.add_node(search_tag) p = {} self.tasktree.add_filter(tag.SEARCH_TAG, search_filter, parameters=p) # Build the separator sep_tag = tag.Tag(tag.SEP_TAG, req=req) sep_tag.set_attribute("special", "sep") sep_tag.set_attribute("order", 3) tagtree.add_node(sep_tag) # Filters tagtree.add_filter('activetag', self.actively_used_tag) tagtree.add_filter('usedtag', self.used_tag) activeview = tagtree.get_viewtree(name='activetags', refresh=False) activeview.apply_filter('activetag') # This view doesn't seem to be used. So it's not useful to build it now # usedview = tagtree.get_viewtree(name='usedtags',refresh=False) # usedview.apply_filter('usedtag') self.tagtree = tagtree self.tagtree_loaded = True return tagtree
def get_tags_tree(self, req): """This create a liblarch tree suitable for tags, including the all_tags_tag and notag_tag. """ tagtree = Tree() ### building the initial tags # Build the "all tasks tag" alltag = Tag(CoreConfig.ALLTASKS_TAG, req=req) alltag.set_attribute("special", "all") alltag.set_attribute("label", "<span weight='bold'>%s</span>" % _("All tasks")) alltag.set_attribute("icon", "gtg-tags-all") alltag.set_attribute("order", 0) tagtree.add_node(alltag) p = {} self.tasktree.add_filter(CoreConfig.ALLTASKS_TAG, self.alltag, parameters=p) # Build the "without tag tag" notag_tag = Tag(CoreConfig.NOTAG_TAG, req=req) notag_tag.set_attribute("special", "notag") notag_tag.set_attribute("label", "<span weight='bold'>%s</span>" % _("Tasks with no tags")) notag_tag.set_attribute("icon", "gtg-tags-none") notag_tag.set_attribute("order", 2) tagtree.add_node(notag_tag) p = {} self.tasktree.add_filter(CoreConfig.NOTAG_TAG, self.notag, parameters=p) # Build the search tag search_tag = Tag(CoreConfig.SEARCH_TAG, req=req) search_tag.set_attribute("special", "search") search_tag.set_attribute("label", "<span weight='bold'>%s</span>" % _("Search")) search_tag.set_attribute("icon", "search") search_tag.set_attribute("order", 1) tagtree.add_node(search_tag) p = {} self.tasktree.add_filter(CoreConfig.SEARCH_TAG, search_filter, parameters=p) # Build the separator sep_tag = Tag(CoreConfig.SEP_TAG, req=req) sep_tag.set_attribute("special", "sep") sep_tag.set_attribute("order", 3) tagtree.add_node(sep_tag) #### Filters tagtree.add_filter("activetag", self.actively_used_tag) tagtree.add_filter("usedtag", self.used_tag) activeview = tagtree.get_viewtree(name="activetags", refresh=False) activeview.apply_filter("activetag") # This view doesn't seem to be used. So it's not useful to build it now # usedview = tagtree.get_viewtree(name='usedtags',refresh=False) # usedview.apply_filter('usedtag') self.tagtree = tagtree self.tagtree_loaded = True return tagtree
def get_tags_tree(self, req): '''This create a liblarch tree suitable for tags, including the all_tags_tag and notag_tag. ''' tagtree = Tree() ### building the initial tags # Build the "all tasks tag" alltag = Tag(CoreConfig.ALLTASKS_TAG, req=req) alltag.set_attribute("special", "all") alltag.set_attribute("label", "<span weight='bold'>%s</span>" % _("All tasks")) alltag.set_attribute("icon", "gtg-tags-all") alltag.set_attribute("order", 0) tagtree.add_node(alltag) p = {} self.tasktree.add_filter(CoreConfig.ALLTASKS_TAG, self.alltag, parameters=p) # Build the "without tag tag" notag_tag = Tag(CoreConfig.NOTAG_TAG, req=req) notag_tag.set_attribute("special", "notag") notag_tag.set_attribute( "label", "<span weight='bold'>%s</span>" % _("Tasks with no tags")) notag_tag.set_attribute("icon", "gtg-tags-none") notag_tag.set_attribute("order", 2) tagtree.add_node(notag_tag) p = {} self.tasktree.add_filter(CoreConfig.NOTAG_TAG, self.notag, parameters=p) # Build the search tag search_tag = Tag(CoreConfig.SEARCH_TAG, req=req) search_tag.set_attribute("special", "search") search_tag.set_attribute("label", "<span weight='bold'>%s</span>" % _("Search")) search_tag.set_attribute("icon", "search") search_tag.set_attribute("order", 1) tagtree.add_node(search_tag) p = {} self.tasktree.add_filter(CoreConfig.SEARCH_TAG, search_filter, parameters=p) # Build the separator sep_tag = Tag(CoreConfig.SEP_TAG, req=req) sep_tag.set_attribute("special", "sep") sep_tag.set_attribute("order", 3) tagtree.add_node(sep_tag) #### Filters tagtree.add_filter('activetag', self.actively_used_tag) tagtree.add_filter('usedtag', self.used_tag) activeview = tagtree.get_viewtree(name='activetags', refresh=False) activeview.apply_filter('activetag') # This view doesn't seem to be used. So it's not useful to build it now # usedview = tagtree.get_viewtree(name='usedtags',refresh=False) # usedview.apply_filter('usedtag') self.tagtree = tagtree self.tagtree_loaded = True return tagtree
class LiblarchDemo: """ Shows a simple GUI demo of liblarch usage with several functions for adding tasks """ def _build_tree_view(self): self.tree = Tree() self.tree.add_filter("even",self.even_filter) self.tree.add_filter("odd",self.odd_filter) self.tree.add_filter("flat",self.flat_filter,{"flat": True}) self.tree.add_filter("leaf",self.leaf_filter) self.view_tree = self.tree.get_viewtree() self.mod_counter = 0 self.view_tree.register_cllbck('node-added-inview',self._update_title) self.view_tree.register_cllbck('node-modified-inview',self._modified_count) self.view_tree.register_cllbck('node-deleted-inview',self._update_title) desc = {} col_name = 'label' col = {} col['title'] = "Title" col['value'] = [str, self.task_label_column] col['expandable'] = True col['resizable'] = True col['sorting'] = 'label' col['order'] = 0 desc[col_name] = col tree_view = TreeView(self.view_tree, desc) # Polish TreeView def on_row_activate(sender,a,b): print("Selected nodes are: %s" %str(tree_view.get_selected_nodes())) tree_view.set_dnd_name('liblarch-demo/liblarch_widget') tree_view.set_multiple_selection(True) tree_view.set_property("enable-tree-lines", True) tree_view.connect('row-activated', on_row_activate) return tree_view def even_filter(self,node): if node.get_id().isdigit(): return int(node.get_id())%2 == 0 else: return False def odd_filter(self,node): return not self.even_filter(node) def flat_filter(self,node,parameters=None): return True def leaf_filter(self,node): return not node.has_child() def _modified_count(self,nid,path): # print "Node %s has been modified" %nid self.mod_counter += 1 def _update_title(self,sender,nid): count = self.view_tree.get_n_nodes() if count == LOAD_MANY_TASKS_COUNT and self.start_time > 0: stop_time = time() - self.start_time print("Time to load %s tasks: %s" %(LOAD_MANY_TASKS_COUNT,stop_time)) # if count > 0: mean = self.mod_counter * 1.0 / count print("%s modified signals were received (%s per task)" %(self.mod_counter, mean)) self.window.set_title('Liblarch demo: %s nodes' %count) def __init__(self): self.window = Gtk.Window() self.window.set_size_request(640, 480) self.window.set_position(Gtk.WindowPosition.CENTER) self.window.set_border_width(10) self.window.set_title('Liblarch demo') self.window.connect('destroy', self.finish) self.liblarch_widget = self._build_tree_view() scrolled_window = Gtk.ScrolledWindow() scrolled_window.add_with_viewport(self.liblarch_widget) self.start_time = 0 # Buttons action_panel = Gtk.Box() action_panel.set_spacing(5) button_desc = [('_Add a Task', self.add_task), ('_Delete a Task', self.delete_task), ('_Print Tree', self.print_tree), ('_Print FT', self.print_ft), ('_Load many Tasks', self.many_tasks), ('_Quit', self.finish)] for name, callback in button_desc: button = Gtk.Button(name) button.connect('clicked', callback) action_panel.pack_start(button, True, True, 0) filter_panel= Gtk.Box() filter_panel.set_spacing(5) for name in self.tree.list_filters(): button = Gtk.ToggleButton("%s filter"%name) button.connect('toggled',self.apply_filter,name) filter_panel.pack_start(button, True, True, 0) # Use cases usecases_vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) usecase_box = None usecase_order = 0 usecase_order_max = 3 button_desc = [('_Tree high 3', self.tree_high_3), ('Tree high 3 backwards', self.tree_high_3_backwards), ('Load from file', self.load_from_file), ('Delete DFXBCAE', self.delete_magic), ('Delete backwards', self.delete_backwards), ('Delete randomly', self.delete_random), ('Change task', self.change_task), ('_Backend use case', self.backends), ] for name, callback in button_desc: if usecase_order <= 0: if usecase_box is not None: usecases_vbox.pack_start(usecase_box, expand=False, fill=True, padding=0) usecase_box = Gtk.Box() usecase_box.set_spacing(5) button = Gtk.Button(name) button.connect('clicked', callback) usecase_box.pack_start(button, True, True, 0) usecase_order = (usecase_order + 1) % usecase_order_max usecases_vbox.pack_start(usecase_box, expand=False, fill=True, padding=0) usecase_panel = Gtk.Expander() usecase_panel.set_label('Use cases') usecase_panel.set_expanded(True) usecase_panel.add(usecases_vbox) # Show it vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) vbox.pack_start(action_panel, False, True, 10) vbox.pack_start(filter_panel, False, True, 10) vbox.pack_start(scrolled_window, True, True, 0) vbox.pack_start(usecase_panel, False, True, 10) self.window.add(vbox) self.window.show_all() self.should_finish = threading.Event() def task_label_column(self, node): newlabel = node.get_label() return newlabel def print_tree(self, widget=None): print() print("=" * 20, "Tree", "=" * 20) self.tree.get_main_view().print_tree() print("=" * 46) print() def print_ft(self, widget=None): print() self.view_tree.print_tree() print() @save_backup def add_task(self, widget): """ Add a new task. If a task is selected, the new task is added as its child """ selected = self.liblarch_widget.get_selected_nodes() t_id = random_id() t_title = random_task_title(t_id) task = TaskNode(t_id, t_title,self.view_tree) if len(selected) == 1: # Adding a subchild parent = selected[0] self.tree.add_node(task, parent_id = parent) print('Added sub-task "%s" (%s) for %s' % (t_title, t_id, parent)) else: # Adding as a new child self.tree.add_node(task) for parent_id in selected: task.add_parent(parent_id) print('Added task "%s" (%s)' % (t_title, t_id)) def apply_filter(self,widget,param): print("applying filter: %s" %param) if param in self.view_tree.list_applied_filters(): self.view_tree.unapply_filter(param) else: self.view_tree.apply_filter(param) @save_backup def tree_high_3(self, widget): ''' We add the leaf nodes before the root, in order to test if it works fine even in this configuration''' print('Adding a tree of height 3') selected = self.liblarch_widget.get_selected_nodes() if len(selected) == 1: parent = selected[0] else: parent = None t_id = random_id() t_title = random_task_title(t_id) roottask = TaskNode(t_id, t_title,self.view_tree) local_parent = t_id for i in range(2): t_id = random_id() t_title = random_task_title(t_id) task = TaskNode(t_id, t_title,self.view_tree) self.tree.add_node(task, parent_id = local_parent) # Task becomes a parent for new task local_parent = t_id self.tree.add_node(roottask, parent_id = parent) @save_backup def tree_high_3_backwards(self, widget): print('Adding a tree of height 3 backwards') selected = self.liblarch_widget.get_selected_nodes() if len(selected) == 1: parent = selected[0] else: parent = None tasks = [] relationships = [] for i in range(3): t_id = random_id() t_title = random_task_title(t_id) task = TaskNode(t_id, t_title,self.view_tree) tasks.append((t_id, task)) if parent is not None: relationships.append((parent, t_id)) parent = t_id # Relationships can come in any order, e.g. reversed relationships = reversed(relationships) for t_id, task in tasks: print("Adding task to tree:", t_id, task) self.tree.add_node(task) print("="*50) for parent, child in relationships: print("New relationship: ", parent, "with", child) parent_node = self.tree.get_node(parent) parent_node.add_child(child) print("="*50) print() @save_backup def delete_task(self, widget, order='normal'): print('Deleting a task') selected = self.liblarch_widget.get_selected_nodes() print('Order: %s' % order) if order == 'normal': ordered_nodes = selected elif order == 'backward': ordered_nodes = reversed(selected) elif order == 'random': ordered_nodes = selected shuffle(ordered_nodes) # Replace iterator for a list => # we want to see the order in logs and the performance is not important ordered_nodes = [node for node in ordered_nodes] elif order == 'magic-combination': # testing a special case from examples/test_suite ordered_nodes = ['D', 'F', 'X', 'B', 'C', 'A', 'E'] else: Log.error('Unknown order, skipping...') return print("Tasks should be removed in this order:", ordered_nodes) for node_id in ordered_nodes: self.tree.del_node(node_id) print('Removed node %s' % node_id) self.print_tree(None) def delete_backwards(self, widget): """ Delete task backward """ self.delete_task(widget, order='backward') def delete_random(self, widget): """ Delete tasks in random order """ self.delete_task(widget, order='random') def delete_magic(self, widget): self.delete_task(widget, order='magic-combination') def change_task(self, widget): view = self.tree.get_main_view() for node_id in self.liblarch_widget.get_selected_nodes(): node = self.tree.get_node(node_id) node.label = "Ahoj" node.modified() def backends(self, widget): print("Backends....") Backend('1sec', self.should_finish, 1, self.tree).start() Backend('3sec', self.should_finish, 3, self.tree).start() Backend('5sec', self.should_finish, 5, self.tree).start() widget.set_sensitive(False) def many_tasks(self, widget): self.start_time = time() def _many_tasks(): tasks_ids = [] prefix = randint(1, 1000)* 100000 for i in range(LOAD_MANY_TASKS_COUNT): t_id = str(prefix + i) t_title = t_id task = TaskNode(t_id, t_title,self.view_tree) # There is 25 % chance to adding as a sub_task if tasks_ids != [] and randint(0, 100) < 90: parent = choice(tasks_ids) self.tree.add_node(task, parent_id = parent) else: self.tree.add_node(task) tasks_ids.append(t_id) # Sleep 0.01 second to create illusion of real tasks sleep(SLEEP_BETWEEN_TASKS) print("end of _many_tasks thread") t = threading.Thread(target=_many_tasks) t.start() def load_from_file(self, widget): dialog = Gtk.FileChooserDialog("Open..", self.window, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) dialog.set_default_response(Gtk.ResponseType.OK) response = dialog.run() if response == Gtk.ResponseType.OK: file_name = dialog.get_filename() else: file_name = None dialog.destroy() if file_name is None: return log = open(file_name, 'r').read() m = re.match('\s*Tree before operation\s+=+\s+Tree\s+=+\s+(.*?)=+', log, re.UNICODE | re.DOTALL) if m: treelines = m.group(1) items = [(len(line) - len(line.lstrip()), line.strip()) for line in treelines.splitlines()] # Filter "root" item and decrease level items = [(level, name) for level, name in items[1:]] # The "root" items should be at level 0, adjust level to that min_level = min(level for level, name in items) items = [(level-min_level, name) for level, name in items] nodes = list(set([name for level, name in items])) relationships = [] parent_level = { -1: None } for level, name in items: parent = parent_level[level - 1] relationships.append((parent, name)) for key in list(parent_level.keys()): if key > level: del parent_level[key] parent_level[level] = name print("Nodes to add:", nodes) print("Relationships:", "\n".join(str(r) for r in relationships)) print() for node_id in nodes: task = TaskNode(node_id, random_task_title(node_id),self.view_tree) self.tree.add_node(task) for parent, child in relationships: parent_node = self.tree.get_node(parent) parent_node.add_child(child) else: print("Not matched") print("Log: ", log) def finish(self, widget): self.should_finish.set() Gtk.main_quit() def run(self): Gtk.main()
class LiblarchDemo(object): """ Shows a simple GUI demo of liblarch usage with several functions for adding tasks. """ def _build_tree_view(self): self.tree = Tree() self.tree.add_filter("even",self.even_filter) self.tree.add_filter("odd",self.odd_filter) self.tree.add_filter("flat",self.flat_filter,{"flat": True}) self.tree.add_filter("leaf",self.leaf_filter) self.view_tree = self.tree.get_viewtree() self.mod_counter = 0 self.view_tree.register_cllbck('node-added-inview',self._update_title) self.view_tree.register_cllbck('node-modified-inview',self._modified_count) self.view_tree.register_cllbck('node-deleted-inview',self._update_title) desc = {} col_name = 'label' col = {} col['title'] = "Title" col['value'] = [str, self.task_label_column] col['expandable'] = True col['resizable'] = True col['sorting'] = 'label' col['order'] = 0 desc[col_name] = col tree_view = TreeView(self.view_tree, desc) # Polish TreeView def on_row_activate(sender,a,b): print( "Selected nodes are: {0!s}".format( tree_view.get_selected_nodes() ) ) tree_view.set_dnd_name('liblarch-demo/liblarch_widget') tree_view.set_multiple_selection(True) tree_view.set_property("enable-tree-lines", True) tree_view.connect('row-activated', on_row_activate) return tree_view def even_filter(self,node): if node.get_id().isdigit(): return int(node.get_id())%2 == 0 else: return False def odd_filter(self, node): return not self.even_filter(node) def flat_filter(self, node, parameters=None): return True def leaf_filter(self, node): return not node.has_child() def _modified_count(self, nid, path): # print("Node {0} has been modified".format(nid)) self.mod_counter += 1 def _update_title(self,sender,nid): count = self.view_tree.get_n_nodes() if count == LOAD_MANY_TASKS_COUNT and self.start_time > 0: stop_time = time() - self.start_time print( "Time to load {0} tasks: {1}".format( LOAD_MANY_TASKS_COUNT,stop_time ) ) # if count > 0: mean = self.mod_counter * 1.0 / count print( "{0} modified signals were received ({1} per task)".format( self.mod_counter, mean ) ) self.window.set_title('Liblarch demo: %s nodes' %count) def __init__(self): self.window = Gtk.Window() self.window.set_size_request(640, 480) self.window.set_position(Gtk.WindowPosition.CENTER) self.window.set_border_width(10) self.window.set_title('Liblarch demo') self.window.connect('destroy', self.finish) self.liblarch_widget = self._build_tree_view() scrolled_window = Gtk.ScrolledWindow() scrolled_window.add_with_viewport(self.liblarch_widget) self.start_time = 0 # Buttons action_panel = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) action_panel.set_spacing(5) button_desc = [ ('_Add a Task', self.add_task), ('_Delete a Task', self.delete_task), ('_Print Tree', self.print_tree), ('_Print FT', self.print_ft), ('_Load many Tasks', self.many_tasks), ('_Quit', self.finish) ] for name, callback in button_desc: button = Gtk.Button(name, use_underline=True) button.connect('clicked', callback) action_panel.pack_start(button, expand=True, fill=True, padding=0) filter_panel= Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) filter_panel.set_spacing(5) for name in self.tree.list_filters(): button = Gtk.ToggleButton("%s filter"%name) button.connect('toggled',self.apply_filter,name) filter_panel.pack_start(button, expand=True, fill=True, padding=0) # Use cases usecases_vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) usecase_box = None usecase_order = 0 usecase_order_max = 3 button_desc = [ ('_Tree high 3', self.tree_high_3), ('Tree high 3 backwards', self.tree_high_3_backwards), ('Load from file', self.load_from_file), ('Delete DFXBCAE', self.delete_magic), ('Delete backwards', self.delete_backwards), ('Delete randomly', self.delete_random), ('Change task', self.change_task), ('_Backend use case', self.backends), ] for name, callback in button_desc: if usecase_order <= 0: if usecase_box is not None: usecases_vbox.pack_start( usecase_box, expand=False, fill=True, padding=0 ) usecase_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) usecase_box.set_spacing(5) button = Gtk.Button(name, use_underline=True) button.connect('clicked', callback) usecase_box.pack_start(button, expand=True, fill=True, padding=0) usecase_order = (usecase_order + 1) % usecase_order_max usecases_vbox.pack_start( usecase_box, expand=False, fill=True, padding=0 ) # usecase_panel = Gtk.Expander('Use cases') usecase_panel = Gtk.Expander() usecase_panel.set_expanded(True) usecase_panel.add(usecases_vbox) # Show it vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) vbox.pack_start(action_panel, False, True, 10) vbox.pack_start(filter_panel, False, True, 10) vbox.pack_start(scrolled_window, expand=True, fill=True, padding=0) vbox.pack_start(usecase_panel, False, True, 10) self.window.add(vbox) self.window.show_all() self.should_finish = threading.Event() def task_label_column(self, node): newlabel = node.get_label() return newlabel def print_tree(self, widget=None): print print("{0} Tree {0}".format("=" * 20)) self.tree.get_main_view().print_tree() print("=" * 46) print def print_ft(self, widget=None): print self.view_tree.print_tree() print @save_backup def add_task(self, widget): """ Add a new task. If a task is selected, the new task is added as its child """ selected = self.liblarch_widget.get_selected_nodes() t_id = random_id() t_title = random_task_title(t_id) task = TaskNode(t_id, t_title,self.view_tree) if len(selected) == 1: # Adding a subchild parent = selected[0] self.tree.add_node(task, parent_id = parent) print( 'Added sub-task "{0}" ({1}) for {2}'.format( t_title, t_id, parent ) ) else: # Adding as a new child self.tree.add_node(task) for parent_id in selected: task.add_parent(parent_id) print('Added task "{0}" ({1})'.format(t_title, t_id)) def apply_filter(self,widget,param): print("applying filter: {0}".format(param)) if param in self.view_tree.list_applied_filters(): self.view_tree.unapply_filter(param) else: self.view_tree.apply_filter(param) @save_backup def tree_high_3(self, widget): """ We add the leaf nodes before the root, in order to test if it works fine even in this configuration """ print('Adding a tree of height 3') selected = self.liblarch_widget.get_selected_nodes() if len(selected) == 1: parent = selected[0] else: parent = None t_id = random_id() t_title = random_task_title(t_id) roottask = TaskNode(t_id, t_title,self.view_tree) local_parent = t_id for i in range(2): t_id = random_id() t_title = random_task_title(t_id) task = TaskNode(t_id, t_title,self.view_tree) self.tree.add_node(task, parent_id = local_parent) # Task becomes a parent for new task local_parent = t_id self.tree.add_node(roottask, parent_id = parent) @save_backup def tree_high_3_backwards(self, widget): print('Adding a tree of height 3 backwards') selected = self.liblarch_widget.get_selected_nodes() if len(selected) == 1: parent = selected[0] else: parent = None tasks = [] relationships = [] for i in range(3): t_id = random_id() t_title = random_task_title(t_id) task = TaskNode(t_id, t_title,self.view_tree) tasks.append((t_id, task)) if parent is not None: relationships.append((parent, t_id)) parent = t_id # Relationships can come in any order, e.g. reversed relationships = reversed(relationships) for t_id, task in tasks: print("Adding task to tree: {0} {1}".format(t_id, task)) self.tree.add_node(task) print("="*50) for parent, child in relationships: print("New relationship: {0} with {1}".format(parent, child)) parent_node = self.tree.get_node(parent) parent_node.add_child(child) print("="*50) print @save_backup def delete_task(self, widget, order="normal"): print("Deleting a task") selected = self.liblarch_widget.get_selected_nodes() print("Order: {0}".format(order)) if order == "normal": ordered_nodes = selected elif order == "backward": ordered_nodes = reversed(selected) elif order == "random": ordered_nodes = selected shuffle(ordered_nodes) # Replace iterator for a list => # we want to see the order in logs and the performance is not important ordered_nodes = [node for node in ordered_nodes] elif order == 'magic-combination': # testing a special case from examples/test_suite ordered_nodes = ['D', 'F', 'X', 'B', 'C', 'A', 'E'] else: Log.error('Unknown order, skipping...') return print( "Tasks should be removed in this order: {0}".format(ordered_nodes) ) for node_id in ordered_nodes: self.tree.del_node(node_id) print("Removed node {0}".format(node_id)) self.print_tree(None) def delete_backwards(self, widget): """ Delete task backward """ self.delete_task(widget, order='backward') def delete_random(self, widget): """ Delete tasks in random order """ self.delete_task(widget, order='random') def delete_magic(self, widget): self.delete_task(widget, order='magic-combination') def change_task(self, widget): view = self.tree.get_main_view() for node_id in self.liblarch_widget.get_selected_nodes(): node = self.tree.get_node(node_id) node.label = "Ahoj" node.modified() def backends(self, widget): print("Backends....") Backend('1sec', self.should_finish, 1, self.tree).start() Backend('3sec', self.should_finish, 3, self.tree).start() Backend('5sec', self.should_finish, 5, self.tree).start() widget.set_sensitive(False) def many_tasks(self, widget): self.start_time = time() def _many_tasks(): tasks_ids = [] prefix = randint(1, 1000)* 100000 for i in range(LOAD_MANY_TASKS_COUNT): t_id = str(prefix + i) t_title = t_id task = TaskNode(t_id, t_title,self.view_tree) # There is 25 % chance to adding as a sub_task if tasks_ids != [] and randint(0, 100) < 90: parent = choice(tasks_ids) self.tree.add_node(task, parent_id = parent) else: self.tree.add_node(task) tasks_ids.append(t_id) # Sleep 0.01 second to create illusion of real tasks sleep(SLEEP_BETWEEN_TASKS) print("end of _many_tasks thread") t = threading.Thread(target=_many_tasks) t.start() def load_from_file(self, widget): dialog = Gtk.FileChooserDialog("Open..", self.window, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) dialog.set_default_response(Gtk.ResponseType.OK) response = dialog.run() if response == Gtk.ResponseType.OK: file_name = dialog.get_filename() else: file_name = None dialog.destroy() if file_name is None: return log = open(file_name, 'r').read() m = re.match( '\s*Tree before operation\s+=+\s+Tree\s+=+\s+(.*?)=+', log, re.UNICODE | re.DOTALL ) if m: treelines = m.group(1) items = [ (len(line) - len(line.lstrip()), line.strip()) for line in treelines.splitlines() ] # Filter "root" item and decrease level items = [(level, name) for level, name in items[1:]] # The "root" items should be at level 0, adjust level to that min_level = min(level for level, name in items) items = [(level-min_level, name) for level, name in items] nodes = list(set([name for level, name in items])) relationships = [] parent_level = { -1: None } for level, name in items: parent = parent_level[level - 1] relationships.append((parent, name)) for key in list(parent_level.keys()): if key > level: del parent_level[key] parent_level[level] = name print("Nodes to add: {0}".format(nodes)) print( "Relationships: {0}".format( "\n".join(str(r) for r in relationships) ) ) print for node_id in nodes: task = TaskNode( node_id, random_task_title(node_id), self.view_tree ) self.tree.add_node(task) for parent, child in relationships: parent_node = self.tree.get_node(parent) parent_node.add_child(child) else: print("Not matched") print("Log: {0}".format(log)) def finish(self, widget): self.should_finish.set() Gtk.main_quit() def run(self): Gtk.main()
class ContactListWindow(object): def __init__(self): # First we do all the GTK stuff # This is not interesting from a liblarch perspective self.window = Gtk.Window() self.window.set_size_request(300, 600) self.window.set_border_width(12) self.window.set_title('Liblarch contact-list') self.window.connect('destroy', self.quit) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) vbox.set_spacing(6) # A check button to show/hide offline contacts show_offline = Gtk.CheckButton("Show offline contacts") show_offline.connect("toggled", self.show_offline_contacts) vbox.pack_start(show_offline, expand=False, fill=True, padding=0) # The search through contacts search = Gtk.Entry() search.set_icon_from_icon_name(0, "search") search.get_buffer().connect("inserted-text", self.search) search.get_buffer().connect("deleted-text", self.search) vbox.pack_start(search, expand=False, fill=True, padding=0) # The contact list, build with liblarch scrolled_window = Gtk.ScrolledWindow() scrolled_window.add_with_viewport(self.make_contact_list()) scrolled_window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) vbox.pack_start(scrolled_window, True, True, 0) # Status box = Gtk.ComboBoxText() box.append_text("Online") box.append_text("Busy") box.append_text("Offline") box.set_active(0) box.connect('changed', self.status_changed) vbox.pack_start(box, expand=False, fill=True, padding=0) self.window.add(vbox) self.window.show_all() # This is the interesting part on how we use liblarch def make_contact_list(self): # LIBLARCH TREE CONSTRUCTION # First thing, we create a liblarch tree self.tree = Tree() # Now, we add each contact *and* each team as nodes of that tree. # The team will be the parents of the contact nodes. for contact in CONTACTS: # We create the node and use the XMPP address as the node_id node = NodeContact(contact['xmpp']) # We add the status and the nickname node.set_status(contact['status']) node.set_nick(contact['name']) # The contact node is added to the tree self.tree.add_node(node) # Now, we create the team if it was not done before for team_name in contact['teams']: if not self.tree.has_node(team_name): team_node = NodeTeam(team_name) self.tree.add_node(team_node) # now we put the contact under the team node.add_parent(team_name) # we could also have done # team_node.add_child(contact[0]) # LIBLARCH VIEW and FILTER # Ok, now we have our liblarch tree. What we need is a view. self.view = self.tree.get_viewtree() # We also create a filter that will allow us to hide offline people self.tree.add_filter("online", self.is_node_online) self.offline = False self.tree.add_filter("search", self.search_filter) # And we apply this filter by default self.view.apply_filter("online") # LIBLARCH GTK.TreeView # And, now, we build our Gtk.TreeView # We will build each column of our TreeView columns = {} # The first column contain the XMPP address but will be hidden # But it is still useful for searching col = {} col['value'] = [str, lambda node: node.get_id()] col['visible'] = False col['order'] = 0 columns['XMPP'] = col # The second column is the status col = {} render_tags = CellRendererTags() render_tags.set_property('xalign', 0.0) col['renderer'] = ['status', render_tags] col['value'] = [GObject.TYPE_PYOBJECT, lambda node: node.get_status()] col['expandable'] = False col['resizable'] = False col['order'] = 1 columns['status'] = col # the third column is the nickname col = {} col['value'] = [str, lambda node: node.get_label()] col['visible'] = True col['order'] = 2 columns['nick'] = col return TreeView(self.view, columns) # This is the "online" filter. # It returns the contacts that are busy or online # and teams that have at least one contact displayed def is_node_online(self, node): if node.get_type() == "contact": # Always show myself if node.get_id() == '*****@*****.**': return True status = node.get_status() if status == "online" or status == "busy": return True else: return False # For the team, we test each contact of that team elif node.get_type() == "team": tree = node.get_tree() for child_id in node.get_children(): child = tree.get_node(child_id) status = child.get_status() if status == "online" or status == "busy": return True return False return True def show_offline_contacts(self, widget): # We should remove the filter to show offline contacts if widget.get_active(): self.view.unapply_filter('online') self.offline = True # else we apply the "online" filter, showing only online/busy people else: self.view.apply_filter('online') self.offline = False def status_changed(self, widget): new = widget.get_active_text() node = self.tree.get_node('*****@*****.**') if new == 'Busy': node.set_status('busy') elif new == 'Offline': node.set_status('offline') else: node.set_status('online') def search(self, widget, position, char, nchar=None): search_string = widget.get_text() if len(search_string) > 0: # First, we remove the old filter # Note the "refresh=False", because we know we will apply another # filter just afterwards # We also remove the online filter to search through offline # contacts if not self.offline: self.view.unapply_filter('online', refresh=False) self.view.unapply_filter('search', refresh=False) self.view.apply_filter('search', parameters={'search': search_string}) else: if not self.offline: self.view.apply_filter('online') self.view.unapply_filter('search') def search_filter(self, node, parameters=None): string = parameters['search'] if node.get_type() == "contact": if string in node.get_id() or string in node.get_nick(): return True else: return False else: return False def quit(self, widget): Gtk.main_quit()
class contact_list_window(): def __init__(self): # First we do all the GTK stuff # This is not interesting from a liblarch perspective self.window = gtk.Window() self.window.set_size_request(300, 600) self.window.set_border_width(12) self.window.set_title('Liblarch contact-list') self.window.connect('destroy', self.quit) vbox = gtk.VBox() vbox.set_spacing(6) #A check button to show/hide offline contacts show_offline = gtk.CheckButton("Show offline contacts") show_offline.connect("toggled",self.show_offline_contacts) vbox.pack_start(show_offline, False, True) #The search through contacts search = gtk.Entry() search.set_icon_from_icon_name(0, "search") search.get_buffer().connect("inserted-text",self.search) search.get_buffer().connect("deleted-text",self.search) vbox.pack_start(search, False, True) #The contact list, build with liblarch scrolled_window = gtk.ScrolledWindow() scrolled_window.add_with_viewport(self.make_contact_list()) scrolled_window.set_policy(gtk.POLICY_NEVER,gtk.POLICY_AUTOMATIC) vbox.pack_start(scrolled_window) #Status box = gtk.combo_box_new_text() box.append_text("Online") box.append_text("Busy") box.append_text("Offline") box.set_active(0) box.connect('changed',self.status_changed) vbox.pack_start(box, False, True) self.window.add(vbox) self.window.show_all() #This is the interesting part on how we use liblarch def make_contact_list(self): ##### LIBLARCH TREE CONSTRUCTION #First thing, we create a liblarch tree self.tree = Tree() #Now, we add each contact *and* each team as nodes of that tree. #The team will be the parents of the contact nodes. for contact in CONTACTS: #We create the node and use the XMPP address as the node_id node = NodeContact(contact[0]) #We add the status and the nickname node.set_status(contact[1]) node.set_nick(contact[2]) #The contact node is added to the tree self.tree.add_node(node) #Now, we create the team if it was not done before for team_name in contact[3]: if not self.tree.has_node(team_name): team_node = NodeTeam(team_name) self.tree.add_node(team_node) #now we put the contact under the team node.add_parent(team_name) #we could also have done #team_node.add_child(contact[0]) ###### LIBLARCH VIEW and FILTER #Ok, now we have our liblarch tree. What we need is a view. self.view = self.tree.get_viewtree() #We also create a filter that will allow us to hide offline people self.tree.add_filter("online",self.is_node_online) self.offline = False self.tree.add_filter("search",self.search_filter) #And we apply this filter by default self.view.apply_filter("online") ###### LIBLARCH GTK.TreeView #And, now, we build our Gtk.TreeView #We will build each column of our TreeView columns = {} #The first column contain the XMPP address but will be hidden #But it is still useful for searching col = {} col['value'] = [str, lambda node: node.get_id()] col['visible'] = False col['order'] = 0 columns['XMPP'] = col #The second column is the status col = {} render_tags = CellRendererTags() render_tags.set_property('xalign', 0.0) col['renderer'] = ['status',render_tags] col['value'] = [gobject.TYPE_PYOBJECT,lambda node: node.get_status()] col['expandable'] = False col['resizable'] = False col['order'] = 1 columns['status'] = col #the third column is the nickname col = {} col['value'] = [str, lambda node: node.get_label()] col['visible'] = True col['order'] = 2 columns['nick'] = col return TreeView(self.view,columns) #This is the "online" filter. #It returns the contacts that are busy or online #and teams that have at least one contact displayed def is_node_online(self,node): if node.get_type() == "contact": #Always show myself if node.get_id() == '*****@*****.**': return True status = node.get_status() if status == "online" or status == "busy": return True else: return False #For the team, we test each contact of that team elif node.get_type() == "team": tree = node.get_tree() for child_id in node.get_children(): child = tree.get_node(child_id) status = child.get_status() if status == "online" or status == "busy": return True return False return True def show_offline_contacts(self,widget): #We should remove the filter to show offline contacts if widget.get_active(): self.view.unapply_filter('online') self.offline = True #else we apply the "online" filter, showing only online/busy people else: self.view.apply_filter('online') self.offline = False def status_changed(self,widget): new = widget.get_active_text() node = self.tree.get_node('*****@*****.**') if new == 'Busy': node.set_status('busy') elif new == 'Offline': node.set_status('offline') else: node.set_status('online') def search(self,widget,position,char,nchar=None): search_string = widget.get_text() if len(search_string) > 0: #First, we remove the old filter #note the "refresh=False", because we know we will apply #another filter just afterwards #We also remove the online filter to search through offline contacts if not self.offline: self.view.unapply_filter('online',refresh=False) self.view.unapply_filter('search',refresh=False) self.view.apply_filter('search',parameters={'search':search_string}) else: if not self.offline: self.view.apply_filter('online') self.view.unapply_filter('search') def search_filter(self,node,parameters=None): string = parameters['search'] if node.get_type() == "contact": if string in node.get_id() or string in node.get_nick(): return True else: return False else: return False def quit(self, widget): gtk.main_quit()