示例#1
0
    def __init__(self, parent, db, db_owner, tmpdir, pyro_timeout):

        self.db = db
        self.db_owner = db_owner
        if pyro_timeout:
            self.pyro_timeout = float(pyro_timeout)
        else:
            self.pyro_timeout = None

        self.regname = None

        self.updater = None
        self.tmpdir = tmpdir
        self.gcapture_windows = []

        gobject.threads_init()

        #self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window = gtk.Dialog(
            "Choose a suite", parent,
            gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
            (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK,
             gtk.RESPONSE_OK))
        #self.window.set_modal(True)
        self.window.set_title("Suite Chooser")
        self.window.set_size_request(750, 400)
        self.window.set_icon(
            get_icon())  # TODO: not needed for a dialog window?
        #self.window.set_border_width( 5 )

        self.window.connect("delete_event", self.delete_all_event)

        sw = gtk.ScrolledWindow()
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)

        self.regd_treeview = gtk.TreeView()
        self.regd_treestore = gtk.TreeStore(str, str, str, str, str, str, str)
        self.regd_treeview.set_model(self.regd_treestore)
        self.regd_treeview.set_rules_hint(True)
        # search column zero (Ctrl-F)
        self.regd_treeview.connect('key_press_event', self.on_suite_select)
        self.regd_treeview.connect('button_press_event', self.on_suite_select)
        self.regd_treeview.set_search_column(0)

        # Start updating the liststore now, as we need values in it
        # immediately below (it may be possible to delay this till the
        # end of __init___() but it doesn't really matter.
        if self.db:
            self.dbopt = '--db=' + self.db
        else:
            self.dbopt = ''

        regd_ts = self.regd_treeview.get_selection()
        regd_ts.set_mode(gtk.SELECTION_SINGLE)

        cr = gtk.CellRendererText()
        #cr.set_property( 'cell-background', '#def' )
        tvc = gtk.TreeViewColumn('Suite',
                                 cr,
                                 text=0,
                                 foreground=4,
                                 background=5)
        tvc.set_resizable(True)
        tvc.set_sort_column_id(0)
        self.regd_treeview.append_column(tvc)

        cr = gtk.CellRendererText()
        tvc = gtk.TreeViewColumn('Port',
                                 cr,
                                 text=1,
                                 foreground=4,
                                 background=5)
        tvc.set_resizable(True)
        # not sure how this sorting works
        #tvc.set_sort_column_id(1)
        self.regd_treeview.append_column(tvc)

        cr = gtk.CellRendererText()
        #cr.set_property( 'cell-background', '#def' )
        tvc = gtk.TreeViewColumn('Title',
                                 cr,
                                 markup=2,
                                 foreground=4,
                                 background=6)
        tvc.set_resizable(True)
        #vc.set_sort_column_id(2)
        self.regd_treeview.append_column(tvc)

        cr = gtk.CellRendererText()
        tvc = gtk.TreeViewColumn('Location',
                                 cr,
                                 text=3,
                                 foreground=4,
                                 background=5)
        tvc.set_resizable(True)
        #vc.set_sort_column_id(3)
        self.regd_treeview.append_column(tvc)

        vbox = self.window.vbox

        sw.add(self.regd_treeview)

        vbox.pack_start(sw, True)

        self.selected_label_text = '(double-click or OK to select; right-click for db options)'
        self.selected_label = gtk.Label(self.selected_label_text)

        filter_entry = EntryTempText()
        filter_entry.set_width_chars(7)  # Reduce width in toolbar
        filter_entry.connect("activate", self.filter)
        filter_entry.set_temp_text("filter")
        filter_toolitem = gtk.ToolItem()
        filter_toolitem.add(filter_entry)
        tooltip = gtk.Tooltips()
        tooltip.enable()
        tooltip.set_tip(filter_toolitem,
                        "Filter suites \n(enter a sub-string or regex)")

        expand_button = gtk.ToolButton()
        image = gtk.image_new_from_stock(gtk.STOCK_ADD,
                                         gtk.ICON_SIZE_SMALL_TOOLBAR)
        expand_button.set_icon_widget(image)
        expand_button.connect('clicked',
                              lambda x: self.regd_treeview.expand_all())

        collapse_button = gtk.ToolButton()
        image = gtk.image_new_from_stock(gtk.STOCK_REMOVE,
                                         gtk.ICON_SIZE_SMALL_TOOLBAR)
        collapse_button.set_icon_widget(image)
        collapse_button.connect('clicked',
                                lambda x: self.regd_treeview.collapse_all())

        hbox = gtk.HBox()

        eb = gtk.EventBox()
        eb.add(self.selected_label)
        eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse('#bbc'))
        hbox.pack_start(eb, True)
        hbox.pack_start(expand_button, False)
        hbox.pack_start(collapse_button, False)
        hbox.pack_start(filter_toolitem, False)

        vbox.pack_start(hbox, False)

        self.window.show_all()

        self.start_updater()
示例#2
0
class ControlLED(object):
    """
LED suite control interface.
    """
    def __init__(self, cfg, updater, usercfg, info_bar, get_right_click_menu,
                 log_colors, insert_task_popup):

        self.cfg = cfg
        self.updater = updater
        self.usercfg = usercfg
        self.info_bar = info_bar
        self.get_right_click_menu = get_right_click_menu
        self.log_colors = log_colors
        self.insert_task_popup = insert_task_popup

        self.gcapture_windows = []

    def get_control_widgets(self):

        main_box = gtk.VBox()

        sw = gtk.ScrolledWindow()
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)

        types = tuple([gtk.gdk.Pixbuf] * (10))
        liststore = gtk.ListStore(*types)
        treeview = gtk.TreeView(liststore)
        treeview.connect('button_press_event', self.on_treeview_button_pressed)
        sw.add(treeview)

        main_box.pack_start(sw, expand=True, fill=True)

        self.t = DotUpdater(self.cfg, self.updater, treeview, self.info_bar,
                            self.usercfg)
        self.t.start()

        return main_box

    def on_treeview_button_pressed(self, treeview, event):
        # DISPLAY MENU ONLY ON RIGHT CLICK ONLY
        if event.button != 3:
            return False
        # the following sets selection to the position at which the
        # right click was done (otherwise selection lags behind the
        # right click):
        x = int(event.x)
        y = int(event.y)
        time = event.time
        pth = treeview.get_path_at_pos(x, y)

        if pth is None:
            return False

        path, col, cellx, celly = pth
        r_iter = treeview.get_model().get_iter(path)

        column_index = treeview.get_columns().index(col)
        if column_index == 0:
            return False

        if self.t.is_transposed:
            ctime = self.t.led_headings[column_index]
            name = treeview.get_model().get_value(r_iter, 0)
        else:
            name = self.t.led_headings[column_index]
            ctime_column = treeview.get_model().get_n_columns() - 1
            ctime = treeview.get_model().get_value(r_iter, ctime_column)

        task_id = name + TaskID.DELIM + ctime

        is_fam = (name in self.t.descendants)

        menu = self.get_right_click_menu(task_id, task_is_family=is_fam)

        sep = gtk.SeparatorMenuItem()
        sep.show()
        menu.append(sep)

        toggle_item = gtk.CheckMenuItem('Toggle Hide Task Headings')
        toggle_item.set_active(self.t.should_hide_headings)
        menu.append(toggle_item)
        toggle_item.connect('toggled', self.toggle_headings)
        toggle_item.show()

        group_item = gtk.CheckMenuItem('Toggle Family Grouping')
        group_item.set_active(self.t.should_group_families)
        menu.append(group_item)
        group_item.connect('toggled', self.toggle_grouping)
        group_item.show()

        transpose_menu_item = gtk.CheckMenuItem('Toggle _Transpose View')
        transpose_menu_item.set_active(self.t.should_transpose_view)
        menu.append(transpose_menu_item)
        transpose_menu_item.connect('toggled', self.toggle_transpose)
        transpose_menu_item.show()

        menu.popup(None, None, None, event.button, event.time)

        # TODO - popup menus are not automatically destroyed and can be
        # reused if saved; however, we need to reconstruct or at least
        # alter ours dynamically => should destroy after each use to
        # prevent a memory leak? But I'm not sure how to do this as yet.)

        return True

    def check_filter_entry(self, e):
        ftxt = self.filter_entry.get_text()
        self.t.filter = self.filter_entry.get_text()
        self.t.action_required = True

    def toggle_grouping(self, toggle_item):
        """Toggle grouping by visualisation families."""
        group_on = toggle_item.get_active()
        if group_on == self.t.should_group_families:
            return False
        if group_on:
            if "dot" in self.cfg.ungrouped_views:
                self.cfg.ungrouped_views.remove("dot")
        elif "dot" not in self.cfg.ungrouped_views:
            self.cfg.ungrouped_views.append("dot")
        self.t.should_group_families = group_on
        if isinstance(toggle_item, gtk.ToggleToolButton):
            if group_on:
                tip_text = "Dot View - Click to ungroup families"
            else:
                tip_text = "Dot View - Click to group tasks by families"
            self._set_tooltip(toggle_item, tip_text)
            self.group_menu_item.set_active(group_on)
        else:
            if toggle_item != self.group_menu_item:
                self.group_menu_item.set_active(group_on)
            self.group_toolbutton.set_active(group_on)
        self.t.action_required = True
        return False

    def toggle_headings(self, toggle_item):
        headings_off = toggle_item.get_active()
        if headings_off == self.t.should_hide_headings:
            return False
        self.t.should_hide_headings = headings_off
        if toggle_item != self.headings_menu_item:
            self.headings_menu_item.set_active(headings_off)
        self.t.action_required = True

    def toggle_transpose(self, toggle_item):
        """Toggle transpose (rows-as-columns, etc) table view."""
        transpose_on = toggle_item.get_active()
        if transpose_on == self.t.should_transpose_view:
            return False
        self.t.should_transpose_view = transpose_on
        if toggle_item != self.transpose_menu_item:
            self.transpose_menu_item.set_active(transpose_on)
        self.t.action_required = True
        return False

    def stop(self):
        self.t.quit = True

    def on_popup_quit(self, b, lv, w):
        lv.quit()
        self.quitters.remove(lv)
        w.destroy()

    def _set_tooltip(self, widget, tip_text):
        # Convenience function to add hover over text to a widget.
        tip = gtk.Tooltips()
        tip.enable()
        tip.set_tip(widget, tip_text)

    def get_menuitems(self):
        """Return the menuitems specific to this view."""
        items = []
        self.headings_menu_item = gtk.CheckMenuItem(
            'Toggle _Hide Task Headings')
        self.headings_menu_item.set_active(self.t.should_hide_headings)
        items.append(self.headings_menu_item)
        self.headings_menu_item.show()
        self.headings_menu_item.connect('toggled', self.toggle_headings)

        self.group_menu_item = gtk.CheckMenuItem('Toggle _Family Grouping')
        self.group_menu_item.set_active(self.t.should_group_families)
        items.append(self.group_menu_item)
        self.group_menu_item.connect('toggled', self.toggle_grouping)

        self.transpose_menu_item = gtk.CheckMenuItem('Toggle _Transpose View')
        self.transpose_menu_item.set_active(self.t.should_transpose_view)
        items.append(self.transpose_menu_item)
        self.transpose_menu_item.connect('toggled', self.toggle_transpose)
        return items

    def get_toolitems(self):
        """Return the tool bar items specific to this view."""
        items = []

        self.group_toolbutton = gtk.ToggleToolButton()
        self.group_toolbutton.set_active(self.t.should_group_families)
        g_image = gtk.image_new_from_stock('group',
                                           gtk.ICON_SIZE_SMALL_TOOLBAR)
        self.group_toolbutton.set_icon_widget(g_image)
        self.group_toolbutton.connect('toggled', self.toggle_grouping)
        self._set_tooltip(self.group_toolbutton,
                          "Dot View - Click to group tasks by families")
        items.append(self.group_toolbutton)

        self.filter_entry = EntryTempText()
        self.filter_entry.set_width_chars(7)  # Reduce width in toolbar
        self.filter_entry.connect("activate", self.check_filter_entry)
        self.filter_entry.set_temp_text("filter")
        filter_toolitem = gtk.ToolItem()
        filter_toolitem.add(self.filter_entry)
        tooltip = gtk.Tooltips()
        tooltip.enable()
        tooltip.set_tip(
            filter_toolitem,
            "Dot View - Filter tasks by name\n(enter a sub-string or regex)")
        items.append(filter_toolitem)

        return items
示例#3
0
    def __init__(self, parent, db, db_owner, tmpdir, pyro_timeout ):

        self.db = db
        self.db_owner = db_owner
        if pyro_timeout:
            self.pyro_timeout = float(pyro_timeout)
        else:
            self.pyro_timeout = None

        self.regname = None

        self.updater = None
        self.tmpdir = tmpdir
        self.gcapture_windows = []

        gobject.threads_init()

        #self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window = gtk.Dialog( "Choose a suite", parent, gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK))
        #self.window.set_modal(True)
        self.window.set_title("Suite Chooser" )
        self.window.set_size_request(750, 400)
        self.window.set_icon(get_icon()) # TODO: not needed for a dialog window?
        #self.window.set_border_width( 5 )

        self.window.connect("delete_event", self.delete_all_event)

        sw = gtk.ScrolledWindow()
        sw.set_policy( gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC )

        self.regd_treeview = gtk.TreeView()
        self.regd_treestore = gtk.TreeStore( str, str, str, str, str, str, str )
        self.regd_treeview.set_model(self.regd_treestore)
        self.regd_treeview.set_rules_hint(True)
        # search column zero (Ctrl-F)
        self.regd_treeview.connect( 'key_press_event', self.on_suite_select )
        self.regd_treeview.connect( 'button_press_event', self.on_suite_select )
        self.regd_treeview.set_search_column(0)

        # Start updating the liststore now, as we need values in it
        # immediately below (it may be possible to delay this till the
        # end of __init___() but it doesn't really matter.
        if self.db:
            self.dbopt = '--db='+self.db
        else:
            self.dbopt = ''

        regd_ts = self.regd_treeview.get_selection()
        regd_ts.set_mode( gtk.SELECTION_SINGLE )

        cr = gtk.CellRendererText()
        #cr.set_property( 'cell-background', '#def' )
        tvc = gtk.TreeViewColumn( 'Suite', cr, text=0, foreground=4, background=5 )
        tvc.set_resizable(True)
        tvc.set_sort_column_id(0)
        self.regd_treeview.append_column( tvc )

        cr = gtk.CellRendererText()
        tvc = gtk.TreeViewColumn( 'Port', cr, text=1, foreground=4, background=5 )
        tvc.set_resizable(True)
        # not sure how this sorting works
        #tvc.set_sort_column_id(1)
        self.regd_treeview.append_column( tvc )

        cr = gtk.CellRendererText()
        #cr.set_property( 'cell-background', '#def' )
        tvc = gtk.TreeViewColumn( 'Title', cr, markup=2, foreground=4, background=6 )
        tvc.set_resizable(True)
        #vc.set_sort_column_id(2)
        self.regd_treeview.append_column( tvc )

        cr = gtk.CellRendererText()
        tvc = gtk.TreeViewColumn( 'Location', cr, text=3, foreground=4, background=5 )
        tvc.set_resizable(True)
        #vc.set_sort_column_id(3)
        self.regd_treeview.append_column( tvc )

        vbox = self.window.vbox

        sw.add( self.regd_treeview )

        vbox.pack_start( sw, True )

        self.selected_label_text = '(double-click or OK to select; right-click for db options)'
        self.selected_label = gtk.Label( self.selected_label_text )

        filter_entry = EntryTempText()
        filter_entry.set_width_chars( 7 )  # Reduce width in toolbar
        filter_entry.connect( "activate", self.filter )
        filter_entry.set_temp_text( "filter" )
        filter_toolitem = gtk.ToolItem()
        filter_toolitem.add(filter_entry)
        tooltip = gtk.Tooltips()
        tooltip.enable()
        tooltip.set_tip(filter_toolitem, "Filter suites \n(enter a sub-string or regex)")

        expand_button = gtk.ToolButton()
        image = gtk.image_new_from_stock( gtk.STOCK_ADD, gtk.ICON_SIZE_SMALL_TOOLBAR )
        expand_button.set_icon_widget( image )
        expand_button.connect( 'clicked', lambda x: self.regd_treeview.expand_all() )

        collapse_button = gtk.ToolButton()
        image = gtk.image_new_from_stock( gtk.STOCK_REMOVE, gtk.ICON_SIZE_SMALL_TOOLBAR )
        collapse_button.set_icon_widget( image )
        collapse_button.connect( 'clicked', lambda x: self.regd_treeview.collapse_all() )

        hbox = gtk.HBox()

        eb = gtk.EventBox()
        eb.add( self.selected_label )
        eb.modify_bg( gtk.STATE_NORMAL, gtk.gdk.color_parse( '#bbc' ) )
        hbox.pack_start( eb, True )
        hbox.pack_start( expand_button, False )
        hbox.pack_start( collapse_button, False )
        hbox.pack_start (filter_toolitem, False)

        vbox.pack_start( hbox, False )

        self.window.show_all()

        self.start_updater()
示例#4
0
class ControlLED(object):
    """
LED suite control interface.
    """
    def __init__(self, cfg, usercfg, info_bar, get_right_click_menu, log_colors):

        self.cfg = cfg
        self.usercfg = usercfg
        self.info_bar = info_bar
        self.get_right_click_menu = get_right_click_menu
        self.log_colors = log_colors

        self.gcapture_windows = []

    def get_control_widgets( self ):

        main_box = gtk.VBox()

        sw = gtk.ScrolledWindow()
        sw.set_policy( gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC )

        types = tuple( [gtk.gdk.Pixbuf]* (10 ))
        liststore = gtk.ListStore(*types)
        treeview = gtk.TreeView( liststore )
        treeview.connect( 'button_press_event', self.on_treeview_button_pressed )
        sw.add( treeview )

        main_box.pack_start( sw, expand=True, fill=True )
        
        self.t = lupdater( self.cfg, treeview, self.info_bar, self.usercfg )
        self.t.start()

        return main_box

    def on_treeview_button_pressed( self, treeview, event ):
        # DISPLAY MENU ONLY ON RIGHT CLICK ONLY
        if event.button != 3:
            return False
        # the following sets selection to the position at which the
        # right click was done (otherwise selection lags behind the
        # right click):
        x = int( event.x )
        y = int( event.y )
        time = event.time
        pth = treeview.get_path_at_pos(x,y)

        if pth is None:
            return False

        path, col, cellx, celly = pth
        r_iter = treeview.get_model().get_iter( path )

        column_index = treeview.get_columns().index(col)
        if column_index == 0:
            return False
        name = self.t.task_list[column_index - 1]
        ctime_column = treeview.get_model().get_n_columns() - 1
        ctime = treeview.get_model().get_value( r_iter, ctime_column )

        task_id = name + TaskID.DELIM + ctime

        is_fam = (name in self.t.descendants)

        menu = self.get_right_click_menu( task_id, task_is_family=is_fam )

        sep = gtk.SeparatorMenuItem()
        sep.show()
        menu.append( sep )

        toggle_item = gtk.CheckMenuItem( 'Toggle Hide Task Headings' )
        toggle_item.set_active( self.t.should_hide_headings )
        menu.append( toggle_item )
        toggle_item.connect( 'toggled', self.toggle_headings )
        toggle_item.show()

        group_item = gtk.CheckMenuItem( 'Toggle Family Grouping' )
        group_item.set_active( self.t.should_group_families )
        menu.append( group_item )
        group_item.connect( 'toggled', self.toggle_grouping )
        group_item.show()
        
        menu.popup( None, None, None, event.button, event.time )

        # TO DO: popup menus are not automatically destroyed and can be
        # reused if saved; however, we need to reconstruct or at least
        # alter ours dynamically => should destroy after each use to
        # prevent a memory leak? But I'm not sure how to do this as yet.)

        return True

    def check_filter_entry( self, e ):
        ftxt = self.filter_entry.get_text()
        self.t.filter = self.filter_entry.get_text()
        self.t.update()
        self.t.update_gui()

    def toggle_grouping( self, toggle_item ):
        """Toggle grouping by visualisation families."""
        if isinstance( toggle_item, gtk.ToggleToolButton ):
            group_on = toggle_item.get_active()
            if group_on == self.t.should_group_families:
                return False
            self.t.should_group_families = group_on
            if group_on:
                tip_text = "Dot View - Click to ungroup families"
            else:
                tip_text = "Dot View - Click to group tasks by families"
            self._set_tooltip( toggle_item, tip_text )
            self.group_menu_item.set_active( group_on )
        else:
            group_on = toggle_item.get_active()
            if group_on == self.t.should_group_families:
                return False
            self.t.should_group_families = group_on
            if toggle_item != self.group_menu_item:
                self.group_menu_item.set_active( group_on )
            self.group_toolbutton.set_active( group_on )
        self.t.update()           
        self.t.update_gui()
        return False

    def toggle_headings(self, toggle_item):
        headings_off = toggle_item.get_active()
        if headings_off == self.t.should_hide_headings:
            return False
        self.t.should_hide_headings = headings_off
        if toggle_item != self.headings_menu_item:
            self.headings_menu_item.set_active( headings_off )
        self.t.set_led_headings()

    def stop(self):
        self.t.quit = True

    def on_popup_quit( self, b, lv, w ):
        lv.quit()
        self.quitters.remove( lv )
        w.destroy()

    def _set_tooltip( self, widget, tip_text ):
        # Convenience function to add hover over text to a widget.
        tip = gtk.Tooltips()
        tip.enable()
        tip.set_tip( widget, tip_text )

    def get_menuitems( self ):
        """Return the menuitems specific to this view."""
        items = []
        self.headings_menu_item = gtk.CheckMenuItem( 'Toggle _Hide Task Headings' )
        self.headings_menu_item.set_active( self.t.should_hide_headings )
        items.append( self.headings_menu_item )
        self.headings_menu_item.show()
        self.headings_menu_item.connect( 'toggled', self.toggle_headings )
        
        self.group_menu_item = gtk.CheckMenuItem( 'Toggle _Family Grouping' )
        self.group_menu_item.set_active( self.t.should_group_families )
        items.append( self.group_menu_item )
        self.group_menu_item.connect( 'toggled', self.toggle_grouping )
        return items

    def get_toolitems( self ):
        """Return the tool bar items specific to this view."""
        items = []

        self.group_toolbutton = gtk.ToggleToolButton()
        self.group_toolbutton.set_active( self.t.should_group_families )
        g_image = gtk.image_new_from_stock( 'group', gtk.ICON_SIZE_SMALL_TOOLBAR )
        self.group_toolbutton.set_icon_widget( g_image )
        self.group_toolbutton.connect( 'toggled', self.toggle_grouping )
        self._set_tooltip( self.group_toolbutton, "Dot View - Click to group tasks by families" )
        items.append( self.group_toolbutton )
        
        self.filter_entry = EntryTempText()
        self.filter_entry.set_width_chars( 7 )  # Reduce width in toolbar
        self.filter_entry.connect( "activate", self.check_filter_entry )
        self.filter_entry.set_temp_text( "filter" )
        filter_toolitem = gtk.ToolItem()
        filter_toolitem.add(self.filter_entry)
        tooltip = gtk.Tooltips()
        tooltip.enable()
        tooltip.set_tip(filter_toolitem, "Dot View - Filter tasks by name\n(enter a sub-string or regex)")
        items.append(filter_toolitem)

        return items
示例#5
0
class ControlLED(object):
    """
LED suite control interface.
    """
    def __init__(self, cfg, updater, theme, dot_size, info_bar, get_right_click_menu,
                 log_colors, insert_task_popup):

        self.cfg = cfg
        self.updater = updater
        self.theme = theme
        self.dot_size = dot_size
        self.info_bar = info_bar
        self.get_right_click_menu = get_right_click_menu
        self.log_colors = log_colors
        self.insert_task_popup = insert_task_popup

        self.gcapture_windows = []

    def get_control_widgets( self ):

        main_box = gtk.VBox()

        sw = gtk.ScrolledWindow()
        sw.set_policy( gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC )

        types = tuple( [gtk.gdk.Pixbuf]* (10 ))
        liststore = gtk.ListStore(*types)
        treeview = gtk.TreeView( liststore )
        treeview.connect( 'button_press_event', self.on_treeview_button_pressed )
        sw.add( treeview )

        main_box.pack_start( sw, expand=True, fill=True )

        self.t = DotUpdater(
                self.cfg, self.updater, treeview, self.info_bar, self.theme,
                self.dot_size
        )
        self.t.start()

        return main_box

    def on_treeview_button_pressed( self, treeview, event ):
        # DISPLAY MENU ONLY ON RIGHT CLICK ONLY
        if event.button != 3:
            return False
        # the following sets selection to the position at which the
        # right click was done (otherwise selection lags behind the
        # right click):
        x = int( event.x )
        y = int( event.y )
        time = event.time
        pth = treeview.get_path_at_pos(x,y)

        if pth is None:
            return False

        path, col, cellx, celly = pth
        r_iter = treeview.get_model().get_iter( path )

        column_index = treeview.get_columns().index(col)
        if column_index == 0:
            return False

        if self.t.is_transposed:
            point_string = self.t.led_headings[column_index]
            name = treeview.get_model().get_value( r_iter, 0 )
        else:
            name = self.t.led_headings[column_index]
            point_string_column = treeview.get_model().get_n_columns() - 1
            point_string = treeview.get_model().get_value(
                r_iter, point_string_column )

        task_id = cylc.TaskID.get( name, point_string )

        is_fam = (name in self.t.descendants)

        menu = self.get_right_click_menu( task_id, task_is_family=is_fam )

        sep = gtk.SeparatorMenuItem()
        sep.show()
        menu.append( sep )

        toggle_item = gtk.CheckMenuItem( 'Toggle Hide Task Headings' )
        toggle_item.set_active( self.t.should_hide_headings )
        menu.append( toggle_item )
        toggle_item.connect( 'toggled', self.toggle_headings )
        toggle_item.show()

        group_item = gtk.CheckMenuItem( 'Toggle Family Grouping' )
        group_item.set_active( self.t.should_group_families )
        menu.append( group_item )
        group_item.connect( 'toggled', self.toggle_grouping )
        group_item.show()

        transpose_menu_item = gtk.CheckMenuItem( 'Toggle _Transpose View' )
        transpose_menu_item.set_active( self.t.should_transpose_view )
        menu.append( transpose_menu_item )
        transpose_menu_item.connect( 'toggled', self.toggle_transpose )
        transpose_menu_item.show()

        if self.cfg.use_defn_order:
            defn_order_menu_item = gtk.CheckMenuItem( 'Toggle _Definition Order' )
            defn_order_menu_item.set_active( self.t.defn_order_on )
            menu.append( defn_order_menu_item )
            defn_order_menu_item.connect( 'toggled', self.toggle_defn_order )
            defn_order_menu_item.show()

        menu.popup( None, None, None, event.button, event.time )

        # TODO - popup menus are not automatically destroyed and can be
        # reused if saved; however, we need to reconstruct or at least
        # alter ours dynamically => should destroy after each use to
        # prevent a memory leak? But I'm not sure how to do this as yet.)

        return True

    def check_filter_entry( self, e ):
        ftext = self.filter_entry.get_text()
        try:
            re.compile(ftext)
        except re.error as exc:
            warning_dialog(
                'Bad filter regex: %s: error: %s' % (ftext, exc)).warn()
            self.t.filter = ""
        else:
            self.t.filter = ftext
        self.t.action_required = True

    def toggle_grouping( self, toggle_item ):
        """Toggle grouping by visualisation families."""
        group_on = toggle_item.get_active()
        if group_on == self.t.should_group_families:
            return False
        if group_on:
            if "dot" in self.cfg.ungrouped_views:
                self.cfg.ungrouped_views.remove("dot")
        elif "dot" not in self.cfg.ungrouped_views:
            self.cfg.ungrouped_views.append("dot")
        self.t.should_group_families = group_on
        if isinstance( toggle_item, gtk.ToggleToolButton ):
            if group_on:
                tip_text = "Dot View - Click to ungroup families"
            else:
                tip_text = "Dot View - Click to group tasks by families"
            self._set_tooltip( toggle_item, tip_text )
            self.group_menu_item.set_active( group_on )
        else:
            if toggle_item != self.group_menu_item:
                self.group_menu_item.set_active( group_on )
            self.group_toolbutton.set_active( group_on )
        self.t.action_required = True
        return False

    def toggle_headings(self, toggle_item):
        headings_off = toggle_item.get_active()
        if headings_off == self.t.should_hide_headings:
            return False
        self.t.should_hide_headings = headings_off
        if toggle_item != self.headings_menu_item:
            self.headings_menu_item.set_active( headings_off )
        self.t.action_required = True

    def toggle_transpose( self, toggle_item ):
        """Toggle transpose (rows-as-columns, etc) table view."""
        transpose_on = toggle_item.get_active()
        if transpose_on == self.t.should_transpose_view:
            return False
        self.t.should_transpose_view = transpose_on
        if toggle_item != self.transpose_menu_item:
            self.transpose_menu_item.set_active( transpose_on )
        self.t.action_required = True
        return False

    def toggle_defn_order( self, toggle_item ):
        """Toggle definition vs alphabetic ordering of namespaces"""
        defn_order_on = toggle_item.get_active()
        if defn_order_on == self.t.defn_order_on:
            return False
        self.t.defn_order_on = defn_order_on
        if toggle_item != self.defn_order_menu_item:
            self.defn_order_menu_item.set_active( defn_order_on )
        self.t.action_required = True
        return False

    def stop(self):
        self.t.quit = True

    def on_popup_quit( self, b, lv, w ):
        lv.quit()
        self.quitters.remove( lv )
        w.destroy()

    def _set_tooltip( self, widget, tip_text ):
        # Convenience function to add hover over text to a widget.
        tip = gtk.Tooltips()
        tip.enable()
        tip.set_tip( widget, tip_text )

    def get_menuitems( self ):
        """Return the menuitems specific to this view."""
        items = []
        self.headings_menu_item = gtk.CheckMenuItem( 'Toggle _Hide Task Headings' )
        self.headings_menu_item.set_active( self.t.should_hide_headings )
        items.append( self.headings_menu_item )
        self.headings_menu_item.show()
        self.headings_menu_item.connect( 'toggled', self.toggle_headings )

        self.group_menu_item = gtk.CheckMenuItem( 'Toggle _Family Grouping' )
        self.group_menu_item.set_active( self.t.should_group_families )
        items.append( self.group_menu_item )
        self.group_menu_item.connect( 'toggled', self.toggle_grouping )

        self.transpose_menu_item = gtk.CheckMenuItem( 'Toggle _Transpose View' )
        self.transpose_menu_item.set_active( self.t.should_transpose_view )
        items.append( self.transpose_menu_item )
        self.transpose_menu_item.connect( 'toggled', self.toggle_transpose )

        if self.cfg.use_defn_order:
            self.defn_order_menu_item = gtk.CheckMenuItem( 'Toggle _Definition Order' )
            self.defn_order_menu_item.set_active( self.t.defn_order_on )
            items.append( self.defn_order_menu_item )
            self.defn_order_menu_item.connect( 'toggled', self.toggle_defn_order )
 
        return items

    def get_toolitems( self ):
        """Return the tool bar items specific to this view."""
        items = []

        self.group_toolbutton = gtk.ToggleToolButton()
        self.group_toolbutton.set_active( self.t.should_group_families )
        g_image = gtk.image_new_from_stock( 'group', gtk.ICON_SIZE_SMALL_TOOLBAR )
        self.group_toolbutton.set_icon_widget( g_image )
        self.group_toolbutton.set_label( "Group" )
        self.group_toolbutton.connect( 'toggled', self.toggle_grouping )
        self._set_tooltip( self.group_toolbutton, "Dot View - Click to group tasks by families" )
        items.append( self.group_toolbutton )

        self.filter_entry = EntryTempText()
        self.filter_entry.set_width_chars( 7 )  # Reduce width in toolbar
        self.filter_entry.connect( "activate", self.check_filter_entry )
        self.filter_entry.set_temp_text( "filter" )
        filter_toolitem = gtk.ToolItem()
        filter_toolitem.add(self.filter_entry)
        tooltip = gtk.Tooltips()
        tooltip.enable()
        tooltip.set_tip(filter_toolitem, "Dot View - Filter tasks by name\n(enter a sub-string or regex)")
        items.append(filter_toolitem)

        return items
示例#6
0
class ControlTree(object):
    """
Text Treeview suite control interface.
    """
    def __init__(self, cfg, updater, theme, dot_size, info_bar,
                 get_right_click_menu, log_colors, insert_task_popup):

        self.cfg = cfg
        self.updater = updater
        self.theme = theme
        self.dot_size = dot_size
        self.info_bar = info_bar
        self.get_right_click_menu = get_right_click_menu
        self.log_colors = log_colors
        self.insert_task_popup = insert_task_popup

        self.gcapture_windows = []

        self.ttree_paths = {}  # Cache dict of tree paths & states, names.

    def get_control_widgets(self):
        main_box = gtk.VBox()
        main_box.pack_start(self.treeview_widgets(), expand=True, fill=True)

        self.tfilt = ''

        self.t = TreeUpdater(self.cfg, self.updater, self.ttreeview,
                             self.ttree_paths, self.info_bar, self.theme,
                             self.dot_size)
        self.t.start()
        return main_box

    def visible_cb(self, model, iter):
        # visibility result determined by state matching active check
        # buttons: set visible if model value NOT in filter_states;
        # and matching name against current name filter setting.
        # (state result: sres; name result: nres)

        point_string = model.get_value(iter, 0)
        name = model.get_value(iter, 1)
        if name is None or point_string is None:
            return True
        name = re.sub(r'<.*?>', '', name)

        if point_string == name:
            # Cycle-time line (not state etc.)
            return True

        # Task or family.
        state = model.get_value(iter, 2)
        if state is not None:
            state = re.sub(r'<.*?>', '', state)
        sres = state not in self.tfilter_states

        if not self.tfilt:
            nres = True
        elif self.tfilt in name:
            # tfilt is any substring of name
            nres = True
        elif re.search(self.tfilt, name):
            # full regex match
            nres = True
        else:
            nres = False

        if model.iter_has_child(iter):
            # Family.
            path = model.get_path(iter)

            sub_st = self.ttree_paths.get(path, {}).get('states', [])
            sres = sres or any([t not in self.tfilter_states for t in sub_st])

            if self.tfilt:
                sub_nm = self.ttree_paths.get(path, {}).get('names', [])
                nres = nres or any([self.tfilt in n for n in sub_nm])

        return sres and nres

    def check_tfilter_buttons(self, tb):
        del self.tfilter_states[:]
        for subbox in self.tfilterbox.get_children():
            for box in subbox.get_children():
                try:
                    icon, cb = box.get_children()
                except (ValueError, AttributeError):
                    # ValueError: a null entry to line things up.
                    # AttributeError: filter_entry box (handled below).
                    pass
                else:
                    if not cb.get_active():
                        # sub '_' from button label keyboard mnemonics
                        self.tfilter_states.append(
                            re.sub('_', '', cb.get_label()))
        self.tmodelfilter.refilter()

    def check_filter_entry(self, e):
        ftext = self.filter_entry.get_text()
        try:
            re.compile(ftext)
        except re.error as exc:
            warning_dialog("Bad filter regex: '%s': error: %s" %
                           (ftext, exc)).warn()
            self.tfilt = ""
        else:
            self.tfilt = ftext
        self.tmodelfilter.refilter()

    def toggle_grouping(self, toggle_item):
        """Toggle grouping by visualisation families."""
        group_on = toggle_item.get_active()
        if group_on == self.t.should_group_families:
            return False
        if group_on:
            if "text" in self.cfg.ungrouped_views:
                self.cfg.ungrouped_views.remove("text")
        elif "text" not in self.cfg.ungrouped_views:
            self.cfg.ungrouped_views.append("text")
        self.t.should_group_families = group_on
        if isinstance(toggle_item, gtk.ToggleToolButton):
            if group_on:
                tip_text = "Tree View - Click to ungroup families"
            else:
                tip_text = "Tree View - Click to group tasks by families"
            self._set_tooltip(toggle_item, tip_text)
            self.group_menu_item.set_active(group_on)
        else:
            if toggle_item != self.group_menu_item:
                self.group_menu_item.set_active(group_on)
            self.group_toolbutton.set_active(group_on)
        self.t.update_gui()
        return False

    def stop(self):
        self.t.quit = True

    def toggle_autoexpand(self, w):
        self.t.autoexpand = not self.t.autoexpand

    def treeview_widgets(self):
        # Treeview of current suite state, with filtering and sorting.
        # sorting is handled somewhat manually because the simple method
        # of interposing a TreeModelSort at the top:
        #   treestore = gtk.TreeStore(str, ...)
        #   tms = gtk.TreeModelSort( treestore )   #\
        #   tmf = tms.filter_new()                 #-- or other way round?
        #   tv = gtk.TreeView()
        #   tv.set_model(tms)
        # failed to produce correct results (the data displayed was not
        # consistently what should have been displayed given the
        # filtering in use) although the exact same code worked for a
        # liststore.

        self.sort_col_num = 0

        self.ttreestore = gtk.TreeStore(str, str, str, str, str, str, str, str,
                                        str, str, gtk.gdk.Pixbuf)
        self.tmodelfilter = self.ttreestore.filter_new()
        self.tmodelfilter.set_visible_func(self.visible_cb)
        self.tmodelsort = gtk.TreeModelSort(self.tmodelfilter)
        self.ttreeview = gtk.TreeView()
        self.ttreeview.set_rules_hint(True)
        self.ttreeview.set_model(self.tmodelsort)

        ts = self.ttreeview.get_selection()
        ts.set_mode(gtk.SELECTION_SINGLE)

        self.ttreeview.connect('button_press_event',
                               self.on_treeview_button_pressed)
        headings = [
            None, 'task', 'state', 'host', 'Job ID', 'T-submit', 'T-start',
            'T-finish', 'dT-mean', 'latest message'
        ]

        for n in range(1, len(headings)):
            # Skip first column (cycle point)
            tvc = gtk.TreeViewColumn(headings[n])
            if n == 1:
                crp = gtk.CellRendererPixbuf()
                tvc.pack_start(crp, False)
                tvc.set_attributes(crp, pixbuf=10)
            cr = gtk.CellRendererText()
            tvc.pack_start(cr, True)
            if n == 7:
                tvc.set_attributes(cr, markup=n)
            else:
                tvc.set_attributes(cr, text=n)
            tvc.set_resizable(True)
            tvc.set_clickable(True)
            self.ttreeview.append_column(tvc)
            tvc.set_sort_column_id(n - 1)
            self.tmodelsort.set_sort_func(n - 1, self.sort_column, n - 1)
        sw = gtk.ScrolledWindow()
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        sw.add(self.ttreeview)

        self.tfilterbox = gtk.VBox()
        subbox1 = gtk.HBox(homogeneous=True)
        subbox2 = gtk.HBox(homogeneous=True)
        self.tfilterbox.pack_start(subbox1)
        self.tfilterbox.pack_start(subbox2)

        self.tfilter_states = []

        dotm = DotMaker(self.theme, size='small')
        cnt = 0
        for st in task_state.legal:
            box = gtk.HBox()
            icon = dotm.get_image(st)
            cb = gtk.CheckButton(task_state.labels[st])
            tooltip = gtk.Tooltips()
            tooltip.enable()
            tooltip.set_tip(cb, "Filter by task state = %s" % st)

            box.pack_start(icon, expand=False)
            box.pack_start(cb, expand=False)
            cnt += 1
            if cnt > (len(task_state.legal) + 1) // 2:
                subbox2.pack_start(box, expand=False, fill=True)
            else:
                subbox1.pack_start(box, expand=False, fill=True)
            if st in self.tfilter_states:
                cb.set_active(False)
            else:
                cb.set_active(True)
            cb.connect('toggled', self.check_tfilter_buttons)

        self.filter_entry = EntryTempText()
        self.filter_entry.set_width_chars(7)
        self.filter_entry.connect("activate", self.check_filter_entry)
        self.filter_entry.set_temp_text("filter")
        tooltip = gtk.Tooltips()
        tooltip.enable()
        tooltip.set_tip(
            self.filter_entry, "Filter by task name.\n"
            "Enter a sub-string or regex and hit Enter\n"
            "(to reset, clear the entry and hit Enter)")
        subbox2.pack_start(self.filter_entry)
        cnt += 1

        if cnt % 2 != 0:
            # subbox2 needs another entry to line things up.
            subbox2.pack_start(gtk.HBox(), expand=False, fill=True)

        filter_hbox = gtk.HBox()
        filter_hbox.pack_start(self.tfilterbox, True, True, 10)
        vbox = gtk.VBox()
        vbox.pack_start(sw, True)
        vbox.pack_end(filter_hbox, False)

        return vbox

    def on_treeview_button_pressed(self, treeview, event):
        # DISPLAY MENU ONLY ON RIGHT CLICK ONLY
        if event.button != 3:
            return False

        # the following sets selection to the position at which the
        # right click was done (otherwise selection lags behind the
        # right click):
        x = int(event.x)
        y = int(event.y)
        time = event.time
        pth = treeview.get_path_at_pos(x, y)

        if pth is None:
            return False

        treeview.grab_focus()
        path, col, cellx, celly = pth
        treeview.set_cursor(path, col, 0)

        selection = treeview.get_selection()
        treemodel, iter = selection.get_selected()
        point_string = treemodel.get_value(iter, 0)
        name = treemodel.get_value(iter, 1)
        if point_string == name:
            # must have clicked on the top level point_string
            return

        task_id = cylc.TaskID.get(name, point_string)

        is_fam = (name in self.t.descendants)

        menu = self.get_right_click_menu(task_id, task_is_family=is_fam)

        sep = gtk.SeparatorMenuItem()
        sep.show()
        menu.append(sep)

        group_item = gtk.CheckMenuItem('Toggle Family Grouping')
        group_item.set_active(self.t.should_group_families)
        menu.append(group_item)
        group_item.connect('toggled', self.toggle_grouping)
        group_item.show()

        menu.popup(None, None, None, event.button, event.time)

        # TODO - popup menus are not automatically destroyed and can be
        # reused if saved; however, we need to reconstruct or at least
        # alter ours dynamically => should destroy after each use to
        # prevent a memory leak? But I'm not sure how to do this as yet.)

        return True

    def sort_column(self, model, iter1, iter2, col_num):
        cols = self.ttreeview.get_columns()
        point_string1 = model.get_value(iter1, 0)
        point_string2 = model.get_value(iter2, 0)
        if point_string1 != point_string2:
            # TODO ISO: worth a proper comparison here?
            if cols[col_num].get_sort_order() == gtk.SORT_DESCENDING:
                return cmp(point_string2, point_string1)
            return cmp(point_string1, point_string2)

        # Columns do not include the cycle point (0th col), so add 1.
        prop1 = model.get_value(iter1, col_num + 1)
        prop2 = model.get_value(iter2, col_num + 1)
        return cmp(prop1, prop2)

    def change_sort_order(self, col, event=None, n=0):
        if hasattr(event, "button") and event.button != 1:
            return False
        cols = self.ttreeview.get_columns()
        self.sort_col_num = n
        if cols[n].get_sort_order() == gtk.SORT_ASCENDING:
            cols[n].set_sort_order(gtk.SORT_DESCENDING)
        else:
            cols[n].set_sort_order(gtk.SORT_ASCENDING)
        return False

    def on_popup_quit(self, b, lv, w):
        lv.quit()
        self.quitters.remove(lv)
        w.destroy()

    def get_menuitems(self):
        """Return the menu items specific to this view."""
        items = []
        autoex_item = gtk.CheckMenuItem('Toggle _Auto-Expand Tree')
        autoex_item.set_active(self.t.autoexpand)
        items.append(autoex_item)
        autoex_item.connect('activate', self.toggle_autoexpand)

        self.group_menu_item = gtk.CheckMenuItem('Toggle _Family Grouping')
        self.group_menu_item.set_active(self.t.should_group_families)
        items.append(self.group_menu_item)
        self.group_menu_item.connect('toggled', self.toggle_grouping)
        return items

    def _set_tooltip(self, widget, tip_text):
        # Convenience function to add hover over text to a widget.
        tip = gtk.Tooltips()
        tip.enable()
        tip.set_tip(widget, tip_text)

    def get_toolitems(self):
        """Return the tool bar items specific to this view."""
        items = []

        expand_button = gtk.ToolButton()
        image = gtk.image_new_from_stock(gtk.STOCK_ADD,
                                         gtk.ICON_SIZE_SMALL_TOOLBAR)
        expand_button.set_icon_widget(image)
        expand_button.set_label("Expand")
        self._set_tooltip(expand_button, "Tree View - Expand all")
        expand_button.connect('clicked', lambda x: self.ttreeview.expand_all())
        items.append(expand_button)

        collapse_button = gtk.ToolButton()
        image = gtk.image_new_from_stock(gtk.STOCK_REMOVE,
                                         gtk.ICON_SIZE_SMALL_TOOLBAR)
        collapse_button.set_icon_widget(image)
        collapse_button.set_label("Collapse")
        collapse_button.connect('clicked',
                                lambda x: self.ttreeview.collapse_all())
        self._set_tooltip(collapse_button, "Tree View - Collapse all")
        items.append(collapse_button)

        self.group_toolbutton = gtk.ToggleToolButton()
        self.group_toolbutton.set_active(self.t.should_group_families)
        g_image = gtk.image_new_from_stock('group',
                                           gtk.ICON_SIZE_SMALL_TOOLBAR)
        self.group_toolbutton.set_icon_widget(g_image)
        self.group_toolbutton.set_label("Group")
        self.group_toolbutton.connect('toggled', self.toggle_grouping)
        self._set_tooltip(self.group_toolbutton,
                          "Tree View - Click to group tasks by families")
        items.append(self.group_toolbutton)

        return items
示例#7
0
class ControlTree(object):
    """
Text Treeview suite control interface.
    """
    def __init__(self, cfg, updater, usercfg, info_bar, get_right_click_menu,
                 log_colors, insert_task_popup ):

        self.cfg = cfg
        self.updater = updater
        self.usercfg = usercfg
        self.info_bar = info_bar
        self.get_right_click_menu = get_right_click_menu
        self.log_colors = log_colors
        self.insert_task_popup = insert_task_popup

        self.gcapture_windows = []

        self.ttree_paths = {}  # Cache dict of tree paths & states, names.

    def get_control_widgets( self ):
        main_box = gtk.VBox()
        main_box.pack_start( self.treeview_widgets(), expand=True, fill=True )
        
        self.tfilt = ''
        
        self.t = TreeUpdater( self.cfg, self.updater, self.ttreeview,
                              self.ttree_paths, self.info_bar, self.usercfg )
        self.t.start()
        return main_box

    def visible_cb(self, model, iter ):
        # visibility result determined by state matching active check
        # buttons: set visible if model value NOT in filter_states;
        # and matching name against current name filter setting.
        # (state result: sres; name result: nres)

        ctime = model.get_value(iter, 0 )
        name = model.get_value(iter, 1)
        if name is None or ctime is None:
            return True
        name = re.sub( r'<.*?>', '', name )

        if ctime == name:
            # Cycle-time line (not state etc.)
            return True

         # Task or family.
        state = model.get_value(iter, 2 ) 
        if state is not None:
            state = re.sub( r'<.*?>', '', state )
        sres = state not in self.tfilter_states

        try:
            if not self.tfilt:
                nres = True
            elif self.tfilt in name:
                # tfilt is any substring of name
                nres = True
            elif re.search( self.tfilt, name ):
                # full regex match
                nres = True
            else:
                nres = False
        except:
            warning_dialog( 'Bad filter regex? ' + self.tfilt ).warn()
            nres = False

        if model.iter_has_child( iter ):
            # Family.
            path = model.get_path( iter )

            sub_st = self.ttree_paths.get( path, {} ).get( 'states', [] )
            sres = sres or any([t not in self.tfilter_states for t in sub_st])

            if self.tfilt:
                sub_nm = self.ttree_paths.get( path, {} ).get( 'names', [] )
                nres = nres or any([self.tfilt in n for n in sub_nm])

        return sres and nres

    def check_tfilter_buttons(self, tb):
        del self.tfilter_states[:]
        for subbox in self.tfilterbox.get_children():
            for b in subbox.get_children():
                if not b.get_active():
                    # sub '_' from button label keyboard mnemonics
                    self.tfilter_states.append( re.sub('_', '', b.get_label()))
        self.tmodelfilter.refilter()

    def check_filter_entry( self, e ):
        ftxt = self.filter_entry.get_text()
        self.tfilt = self.filter_entry.get_text()
        self.tmodelfilter.refilter()

    def toggle_grouping( self, toggle_item ):
        """Toggle grouping by visualisation families."""
        group_on = toggle_item.get_active()
        if group_on == self.t.should_group_families:
            return False
        if group_on:
            if "text" in self.cfg.ungrouped_views:
                self.cfg.ungrouped_views.remove("text")
        elif "text" not in self.cfg.ungrouped_views:
            self.cfg.ungrouped_views.append("text")
        self.t.should_group_families = group_on
        if isinstance( toggle_item, gtk.ToggleToolButton ):
            if group_on:
                tip_text = "Tree View - Click to ungroup families"
            else:
                tip_text = "Tree View - Click to group tasks by families"
            self._set_tooltip( toggle_item, tip_text )
            self.group_menu_item.set_active( group_on )
        else:
            if toggle_item != self.group_menu_item:
                self.group_menu_item.set_active( group_on )
            self.group_toolbutton.set_active( group_on )            
        self.t.update_gui()
        return False

    def stop(self):
        self.t.quit = True

    def toggle_autoexpand( self, w ):
        self.t.autoexpand = not self.t.autoexpand

    def treeview_widgets( self ):
        # Treeview of current suite state, with filtering and sorting.
        # sorting is handled somewhat manually because the simple method 
        # of interposing a TreeModelSort at the top:
        #   treestore = gtk.TreeStore(str, ...)
        #   tms = gtk.TreeModelSort( treestore )   #\ 
        #   tmf = tms.filter_new()                 #-- or other way round?
        #   tv = gtk.TreeView()
        #   tv.set_model(tms)
        # failed to produce correct results (the data displayed was not 
        # consistently what should have been displayed given the
        # filtering in use) although the exact same code worked for a
        # liststore.

        self.sort_col_num = 0

        self.ttreestore = gtk.TreeStore(str, str, str, str, str, str, str, str, gtk.gdk.Pixbuf )
        self.tmodelfilter = self.ttreestore.filter_new()
        self.tmodelfilter.set_visible_func(self.visible_cb)
        self.tmodelsort = gtk.TreeModelSort(self.tmodelfilter)
        self.ttreeview = gtk.TreeView()
        self.ttreeview.set_model(self.tmodelsort)

        ts = self.ttreeview.get_selection()
        ts.set_mode( gtk.SELECTION_SINGLE )

        self.ttreeview.connect( 'button_press_event', self.on_treeview_button_pressed )
        headings = [ None, 'task', 'state', 'message', 'Tsubmit', 'Tstart', 'mean dT', 'ETC' ]
        bkgcols  = [ None, None,  '#def',  '#fff',    '#def',    '#fff',   '#def',    '#fff']

        for n in range(1, len(headings)):
            # Skip first column (cycle time)
            cr = gtk.CellRendererText()
            tvc = gtk.TreeViewColumn( headings[n] )
            cr.set_property( 'cell-background', bkgcols[n] )
            if n == 2:
                crp = gtk.CellRendererPixbuf()
                tvc.pack_start( crp, False )
                tvc.set_attributes( crp, pixbuf=8 )
            tvc.pack_start( cr, True )
            tvc.set_attributes( cr, text=n )
            tvc.set_resizable(True)
            tvc.set_clickable(True)
         #   tvc.connect("clicked", self.change_sort_order, n - 1 )
            self.ttreeview.append_column(tvc)
            tvc.set_sort_column_id( n - 1 )
            self.tmodelsort.set_sort_func( n - 1, self.sort_column, n - 1 )
        sw = gtk.ScrolledWindow()
        sw.set_policy( gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC )
        sw.add( self.ttreeview )

        self.tfilterbox = gtk.VBox()
        subbox1 = gtk.HBox(homogeneous=True)
        subbox2 = gtk.HBox(homogeneous=True)
        self.tfilterbox.pack_start(subbox1)
        self.tfilterbox.pack_start(subbox2)

        self.tfilter_states = []

        cnt = 0
        for st in task_state.legal:
            b = gtk.CheckButton( task_state.labels[st] )
            cnt += 1
            if cnt > len(task_state.legal)/2:
                subbox2.pack_start(b)
            else:
                subbox1.pack_start(b)
            if st in self.tfilter_states:
                b.set_active(False)
            else:
                b.set_active(True)
            b.connect('toggled', self.check_tfilter_buttons)

        ahbox = gtk.HBox()
        ahbox.pack_start( self.tfilterbox, True)

        vbox = gtk.VBox()
        vbox.pack_start( sw, True )
        vbox.pack_end( ahbox, False )

        return vbox

    def on_treeview_button_pressed( self, treeview, event ):
        # DISPLAY MENU ONLY ON RIGHT CLICK ONLY
        if event.button != 3:
            return False

        # the following sets selection to the position at which the
        # right click was done (otherwise selection lags behind the
        # right click):
        x = int( event.x )
        y = int( event.y )
        time = event.time
        pth = treeview.get_path_at_pos(x,y)

        if pth is None:
            return False

        treeview.grab_focus()
        path, col, cellx, celly = pth
        treeview.set_cursor( path, col, 0 )

        selection = treeview.get_selection()
        treemodel, iter = selection.get_selected()
        ctime = treemodel.get_value( iter, 0 )
        name = treemodel.get_value( iter, 1 )
        if ctime == name:
            # must have clicked on the top level ctime 
            return

        task_id = name + TaskID.DELIM + ctime

        is_fam = (name in self.t.descendants)

        menu = self.get_right_click_menu( task_id, task_is_family=is_fam )

        sep = gtk.SeparatorMenuItem()
        sep.show()
        menu.append( sep )

        group_item = gtk.CheckMenuItem( 'Toggle Family Grouping' )
        group_item.set_active( self.t.should_group_families )
        menu.append( group_item )
        group_item.connect( 'toggled', self.toggle_grouping )
        group_item.show()

        menu.popup( None, None, None, event.button, event.time )

        # TODO - popup menus are not automatically destroyed and can be
        # reused if saved; however, we need to reconstruct or at least
        # alter ours dynamically => should destroy after each use to
        # prevent a memory leak? But I'm not sure how to do this as yet.)

        return True

    def sort_column( self, model, iter1, iter2, col_num ):
        cols = self.ttreeview.get_columns()
        ctime1 = model.get_value( iter1 , 0 )
        ctime2 = model.get_value( iter2, 0 )
        if ctime1 != ctime2:
            if cols[col_num].get_sort_order() == gtk.SORT_DESCENDING:
                return cmp(ctime2, ctime1)
            return cmp(ctime1, ctime2)

        # Columns do not include the cycle time (0th col), so add 1.
        prop1 = model.get_value( iter1, col_num + 1 )
        prop2 = model.get_value( iter2, col_num + 1 )
        return cmp( prop1, prop2 )

    def change_sort_order( self, col, event=None, n=0 ):
        if hasattr(event, "button") and event.button != 1:
            return False
        cols = self.ttreeview.get_columns()
        self.sort_col_num = n
        if cols[n].get_sort_order() == gtk.SORT_ASCENDING:
            cols[n].set_sort_order( gtk.SORT_DESCENDING )
        else:
            cols[n].set_sort_order( gtk.SORT_ASCENDING )
        return False

    def on_popup_quit( self, b, lv, w ):
        lv.quit()
        self.quitters.remove( lv )
        w.destroy()

    def get_menuitems( self ):
        """Return the menu items specific to this view."""
        items = []
        autoex_item = gtk.CheckMenuItem( 'Toggle _Auto-Expand Tree' )
        autoex_item.set_active( self.t.autoexpand )
        items.append( autoex_item )
        autoex_item.connect( 'activate', self.toggle_autoexpand )

        self.group_menu_item = gtk.CheckMenuItem( 'Toggle _Family Grouping' )
        self.group_menu_item.set_active( self.t.should_group_families )
        items.append( self.group_menu_item )
        self.group_menu_item.connect( 'toggled', self.toggle_grouping )
        return items

    def _set_tooltip( self, widget, tip_text ):
        # Convenience function to add hover over text to a widget.
        tip = gtk.Tooltips()
        tip.enable()
        tip.set_tip( widget, tip_text )

    def get_toolitems( self ):
        """Return the tool bar items specific to this view."""
        items = []

        expand_button = gtk.ToolButton()
        image = gtk.image_new_from_stock( gtk.STOCK_ADD, gtk.ICON_SIZE_SMALL_TOOLBAR )
        expand_button.set_icon_widget( image )
        self._set_tooltip( expand_button, "Tree View - Expand all" )
        expand_button.connect( 'clicked', lambda x: self.ttreeview.expand_all() )
        items.append( expand_button )

        collapse_button = gtk.ToolButton()
        image = gtk.image_new_from_stock( gtk.STOCK_REMOVE, gtk.ICON_SIZE_SMALL_TOOLBAR )
        collapse_button.set_icon_widget( image )        
        collapse_button.connect( 'clicked', lambda x: self.ttreeview.collapse_all() )
        self._set_tooltip( collapse_button, "Tree View - Collapse all" )
        items.append( collapse_button )
     
        self.group_toolbutton = gtk.ToggleToolButton()
        self.group_toolbutton.set_active( self.t.should_group_families )
        g_image = gtk.image_new_from_stock( 'group', gtk.ICON_SIZE_SMALL_TOOLBAR )
        self.group_toolbutton.set_icon_widget( g_image )
        self.group_toolbutton.connect( 'toggled', self.toggle_grouping )
        self._set_tooltip( self.group_toolbutton, "Tree View - Click to group tasks by families" )
        items.append( self.group_toolbutton )

        self.filter_entry = EntryTempText()
        self.filter_entry.set_width_chars( 7 )  # Reduce width in toolbar
        self.filter_entry.connect( "activate", self.check_filter_entry )
        self.filter_entry.set_temp_text( "filter" )
        filter_toolitem = gtk.ToolItem()
        filter_toolitem.add(self.filter_entry)
        tooltip = gtk.Tooltips()
        tooltip.enable()
        tooltip.set_tip(filter_toolitem, "Tree View - Filter tasks by name\n(enter a sub-string or regex)")
        items.append(filter_toolitem)

        return items