Exemple #1
0
    nodes_id.append(node.get_id())
    tree.add_node(node)
    previous_id = nid
end = time.time()
print "\nADDING %d NODES: %f" % (BIG_NUMBER, end - start)

start = time.time()
for node_id in nodes_id:
    tree.refresh_node(node_id)
end = time.time()
print "\nUPDATING %d NODES: %f" % (BIG_NUMBER, end - start)


start = time.time()
for node_id in nodes_id:
    tree.del_node(node_id)
end = time.time()
print "\nDELETING %d NODES: %f" % (BIG_NUMBER, end - start)

start = time.time()
previous_id = None
for index in xrange(STAIRS):
    nid = "stress" + str(index)
    node = DummyNode(nid)
    nodes_id.append(node.get_id())
    tree.add_node(node,parent_id=previous_id)
    previous_id = nid
end = time.time()
print "\nADDING %d NODES in stairs: %f" % (STAIRS, end - start)

start = time.time()
Exemple #2
0
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()
Exemple #3
0
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()