Skip to contentSkip to conversation

Hob Notebook Implementation

PATCH Superseded Shane Wang 3 participants 10 messages Mar 12, 2012 – Mar 13, 2012

v2 of 4 Superseded Latest is v4 →

lore
Share & tools
lore

Shane Wang

author
#0 lore raw

v2 cover Hob Notebook Implementation

This patchset is to implement Hob self-defined notebook, and use it in the build details page.

The following changes since commit fe0659991b826eb3b4163743d29533fe5f91cb12:

libx11-trim/diet: Ensure libx11-locale doesn't trigger multiple provider issues (2012-03-12 02:28:03 +0000)

are available in the git repository at:
git://git.pokylinux.org/poky-contrib shane/hob2-v0.69
http://git.pokylinux.org/cgit.cgi/poky-contrib/log/?h=shane/hob2-v0.69

Shane Wang (3):
Hob: implement a self-defined notebook visual component for Hob
Hob: use HobNotebook to implement a notebook in build details page
Hob: show indicators on the tabs of the Hob notebook

 bitbake/lib/bb/ui/crumbs/builddetailspage.py     |   40 ++-
 bitbake/lib/bb/ui/crumbs/builder.py              |    5 +
 bitbake/lib/bb/ui/crumbs/hobcolor.py             |    1 +
 bitbake/lib/bb/ui/crumbs/hobwidget.py            |  482 ++++++++++++++++++----
 bitbake/lib/bb/ui/crumbs/packageselectionpage.py |   19 +-
 bitbake/lib/bb/ui/crumbs/recipeselectionpage.py  |   19 +-
 bitbake/lib/bb/ui/crumbs/runningbuild.py         |   69 +++
 7 files changed, 518 insertions(+), 117 deletions(-)
Signature · 1 line
1.7.6
author
#1 lore raw

PATCH v2 1/3 Hob: implement a self-defined notebook visual component for Hob

In recipe selection page, package selection page, and build details page, etc, there is a notebook component which is not gtk.Notebook in the design video. We implement the visual component with a drawing area, and use it to replace the old notebook in recipe selection page and package selection page. The reasons why we do it are:

1) General speaking, gtk.Notebook doesn't look like the designer worked out. (see https://wiki.yoctoproject.org/wiki/File:Hob1.2-screencast2.mov)
2) And the designer version looks better, for example, there is an indicator to show how many recipes or packages are included, and how many issues happened when building? Very straightforward. But technically, gtk.Notebook can't implement that, as far as we know.
3) Moreover, there is an entry for "search recipes", and "search packages". How to make it horizontal to the tabs is a problem to us.

Regarding those, we give up gtk.Notebook and use our own.

---
 bitbake/lib/bb/ui/crumbs/hobcolor.py             |    1 +
 bitbake/lib/bb/ui/crumbs/hobwidget.py            |  468 ++++++++++++++++++----
 bitbake/lib/bb/ui/crumbs/packageselectionpage.py |   19 +-
 bitbake/lib/bb/ui/crumbs/recipeselectionpage.py  |   19 +-
 4 files changed, 395 insertions(+), 112 deletions(-)
bitbake/lib/bb/ui/crumbs/hobcolor.py +1 -0
index 9d67d5c..402f022 100644
@@ -28,6 +28,7 @@ class HobColors:
     DARK         = "#3c3b37"
     BLACK        = "#000000"
     LIGHT_ORANGE = "#f7a787"
+    YELLOW       = "#ffff00"
 
     OK = WHITE
     RUNNING = PALE_GREEN
bitbake/lib/bb/ui/crumbs/hobwidget.py +383 -85

487 lines not on this page · raw message

bitbake/lib/bb/ui/crumbs/packageselectionpage.py +6 -13
index a8628ac..5459743 100755
@@ -23,7 +23,7 @@
 import gtk
 import glib
 from bb.ui.crumbs.hobcolor import HobColors
-from bb.ui.crumbs.hobwidget import HobViewBar, HobViewTable
+from bb.ui.crumbs.hobwidget import HobViewTable, HobNotebook
 from bb.ui.crumbs.hoblistmodel import PackageListModel
 from bb.ui.crumbs.hobpages import HobPage
 
@@ -102,11 +102,7 @@ class PackageSelectionPage (HobPage):
         self.pack_start(self.group_align, expand=True, fill=True)
 
         # set visiable members
-        self.grid = gtk.Table(10, 1, True)
-        self.grid.set_col_spacings(3)
-
-        self.ins = gtk.Notebook()
-        self.ins.set_show_tabs(False)
+        self.ins = HobNotebook()
         self.tables = [] # we need to modify table when the dialog is shown
         # append the tab
         for i in range(len(self.pages)):
@@ -128,19 +124,16 @@ class PackageSelectionPage (HobPage):
             self.ins.append_page(tab, label)
             self.tables.append(tab)
 
-        self.grid.attach(self.ins, 0, 1, 1, 10, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 1, 1)
-        # a black bar associated with the notebook
-        self.topbar = HobViewBar(self.ins) 
-        self.grid.attach(self.topbar, 0, 1, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 1, 1)
+        self.ins.set_entry("Search packages:")
         # set the search entry for each table
         for tab in self.tables:
-            tab.set_search_entry(0, self.topbar.search)
+            tab.set_search_entry(0, self.ins.search)
 
         # add all into the dialog
-        self.box_group_area.add(self.grid)
+        self.box_group_area.pack_start(self.ins, expand=True, fill=True)
 
         button_box = gtk.HBox(False, 5)
-        self.box_group_area.pack_start(button_box, expand=False, fill=False)
+        self.box_group_area.pack_end(button_box, expand=False, fill=False)
 
         self.build_image_button = gtk.Button()
         label = gtk.Label()
bitbake/lib/bb/ui/crumbs/recipeselectionpage.py +5 -14
index aea9c0c..8423714 100755
@@ -23,7 +23,7 @@
 import gtk
 import glib
 from bb.ui.crumbs.hobcolor import HobColors
-from bb.ui.crumbs.hobwidget import HobViewBar, HobViewTable
+from bb.ui.crumbs.hobwidget import HobViewTable, HobNotebook
 from bb.ui.crumbs.hoblistmodel import RecipeListModel
 from bb.ui.crumbs.hobpages import HobPage
 
@@ -124,13 +124,7 @@ class RecipeSelectionPage (HobPage):
         self.pack_start(self.group_align, expand=True, fill=True)
 
         # set visiable members
-        self.grid = gtk.Table(10, 1, True)
-        self.grid.set_col_spacings(3)
-
-        # draw the left part of the window
-        # a notebook
-        self.ins = gtk.Notebook()
-        self.ins.set_show_tabs(False)
+        self.ins = HobNotebook()
         self.tables = [] # we need modify table when the dialog is shown
         # append the tabs in order
         for i in range(len(self.pages)):
@@ -151,16 +145,13 @@ class RecipeSelectionPage (HobPage):
             self.ins.append_page(tab, label)
             self.tables.append(tab)
 
-        self.grid.attach(self.ins, 0, 1, 1, 10, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND)
-        # a black bar associated with the notebook
-        self.topbar = HobViewBar(self.ins)
-        self.grid.attach(self.topbar, 0, 1, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND)
+        self.ins.set_entry("Search recipes:")
         # set the search entry for each table
         for tab in self.tables:
-            tab.set_search_entry(0, self.topbar.search)
+            tab.set_search_entry(0, self.ins.search)
 
         # add all into the window
-        self.box_group_area.add(self.grid)
+        self.box_group_area.pack_start(self.ins, expand=True, fill=True)
 
         button_box = gtk.HBox(False, 5)
         self.box_group_area.pack_end(button_box, expand=False, fill=False)
Signature · 1 line
1.7.6
author
#2 lore raw

PATCH v2 2/3 Hob: use HobNotebook to implement a notebook in build details page

This patch is to use HobNotebook we defined to implement the notebook in the build details page.

---
 bitbake/lib/bb/ui/crumbs/builddetailspage.py |   30 ++++++++++--
 bitbake/lib/bb/ui/crumbs/runningbuild.py     |   65 ++++++++++++++++++++++++++
 2 files changed, 90 insertions(+), 5 deletions(-)
bitbake/lib/bb/ui/crumbs/builddetailspage.py +25 -5
index 941f1e3..8ebcf08 100755
@@ -22,8 +22,10 @@
 
 import gtk
 from bb.ui.crumbs.progressbar import HobProgressBar
-from bb.ui.crumbs.hobwidget import hic
+from bb.ui.crumbs.hobwidget import hic, HobNotebook
 from bb.ui.crumbs.runningbuild import RunningBuildTreeView
+from bb.ui.crumbs.runningbuild import BuildConfigurationTreeView
+from bb.ui.crumbs.runningbuild import BuildFailureTreeView
 from bb.ui.crumbs.hobpages import HobPage
 
 #
@@ -49,11 +51,29 @@ class BuildDetailsPage (HobPage):
         self.stop_button.connect("clicked", self.stop_button_clicked_cb)
         self.progress_box.pack_end(self.stop_button, expand=False, fill=False)
 
+        self.notebook = HobNotebook()
+        self.config_tv = BuildConfigurationTreeView()
+        self.config_model = self.builder.handler.build.model.config_model()
+        self.config_tv.set_model(self.config_model)
+        self.scrolled_view_config = gtk.ScrolledWindow ()
+        self.scrolled_view_config.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.scrolled_view_config.add(self.config_tv)
+        self.notebook.append_page(self.scrolled_view_config, gtk.Label("Build Configuration"))
+
+        self.failure_tv = BuildFailureTreeView()
+        self.failure_model = self.builder.handler.build.model.failure_model()
+        self.failure_tv.set_model(self.failure_model)
+        self.scrolled_view_failure = gtk.ScrolledWindow ()
+        self.scrolled_view_failure.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.scrolled_view_failure.add(self.failure_tv)
+        self.notebook.append_page(self.scrolled_view_failure, gtk.Label("Issues"))
+
         self.build_tv = RunningBuildTreeView(readonly=True)
         self.build_tv.set_model(self.builder.handler.build.model)
-        self.scrolled_view = gtk.ScrolledWindow ()
-        self.scrolled_view.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-        self.scrolled_view.add(self.build_tv)
+        self.scrolled_view_build = gtk.ScrolledWindow ()
+        self.scrolled_view_build.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.scrolled_view_build.add(self.build_tv)
+        self.notebook.append_page(self.scrolled_view_build, gtk.Label("Log"))
 
         self.button_box = gtk.HBox(False, 5)
         self.back_button = gtk.LinkButton("Go back to Image Configuration screen", "<< Back to image configuration")
@@ -86,7 +106,7 @@ class BuildDetailsPage (HobPage):
         self.progress_bar.reset()
         self.vbox.pack_start(self.progress_box, expand=False, fill=False)
 
-        self.vbox.pack_start(self.scrolled_view, expand=True, fill=True)
+        self.vbox.pack_start(self.notebook, expand=True, fill=True)
 
         self.box_group_area.pack_end(self.button_box, expand=False, fill=False)
         self.show_all()
bitbake/lib/bb/ui/crumbs/runningbuild.py +65 -0

80 lines not on this page · raw message

Signature · 1 line
1.7.6
author
#4 lore raw

PATCH v2 3/3 Hob: show indicators on the tabs of the Hob notebook

In reply to Shane Wang

This patch is to show the indicators (e.g., the number of the issues) in the build details page to highlight.

---
 bitbake/lib/bb/ui/crumbs/builddetailspage.py |   10 ++++++++++
 bitbake/lib/bb/ui/crumbs/builder.py          |    5 +++++
 bitbake/lib/bb/ui/crumbs/hobwidget.py        |   14 ++++++++++++++
 bitbake/lib/bb/ui/crumbs/runningbuild.py     |    4 ++++
 4 files changed, 33 insertions(+), 0 deletions(-)
bitbake/lib/bb/ui/crumbs/builddetailspage.py +10 -0
index 8ebcf08..4a9f658 100755
@@ -37,6 +37,8 @@ class BuildDetailsPage (HobPage):
     def __init__(self, builder):
         super(BuildDetailsPage, self).__init__(builder, "Building ...")
 
+        self.num_of_issues = 0
+
         # create visual elements
         self.create_visual_elements()
 
@@ -80,6 +82,14 @@ class BuildDetailsPage (HobPage):
         self.back_button.connect("clicked", self.back_button_clicked_cb)
         self.button_box.pack_start(self.back_button, expand=False, fill=False)
 
+    def show_issues(self):
+        self.num_of_issues += 1
+        self.notebook.show_indicator_icon("Issues", self.num_of_issues)
+
+    def reset_issues(self):
+        self.num_of_issues = 0
+        self.notebook.hide_indicator_icon("Issues")
+
     def _remove_all_widget(self):
         children = self.vbox.get_children() or []
         for child in children:
bitbake/lib/bb/ui/crumbs/builder.py +5 -0
index a905030..be8abd9 100755
@@ -212,6 +212,7 @@ class Builder(gtk.Window):
         self.handler.build.connect("build-succeeded",    self.handler_build_succeeded_cb)
         self.handler.build.connect("build-failed",       self.handler_build_failed_cb)
         self.handler.build.connect("task-started",       self.handler_task_started_cb)
+        self.handler.build.connect("log-error",          self.handler_build_failure_cb)
         self.handler.connect("generating-data",          self.handler_generating_data_cb)
         self.handler.connect("data-generated",           self.handler_data_generated_cb)
         self.handler.connect("command-succeeded",        self.handler_command_succeeded_cb)
@@ -518,6 +519,7 @@ class Builder(gtk.Window):
         elif self.current_step == self.PACKAGE_GENERATING:
             fraction = 0
         self.build_details_page.update_progress_bar("Build Started: ", fraction)
+        self.build_details_page.reset_issues()
 
     def build_succeeded(self):
         if self.current_step == self.FAST_IMAGE_GENERATING:
@@ -585,6 +587,9 @@ class Builder(gtk.Window):
                 fraction = 0.2 + 0.8 * fraction
         self.build_details_page.update_progress_bar(title + ": ", fraction)
 
+    def handler_build_failure_cb(self, running_build):
+        self.build_details_page.show_issues()
+
     def destroy_window_cb(self, widget, event):
         lbl = "<b>Do you really want to exit the Hob image creator?</b>"
         dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
bitbake/lib/bb/ui/crumbs/hobwidget.py +14 -0
index 9af19f6..d1557a8 100644
@@ -619,6 +619,20 @@ class HobNotebook(gtk.VBox):
  
         self.tb.show()
 
+    def show_indicator_icon(self, title, number):
+        for i in range(len(self.tabbar.children)):
+            if self.tabbar.children[i]["toggled_page"] == -1:
+                continue
+            if self.tabbar.children[i]["title"] == title:
+                self.tabbar.show_indicator_icon(i, number)
+
+    def hide_indicator_icon(self, title):
+        for i in range(len(self.tabbar.children)):
+            if self.tabbar.children[i]["toggled_page"] == -1:
+                continue
+            if self.tabbar.children[i]["title"] == title:
+                self.tabbar.hide_indicator_icon(i)
+
     def tab_switched_cb(self, widget, page):
         self.notebook.set_current_page(page)
 
bitbake/lib/bb/ui/crumbs/runningbuild.py +4 -0
index eedd8d9..ddac232 100644
@@ -84,6 +84,9 @@ class RunningBuild (gobject.GObject):
           'task-started'   : (gobject.SIGNAL_RUN_LAST,
                               gobject.TYPE_NONE,
                               (gobject.TYPE_PYOBJECT,)),
+          'log-error'      : (gobject.SIGNAL_RUN_LAST,
+                              gobject.TYPE_NONE,
+                              ()),
           }
     pids_to_task = {}
     tasks_to_iter = {}
@@ -134,6 +137,7 @@ class RunningBuild (gobject.GObject):
             if event.levelno >= logging.ERROR:
                 icon = "dialog-error"
                 color = HobColors.ERROR
+                self.emit("log-error")
             elif event.levelno >= logging.WARNING:
                 icon = "dialog-warning"
                 color = HobColors.WARNING
Signature · 1 line
1.7.6
#6 lore raw

Re: [PATCH 1/3] Hob: implement a self-defined notebook visual component for Hob

In reply to Shane Wang
Quoted 9 lines · Shane Wang

In recipe selection page, package selection page, and build details page, etc, there is a notebook component which is not gtk.Notebook in the design video. We implement the visual component with a drawing area, and use it to replace the old notebook in recipe selection page and package selection page. The reasons why we do it are:

1) General speaking, gtk.Notebook doesn't look like the designer worked out. (see https://wiki.yoctoproject.org/wiki/File:Hob1.2-screencast2.mov)
2) And the designer version looks better, for example, there is an indicator to show how many recipes or packages are included, and how many issues happened when building? Very straightforward. But technically, gtk.Notebook can't implement that, as far as we know.
3) Moreover, there is an entry for "search recipes", and "search packages". How to make it horizontal to the tabs is a problem to us.

Regarding those, we give up gtk.Notebook and use our own.

I have some relatively minor concerns with this patch which have been noted before but I'll repeat here.

1) We're doing the range dance rather than just doing the Pythonic thing and iterating over the sequence with "for x in y".
http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#use-in-where-possible-1

I understand its desirable to have the index in some places, but where it isn't we should use the standard pattern. This is good coding practice.

2) The implementation matches the look of the interaction design, not the visual design. We need to start more closely matching the look of the application with the visual design:

https://wiki.yoctoproject.org/wiki/Hob2_%28Hob_v2%29#Visual_design_proposal_for_Hob_1.2

3) I still don't know why we get a flash of a green border around tabs when we select them?

4) Clicking the tabs doesn't change the focus, so when I have selected in the search entry and click a tab the search entry still has input-focus and shows the blinking cursor rather than the alternative text.

More comments and questions on the code below.

Quoted 191 lines
---
  bitbake/lib/bb/ui/crumbs/hobcolor.py             |    1 +
  bitbake/lib/bb/ui/crumbs/hobwidget.py            |  468 ++++++++++++++++++----
  bitbake/lib/bb/ui/crumbs/packageselectionpage.py |   19 +-
  bitbake/lib/bb/ui/crumbs/recipeselectionpage.py  |   19 +-
  4 files changed, 395 insertions(+), 112 deletions(-)
bitbake/lib/bb/ui/crumbs/hobcolor.py +1 -0
index 9d67d5c..402f022 100644
@@ -28,6 +28,7 @@ class HobColors:
      DARK         = "#3c3b37"
      BLACK        = "#000000"
      LIGHT_ORANGE = "#f7a787"
+    YELLOW       = "#ffff00"
 
      OK = WHITE
      RUNNING = PALE_GREEN
bitbake/lib/bb/ui/crumbs/hobwidget.py +58 -85
index f4ff1dc..9af19f6 100644
@@ -17,11 +17,14 @@
  # You should have received a copy of the GNU General Public License along
  # with this program; if not, write to the Free Software Foundation, Inc.,
  # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
  import gtk
  import gobject
  import os
  import os.path
+import sys
+import pango, pangocairo
+import math
+
  from bb.ui.crumbs.hobcolor import HobColors
  from bb.ui.crumbs.persistenttooltip import PersistentTooltip
 
@@ -168,90 +171,6 @@ class HobViewTable (gtk.VBox):
          if not view_column.get_title() in self.toggle_columns:
              self.emit("row-activated", tree.get_model(), path)
 
-class HobViewBar (gtk.EventBox):
-    """
-    A EventBox with the specified gray background color is associated with a notebook.
-    And the toolbar to simulate the tabs.
-    """
-
-    def __init__(self, notebook):
-        if not notebook:
-            return
-        self.notebook = notebook
-
-        # setup an event box
-        gtk.EventBox.__init__(self)
-        self.set_border_width(2)
-        style = self.get_style().copy()
-        style.bg[gtk.STATE_NORMAL] = self.get_colormap().alloc_color (HobColors.GRAY, False, False)
-        self.set_style(style)
-
-        hbox = gtk.HBox()
-        self.add(hbox)
-
-        # setup a tool bar in the event box
-        self.toolbar = gtk.Toolbar()
-        self.toolbar.set_orientation(gtk.ORIENTATION_HORIZONTAL)
-        self.toolbar.set_style(gtk.TOOLBAR_TEXT)
-        self.toolbar.set_border_width(5)
-
-        self.toolbuttons = []
-        for index in range(self.notebook.get_n_pages()):
-            child = self.notebook.get_nth_page(index)
-            label = self.notebook.get_tab_label_text(child)
-            tip_text = 'switch to ' + label + ' page'
-            toolbutton = self.toolbar.append_element(gtk.TOOLBAR_CHILD_RADIOBUTTON, None,
-                                label, tip_text, "Private text", None,
-                                self.toolbutton_cb, index)
-            toolbutton.set_size_request(200, 100)
-            self.toolbuttons.append(toolbutton)
-
-        # set the default current page
-        self.modify_toolbuttons_bg(0)
-        self.notebook.set_current_page(0)
-
-        self.toolbar.append_space()
-
-        # add the tool bar into the event box
-        hbox.pack_start(self.toolbar, expand=False, fill=False)
-
-        self.search = gtk.Entry()
-        self.align = gtk.Alignment(xalign=0.5, yalign=0.5)
-        self.align.add(self.search)
-        hbox.pack_end(self.align, expand=False, fill=False)
-
-        self.label = gtk.Label(" Search: ")
-        self.label.set_alignment(0.5, 0.5)
-        hbox.pack_end(self.label, expand=False, fill=False)
-
-    def toolbutton_cb(self, widget, index):
-        if index>= self.notebook.get_n_pages():
-            return
-        self.notebook.set_current_page(index)
-        self.modify_toolbuttons_bg(index)
-
-    def modify_toolbuttons_bg(self, index):
-        if index>= len(self.toolbuttons):
-            return
-        for i in range(0, len(self.toolbuttons)):
-            toolbutton = self.toolbuttons[i]
-            if i == index:
-                self.modify_toolbutton_bg(toolbutton, True)
-            else:
-                self.modify_toolbutton_bg(toolbutton)
-
-    def modify_toolbutton_bg(self, toolbutton, active=False):
-        if active:
-            toolbutton.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(HobColors.WHITE))
-            toolbutton.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.Color(HobColors.WHITE))
-            toolbutton.modify_bg(gtk.STATE_SELECTED, gtk.gdk.Color(HobColors.WHITE))
-            toolbutton.modify_bg(gtk.STATE_PRELIGHT, gtk.gdk.Color(HobColors.WHITE))
-        else:
-            toolbutton.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(HobColors.GRAY))
-            toolbutton.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.Color(HobColors.GRAY))
-            toolbutton.modify_bg(gtk.STATE_SELECTED, gtk.gdk.Color(HobColors.GRAY))
-            toolbutton.modify_bg(gtk.STATE_PRELIGHT, gtk.gdk.Color(HobColors.GRAY))
-
  class HobXpmLabelButtonBox(gtk.EventBox):
      """ label: name of buttonbox
          description: the simple  description
@@ -360,3 +279,382 @@ class HobInfoButton(gtk.EventBox):
      """
      def mouse_out_cb(self, widget, event):
          self.image.set_from_file(hic.ICON_INFO_DISPLAY_FILE)
+
+class HobTabBar(gtk.DrawingArea):
+    __gsignals__ = {
+        "blank-area-changed" : (gobject.SIGNAL_RUN_LAST,
+                                gobject.TYPE_NONE,
+                               (gobject.TYPE_INT,
+                                gobject.TYPE_INT,
+                                gobject.TYPE_INT,
+                                gobject.TYPE_INT,)),
+
+        "tab-switched" : (gobject.SIGNAL_RUN_LAST,
+                          gobject.TYPE_NONE,
+                         (gobject.TYPE_INT,)),
+    }
+
+    def __init__(self):
+        gtk.DrawingArea.__init__(self)
+        self.children = []
+
+        self.tab_width = 140
+        self.tab_height = 52
+        self.tab_x = 10
+        self.tab_y = 0
+
+        self.width = 500
+        self.height = 53
+        self.tab_w_ratio = 140 * 1.0/500
+        self.tab_h_ratio = 52 * 1.0/53
+        self.set_size_request(self.width, self.height)
+
+        self.current_child = 0
+        self.font = self.get_style().font_desc
+        self.font.set_size(pango.SCALE * 13)
+        self.update_children_text_layout_and_bg_color()
+
+        self.blank_rectangle = None
+        self.tab_pressed = False
+
+        self.set_property('can-focus', True)
+        self.set_events(gtk.gdk.EXPOSURE_MASK | gtk.gdk.POINTER_MOTION_MASK |
+                        gtk.gdk.BUTTON1_MOTION_MASK | gtk.gdk.BUTTON_PRESS_MASK |
+                        gtk.gdk.BUTTON_RELEASE_MASK)
+
+        self.connect("expose-event", self.on_draw)
+        self.connect("button-press-event", self.button_pressed_cb)
+        self.connect("button-release-event", self.button_released_cb)
+        self.show_all()
+
+    def button_released_cb(self, widget, event):
+        self.tab_pressed = False
+        self.queue_draw()
+
+    def button_pressed_cb(self, widget, event):
+        if event.type == gtk.gdk._2BUTTON_PRESS:

bitbake/lib/bb/ui/crumbs/hobwidget.py class HobInfoButton(gtk.EventBox): · line 345

Quoted 10 lines
bitbake/lib/bb/ui/crumbs/hobwidget.py +10 -0
+            return
+
+        result = False
+        if self.is_focus() or event.type == gtk.gdk.BUTTON_PRESS:
+            x, y = event.get_coords()
+            # check which tab be clicked
+            for i in range(len(self.children)):
+               if      (self.children[i]["x"]<  x) and (x<  self.children[i]["x"] + self.tab_width) \
+                   and (self.children[i]["y"]<  y) and (y<  self.children[i]["y"] + self.tab_height):
+                   self.current_child = i

I can see why you're using range() here

bitbake/lib/bb/ui/crumbs/hobwidget.py class HobInfoButton(gtk.EventBox): · line 353

Quoted 8 lines
bitbake/lib/bb/ui/crumbs/hobwidget.py +8 -0
+                   result = True
+                   break
+
+            # check the blank area is focus in or not
+            if (self.blank_rectangle) and (self.blank_rectangle.x>  0) and (self.blank_rectangle.y>  0):
+                if      (self.blank_rectangle.x<  x) and (x<  self.blank_rectangle.x + self.blank_rectangle.width) \
+                    and (self.blank_rectangle.y<  y) and (y<  self.blank_rectangle.y + self.blank_rectangle.height):
+                   self.grab_focus()

What's the blank rectangle? Why does it need focus?

Quoted 4 lines
bitbake/lib/bb/ui/crumbs/hobwidget.py +4 -0
+
+        if result == True:
+            page = self.children[self.current_child]["toggled_page"]
+            self.emit("tab-switched", page)

bitbake/lib/bb/ui/crumbs/hobwidget.py class HobInfoButton(gtk.EventBox): · line 367

Quoted 10 lines
bitbake/lib/bb/ui/crumbs/hobwidget.py +10 -0
+            self.tab_pressed = True
+            self.queue_draw()
+
+    def update_children_size(self):
+        # calculate the size of tabs
+        self.tab_width = int(self.width * self.tab_w_ratio)
+        self.tab_height = int(self.height * self.tab_h_ratio)
+        for i in range(len(self.children)):
+            self.children[i]["x"] = self.tab_x + i * self.tab_width
+            self.children[i]["y"] = self.tab_y

Please use standard Python iterators here.

Quoted 5 lines
bitbake/lib/bb/ui/crumbs/hobwidget.py +5 -0
+
+        if self.blank_rectangle != None:
+            self.resize_blank_rectangle()
+
+    def resize_blank_rectangle(self):

bitbake/lib/bb/ui/crumbs/hobwidget.py class HobInfoButton(gtk.EventBox): · line 382

Quoted 10 lines
bitbake/lib/bb/ui/crumbs/hobwidget.py +10 -0
+        width = self.width - self.tab_width * len(self.children) - self.tab_x
+        x = self.tab_x + self.tab_width * len(self.children)
+        hpadding = vpadding = 5
+        self.blank_rectangle = self.set_blank_size(x + hpadding, self.tab_y + vpadding,
+            width - 2 * hpadding, self.tab_height - 2 * vpadding)
+
+    def update_children_text_layout_and_bg_color(self):
+        style = self.get_style().copy()
+        color = style.base[gtk.STATE_NORMAL]
+        for i in range(len(self.children)):

Python iterators, please.

Quoted 40 lines
bitbake/lib/bb/ui/crumbs/hobwidget.py +40 -0
+            pangolayout = self.create_pango_layout(self.children[i]["title"])
+            pangolayout.set_font_description(self.font)
+            self.children[i]["title_layout"] = pangolayout
+            self.children[i]["r"] = color.red
+            self.children[i]["g"] = color.green
+            self.children[i]["b"] = color.blue
+
+    def append_tab_child(self, title, page):
+        num = len(self.children) + 1
+        self.tab_width = self.tab_width * len(self.children) / num
+
+        i = 0
+        for child in self.children:
+            child["x"] = self.tab_x + i * self.tab_width
+            i += 1
+
+        x = self.tab_x + i * self.tab_width
+        y = self.tab_y
+        pangolayout = self.create_pango_layout(title)
+        pangolayout.set_font_description(self.font)
+        color = self.style.base[gtk.STATE_NORMAL]
+        new_one = {
+            "x" : x,
+            "y" : y,
+            "r" : color.red,
+            "g" : color.green,
+            "b" : color.blue,
+            "title_layout" : pangolayout,
+            "toggled_page" : page,
+            "title"        : title,
+            "indicator_show"   : False,
+            "indicator_number" : 0,
+        }
+        self.children.append(new_one)
+
+    def on_draw(self, widget, event):
+        cr = widget.window.cairo_create()
+
+        self.width = self.allocation.width
+        self.height = self.allocation.height

bitbake/lib/bb/ui/crumbs/hobwidget.py class HobInfoButton(gtk.EventBox): · line 432

Quoted 10 lines
bitbake/lib/bb/ui/crumbs/hobwidget.py +10 -0
+
+        self.update_children_size()
+
+        self.draw_background(cr)
+        self.draw_toggled_tab(cr)
+        self.draw_tab_text(cr)
+
+        for i in range(len(self.children)):
+            if self.children[i]["indicator_show"] == True:
+                self.draw_indicator(cr, i)

This is the sort of use case where the range(len(list)) dance makes sense.

Though as draw_indicator is a method we've developed I imagine we could just pass the widget as the second argument? Just a thought really.

Quoted 14 lines
bitbake/lib/bb/ui/crumbs/hobwidget.py +14 -0
+
+    def draw_background(self, cr):
+        style = self.get_style()
+
+        if self.is_focus():
+            cr.set_source_color(style.base[gtk.STATE_SELECTED])
+        else:
+            cr.set_source_color(style.base[gtk.STATE_NORMAL])
+
+        y = 6
+        h = self.height - 6 - 1
+        gap = 1
+
+        w = self.children[0]["x"]

bitbake/lib/bb/ui/crumbs/hobwidget.py class HobInfoButton(gtk.EventBox): · line 456

Quoted 10 lines
bitbake/lib/bb/ui/crumbs/hobwidget.py +10 -0
+        cr.set_source_color(gtk.gdk.color_parse(HobColors.GRAY))
+        cr.rectangle(0, y, w - gap, h) # start rectangle
+        cr.fill()
+
+        cr.set_source_color(style.base[gtk.STATE_NORMAL])
+        cr.rectangle(w - gap, y, w, h) #first gap
+        cr.fill()
+
+        w = self.tab_width
+        for i in range(len(self.children)):

Let's fix this one too, please.

Quoted 6 lines
bitbake/lib/bb/ui/crumbs/hobwidget.py +6 -0
+            x = self.children[i]["x"]
+            cr.set_source_color(gtk.gdk.color_parse(HobColors.GRAY))
+            cr.rectangle(x, y, w - gap, h) # tab rectangle
+            cr.fill()
+            cr.set_source_color(style.base[gtk.STATE_NORMAL])
+            cr.rectangle(x + w - gap, y, w, h) # gap

bitbake/lib/bb/ui/crumbs/hobwidget.py class HobInfoButton(gtk.EventBox): · line 472

Quoted 10 lines
bitbake/lib/bb/ui/crumbs/hobwidget.py +10 -0
+            cr.fill()
+
+        cr.set_source_color(gtk.gdk.color_parse(HobColors.GRAY))
+        cr.rectangle(x + w, y, self.width - x - w, h) # last rectangle
+        cr.fill()
+
+    def draw_tab_text(self, cr):
+        style = self.get_style()
+
+        for i in range(len(self.children)):

And this one.

Quoted 99 lines
bitbake/lib/bb/ui/crumbs/hobwidget.py +99 -0
+            pangolayout = self.children[i]["title_layout"]
+            if pangolayout:
+                fontw, fonth = pangolayout.get_pixel_size()
+                # center pos
+                off_x = (self.tab_width - fontw) / 2
+                off_y = (self.tab_height - fonth) / 2
+                x = self.children[i]["x"] + off_x
+                y = self.children[i]["y"] + off_y
+                self.window.draw_layout(self.style.fg_gc[gtk.STATE_NORMAL], int(x), int(y), pangolayout)
+
+    def draw_toggled_tab(self, cr):
+        i = self.current_child
+        x = self.children[i]["x"]
+        y = self.children[i]["y"]
+        width = self.tab_width
+        height = self.tab_height
+        style = self.get_style()
+        color = style.base[gtk.STATE_NORMAL]
+
+        r = height / 10
+        if self.tab_pressed == True:
+            for xoff, yoff in [(1, 0), (2, 0)]:
+                cr.set_source_color(gtk.gdk.color_parse(HobColors.PALE_GREEN))
+                cr.move_to(x + r + xoff, y + yoff)
+                cr.line_to(x + width - r + xoff, y + yoff)
+                cr.arc(x + width - r+ xoff, y + r + yoff, r, 1.5*math.pi, 2*math.pi)
+                cr.move_to(x + width + xoff, r + yoff)
+                cr.line_to(x + width + xoff, y + height + yoff)
+                cr.line_to(x + xoff, y + height + yoff)
+                cr.line_to(x + xoff, r + yoff)
+                cr.arc(x + r + xoff, y + r + yoff, r, math.pi, 1.5*math.pi)
+                cr.stroke()
+            x = x + 2
+            y = y + 2
+        cr.set_source_rgba(color.red, color.green, color.blue, 1)
+        cr.move_to(x + r, y)
+        cr.line_to(x + width - r , y)
+        cr.arc(x + width - r, y + r, r, 1.5*math.pi, 2*math.pi)
+        cr.move_to(x + width, r)
+        cr.line_to(x + width, y + height)
+        cr.line_to(x, y + height)
+        cr.line_to(x, r)
+        cr.arc(x + r, y + r, r, math.pi, 1.5*math.pi)
+        cr.fill()
+
+    def draw_indicator(self, cr, i):
+        style = self.get_style()
+        tab_x = self.children[i]["x"]
+        tab_y = self.children[i]["y"]
+        number = self.children[i]["indicator_number"]
+        dest_w = int(32 * self.tab_w_ratio)
+        dest_h = int(32 * self.tab_h_ratio)
+        if dest_h<  self.tab_height:
+            dest_w = dest_h
+
+        # x position is offset(tab_width*3/4 - icon_width/2) + start_pos(tab_x)
+        x = tab_x + self.tab_width * 3/4 - dest_w/2
+        y = tab_y + self.tab_height/2 - dest_h/2
+        cr.move_to(tab_x, tab_y)
+        r = min(dest_w, dest_h)/2
+        color = cr.set_source_color(gtk.gdk.color_parse(HobColors.ORANGE))
+        cr.arc(x + r, y + r, r, 0, 2*math.pi)
+        cr.fill()
+
+        text = ("%d" % number)
+        layout = self.create_pango_layout(text)
+        layout.set_font_description(self.font)
+        textw, texth = layout.get_pixel_size()
+        x = x + (dest_w/2)-(textw/2)
+        y = y + (dest_h/2) - (texth/2)
+        cr.move_to(x, y)
+        self.window.draw_layout(self.style.fg_gc[gtk.STATE_NORMAL], int(x), int(y), layout)
+
+    def show_indicator_icon(self, i, number):
+        self.children[i]["indicator_show"] = True
+        self.children[i]["indicator_number"] = number
+        self.queue_draw()
+
+    def hide_indicator_icon(self, i):
+        self.children[i]["indicator_show"] = False
+        self.queue_draw()
+
+    def set_blank_size(self, x, y, w, h):
+        if self.blank_rectangle == None or self.blank_rectangle.x != x or self.blank_rectangle.width != w:
+            self.emit("blank-area-changed", x, y, w, h)
+
+        return gtk.gdk.Rectangle(x, y, w, h)
+
+class HobNotebook(gtk.VBox):
+
+    def __init__(self):
+        gtk.VBox.__init__(self, False, 0)
+
+        self.notebook = gtk.Notebook()
+        self.notebook.set_property('homogeneous', True)
+        self.notebook.set_property('show-tabs', False)
+
+        self.tabbar = HobTabBar()
+        self.tabbar.connect("tab-switched",   self.tab_switched_cb)

bitbake/lib/bb/ui/crumbs/hobwidget.py class HobInfoButton(gtk.EventBox): · line 581

Quoted 10 lines
bitbake/lib/bb/ui/crumbs/hobwidget.py +10 -0
+        self.notebook.connect("page-added",   self.page_added_cb)
+        self.notebook.connect("page-removed", self.page_removed_cb)
+
+        self.search = None
+        self.search_name = ""
+
+        self.tb = gtk.Table(1, 100, False)
+        self.hbox= gtk.HBox(False, 0)
+        self.hbox.pack_start(self.tabbar, True, True)
+        self.tb.attach(self.hbox, 0, 100, 0, 1)

We seem to have a lot of containers here. Do we really need a table, hbox and the vbox? It looks like we should just be able to pack self.tabbar at the top of the vbox?

Quoted 40 lines
bitbake/lib/bb/ui/crumbs/hobwidget.py +40 -0
+
+        self.pack_start(self.tb, False, False)
+        self.pack_start(self.notebook)
+
+        self.show_all()
+
+    def append_page(self, child, tab_label):
+        self.notebook.set_current_page(self.notebook.append_page(child, tab_label))
+
+    def set_entry(self, name="Search:"):
+        for child in self.tb.get_children():
+            if child:
+                self.tb.remove(child)
+
+        hbox_entry = gtk.HBox(False, 0)
+        hbox_entry.show()
+
+        self.search = gtk.Entry()
+        self.search_name = name
+        style = self.search.get_style()
+        style.text[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(HobColors.GRAY, False, False)
+        self.search.set_style(style)
+        self.search.set_text(name)
+        self.search.set_editable(False)
+        self.search.show()
+        self.align = gtk.Alignment(xalign=1.0, yalign=0.7)
+        self.align.add(self.search)
+        self.align.show()
+        hbox_entry.pack_end(self.align, False, False)
+        self.tabbar.resize_blank_rectangle()
+
+        self.tb.attach(hbox_entry, 75, 100, 0, 1, xpadding=5)
+        self.tb.attach(self.hbox, 0, 100, 0, 1)
+
+        self.tabbar.connect("blank-area-changed", self.blank_area_resize_cb)
+        self.search.connect("focus-in-event", self.set_search_entry_editable_cb)
+        self.search.connect("focus-out-event", self.set_search_entry_reset_cb)
+
+        self.tb.show()
+

bitbake/lib/bb/ui/crumbs/hobwidget.py class HobInfoButton(gtk.EventBox): · line 631

Quoted 10 lines
bitbake/lib/bb/ui/crumbs/hobwidget.py +10 -0
+    def tab_switched_cb(self, widget, page):
+        self.notebook.set_current_page(page)
+
+    def page_added_cb(self, notebook, notebook_child, page):
+        if not notebook:
+            return
+        title = notebook.get_tab_label_text(notebook_child)
+        if title == None:
+            return
+        for i in range(len(self.tabbar.children)):

Looks like we can use the iterator idiom here?

bitbake/lib/bb/ui/crumbs/hobwidget.py class HobInfoButton(gtk.EventBox): · line 638

Quoted 7 lines
bitbake/lib/bb/ui/crumbs/hobwidget.py +7 -0
+            if self.tabbar.children[i]["title"] == title:
+                self.tabbar.children[i]["toggled_page"] = page
+                return
+        self.tabbar.append_tab_child(title, page)
+
+    def page_removed_cb(self, notebook, notebook_child, page, title=""):
+        for i in range(len(self.tabbar.children)):

and here

bitbake/lib/bb/ui/crumbs/hobwidget.py class HobInfoButton(gtk.EventBox): · line 647

Quoted 9 lines
bitbake/lib/bb/ui/crumbs/hobwidget.py +9 -0
+            if self.tabbar.children[i] == title:
+                self.tabbar.children[i]["toggled_page"] = -1
+
+    def blank_area_resize_cb(self, widget, request_x, request_y, request_width, request_height):
+        self.search.set_size_request(request_width, request_height)
+        widget.modify_bg(gtk.STATE_SELECTED, gtk.gdk.color_parse(HobColors.YELLOW))
+
+    def set_search_entry_editable_cb(self, widget, event):
+        if self.search:

The widget variable passed to the callback here should be the self.search entry the signal handler was attached to so we should be able avoid having to check if the member variable has been assigned yet and just use widget, rather than self.search, in here.

Though when self.search would actually be None I'm not certain?

bitbake/lib/bb/ui/crumbs/hobwidget.py class HobInfoButton(gtk.EventBox): · line 655

Quoted 8 lines
bitbake/lib/bb/ui/crumbs/hobwidget.py +8 -0
+            self.search.set_editable(True)
+            self.search.set_text("")
+            style = self.search.get_style()
+            style.text[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(HobColors.BLACK, False, False)
+            self.search.set_style(style)
+
+    def set_search_entry_reset_cb(self, widget, event):
+        if self.search:

Same as above, can't we just use widget rather than self.search?

Quoted 103 lines
bitbake/lib/bb/ui/crumbs/hobwidget.py +5 -0
+            style = self.search.get_style()
+            style.text[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(HobColors.GRAY, False, False)
+            self.search.set_style(style)
+            self.search.set_text(self.search_name)
+            self.search.set_editable(False)
bitbake/lib/bb/ui/crumbs/packageselectionpage.py +6 -13
index a8628ac..5459743 100755
@@ -23,7 +23,7 @@
  import gtk
  import glib
  from bb.ui.crumbs.hobcolor import HobColors
-from bb.ui.crumbs.hobwidget import HobViewBar, HobViewTable
+from bb.ui.crumbs.hobwidget import HobViewTable, HobNotebook
  from bb.ui.crumbs.hoblistmodel import PackageListModel
  from bb.ui.crumbs.hobpages import HobPage
 
@@ -102,11 +102,7 @@ class PackageSelectionPage (HobPage):
          self.pack_start(self.group_align, expand=True, fill=True)
 
          # set visiable members
-        self.grid = gtk.Table(10, 1, True)
-        self.grid.set_col_spacings(3)
-
-        self.ins = gtk.Notebook()
-        self.ins.set_show_tabs(False)
+        self.ins = HobNotebook()
          self.tables = [] # we need to modify table when the dialog is shown
          # append the tab
          for i in range(len(self.pages)):
@@ -128,19 +124,16 @@ class PackageSelectionPage (HobPage):
              self.ins.append_page(tab, label)
              self.tables.append(tab)
 
-        self.grid.attach(self.ins, 0, 1, 1, 10, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 1, 1)
-        # a black bar associated with the notebook
-        self.topbar = HobViewBar(self.ins)
-        self.grid.attach(self.topbar, 0, 1, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 1, 1)
+        self.ins.set_entry("Search packages:")
          # set the search entry for each table
          for tab in self.tables:
-            tab.set_search_entry(0, self.topbar.search)
+            tab.set_search_entry(0, self.ins.search)
 
          # add all into the dialog
-        self.box_group_area.add(self.grid)
+        self.box_group_area.pack_start(self.ins, expand=True, fill=True)
 
          button_box = gtk.HBox(False, 5)
-        self.box_group_area.pack_start(button_box, expand=False, fill=False)
+        self.box_group_area.pack_end(button_box, expand=False, fill=False)
 
          self.build_image_button = gtk.Button()
          label = gtk.Label()
bitbake/lib/bb/ui/crumbs/recipeselectionpage.py +5 -14
index aea9c0c..8423714 100755
@@ -23,7 +23,7 @@
  import gtk
  import glib
  from bb.ui.crumbs.hobcolor import HobColors
-from bb.ui.crumbs.hobwidget import HobViewBar, HobViewTable
+from bb.ui.crumbs.hobwidget import HobViewTable, HobNotebook
  from bb.ui.crumbs.hoblistmodel import RecipeListModel
  from bb.ui.crumbs.hobpages import HobPage
 
@@ -124,13 +124,7 @@ class RecipeSelectionPage (HobPage):
          self.pack_start(self.group_align, expand=True, fill=True)
 
          # set visiable members
-        self.grid = gtk.Table(10, 1, True)
-        self.grid.set_col_spacings(3)
-
-        # draw the left part of the window
-        # a notebook
-        self.ins = gtk.Notebook()
-        self.ins.set_show_tabs(False)
+        self.ins = HobNotebook()
          self.tables = [] # we need modify table when the dialog is shown
          # append the tabs in order
          for i in range(len(self.pages)):
@@ -151,16 +145,13 @@ class RecipeSelectionPage (HobPage):
              self.ins.append_page(tab, label)
              self.tables.append(tab)
 
-        self.grid.attach(self.ins, 0, 1, 1, 10, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND)
-        # a black bar associated with the notebook
-        self.topbar = HobViewBar(self.ins)
-        self.grid.attach(self.topbar, 0, 1, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND)
+        self.ins.set_entry("Search recipes:")
          # set the search entry for each table
          for tab in self.tables:
-            tab.set_search_entry(0, self.topbar.search)
+            tab.set_search_entry(0, self.ins.search)
 
          # add all into the window
-        self.box_group_area.add(self.grid)
+        self.box_group_area.pack_start(self.ins, expand=True, fill=True)
 
          button_box = gtk.HBox(False, 5)
          self.box_group_area.pack_end(button_box, expand=False, fill=False)
Signature · 3 lines
Joshua '贾詡' Lock
        Yocto Project "Johannes factotum"
        Intel Open Source Technology Centre
#3 lore raw

Re: [PATCH 2/3] Hob: use HobNotebook to implement a notebook in build details page

In reply to Shane Wang
Quoted 80 lines · Shane Wang

This patch is to use HobNotebook we defined to implement the notebook in the build details page.

---
  bitbake/lib/bb/ui/crumbs/builddetailspage.py |   30 ++++++++++--
  bitbake/lib/bb/ui/crumbs/runningbuild.py     |   65 ++++++++++++++++++++++++++
  2 files changed, 90 insertions(+), 5 deletions(-)
bitbake/lib/bb/ui/crumbs/builddetailspage.py +25 -5
index 941f1e3..8ebcf08 100755
@@ -22,8 +22,10 @@
 
  import gtk
  from bb.ui.crumbs.progressbar import HobProgressBar
-from bb.ui.crumbs.hobwidget import hic
+from bb.ui.crumbs.hobwidget import hic, HobNotebook
  from bb.ui.crumbs.runningbuild import RunningBuildTreeView
+from bb.ui.crumbs.runningbuild import BuildConfigurationTreeView
+from bb.ui.crumbs.runningbuild import BuildFailureTreeView
  from bb.ui.crumbs.hobpages import HobPage
 
  #
@@ -49,11 +51,29 @@ class BuildDetailsPage (HobPage):
          self.stop_button.connect("clicked", self.stop_button_clicked_cb)
          self.progress_box.pack_end(self.stop_button, expand=False, fill=False)
 
+        self.notebook = HobNotebook()
+        self.config_tv = BuildConfigurationTreeView()
+        self.config_model = self.builder.handler.build.model.config_model()
+        self.config_tv.set_model(self.config_model)
+        self.scrolled_view_config = gtk.ScrolledWindow ()
+        self.scrolled_view_config.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.scrolled_view_config.add(self.config_tv)
+        self.notebook.append_page(self.scrolled_view_config, gtk.Label("Build Configuration"))
+
+        self.failure_tv = BuildFailureTreeView()
+        self.failure_model = self.builder.handler.build.model.failure_model()
+        self.failure_tv.set_model(self.failure_model)
+        self.scrolled_view_failure = gtk.ScrolledWindow ()
+        self.scrolled_view_failure.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.scrolled_view_failure.add(self.failure_tv)
+        self.notebook.append_page(self.scrolled_view_failure, gtk.Label("Issues"))
+
          self.build_tv = RunningBuildTreeView(readonly=True)
          self.build_tv.set_model(self.builder.handler.build.model)
-        self.scrolled_view = gtk.ScrolledWindow ()
-        self.scrolled_view.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-        self.scrolled_view.add(self.build_tv)
+        self.scrolled_view_build = gtk.ScrolledWindow ()
+        self.scrolled_view_build.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        self.scrolled_view_build.add(self.build_tv)
+        self.notebook.append_page(self.scrolled_view_build, gtk.Label("Log"))
 
          self.button_box = gtk.HBox(False, 5)
          self.back_button = gtk.LinkButton("Go back to Image Configuration screen", "<<  Back to image configuration")
@@ -86,7 +106,7 @@ class BuildDetailsPage (HobPage):
          self.progress_bar.reset()
          self.vbox.pack_start(self.progress_box, expand=False, fill=False)
 
-        self.vbox.pack_start(self.scrolled_view, expand=True, fill=True)
+        self.vbox.pack_start(self.notebook, expand=True, fill=True)
 
          self.box_group_area.pack_end(self.button_box, expand=False, fill=False)
          self.show_all()
bitbake/lib/bb/ui/crumbs/runningbuild.py +1 -0
index 718f692..eedd8d9 100644
@@ -25,6 +25,7 @@ import logging
  import time
  import urllib
  import urllib2
+import pango
  from bb.ui.crumbs.hobcolor import HobColors
 
  class RunningBuildModel (gtk.TreeStore):

bitbake/lib/bb/ui/crumbs/runningbuild.py class RunningBuildModel (gtk.TreeStore): · line 50

Quoted 10 lines
bitbake/lib/bb/ui/crumbs/runningbuild.py +7 -0
@@ -40,6 +41,32 @@ class RunningBuildModel (gtk.TreeStore):
                                  gobject.TYPE_STRING,
                                  gobject.TYPE_INT)
 
+    def config_model_filter(self, model, it):
+        msg = model.get(it, self.COL_MESSAGE)[0]
+        if msg == None or type(msg) != str:
+            return False
+        if msg.startswith("\nOE Build Configuration:\n"):
+            return True
+        return False

This seems like a really inefficient way to do this. Gtk+ is going to run every message in the build output through this filter...

I expect we already collect all, or most, of the information in this tab at some point throughout Hobs life-cycle. Can we somehow store it for display here?

bitbake/lib/bb/ui/crumbs/runningbuild.py class RunningBuildModel (gtk.TreeStore): · line 57

Quoted 7 lines
bitbake/lib/bb/ui/crumbs/runningbuild.py +7 -0
+
+    def failure_model_filter(self, model, it):
+        color = model.get(it, self.COL_COLOR)[0]
+        if color == None:
+            return False
+        if color == HobColors.ERROR:
+            return True

I think that the issues tab should include warnings also. Do they Warnings and Errors currently share the same colour or do we need to add an extra test to the filter?

Quoted 57 lines
bitbake/lib/bb/ui/crumbs/runningbuild.py +50 -0
+        return False
+
+    def config_model(self):
+        model = self.filter_new()
+        model.set_visible_func(self.config_model_filter)
+        return model
+
+    def failure_model(self):
+        model = self.filter_new()
+        model.set_visible_func(self.failure_model_filter)
+        return model
+
  class RunningBuild (gobject.GObject):
      __gsignals__ = {
            'build-started' : (gobject.SIGNAL_RUN_LAST,
@@ -376,3 +403,41 @@ class RunningBuildTreeView (gtk.TreeView):
          message = model.get(it, model.COL_MESSAGE)[0]
 
          self._add_to_clipboard(message)
+
+
+class BuildConfigurationTreeView(gtk.TreeView):
+
+    def __init__ (self):
+        gtk.TreeView.__init__(self)
+        self.set_rules_hint(False)
+        self.set_headers_visible(False)
+        self.set_property("hover-expand", True)
+        self.get_selection().set_mode(gtk.SELECTION_SINGLE)
+
+        # The message of the build.
+        self.message_renderer = gtk.CellRendererText ()
+        self.message_column = gtk.TreeViewColumn ("Message", self.message_renderer, text=RunningBuildModel.COL_MESSAGE, background=RunningBuildModel.COL_COLOR)
+        font = self.get_style().font_desc
+        font.set_size(pango.SCALE * 13)
+        self.message_renderer.set_property('font-desc', font)
+        self.append_column (self.message_column)
+
+
+class BuildFailureTreeView(gtk.TreeView):
+
+    def __init__ (self):
+        gtk.TreeView.__init__(self)
+        self.set_rules_hint(False)
+        self.set_headers_visible(False)
+        self.get_selection().set_mode(gtk.SELECTION_SINGLE)
+
+        # The icon that indicates whether we're building or failed.
+        renderer = gtk.CellRendererPixbuf ()
+        col = gtk.TreeViewColumn ("Status", renderer)
+        col.add_attribute (renderer, "icon-name", RunningBuildModel.COL_ICON)
+        self.append_column (col)
+
+        # The message of the build.
+        self.message_renderer = gtk.CellRendererText ()
+        self.message_column = gtk.TreeViewColumn ("Message", self.message_renderer, text=RunningBuildModel.COL_MESSAGE, background=RunningBuildModel.COL_COLOR)
+        self.append_column (self.message_column)
Signature · 3 lines
Joshua '贾詡' Lock
        Yocto Project "Johannes factotum"
        Intel Open Source Technology Centre
#9 lore raw
In reply to Shane Wang
Quoted 1 line · Shane Wang

This patchset is to implement Hob self-defined notebook, and use it in the build details page.

I have raised some concerns with the implementation of this series on each of the patches. It may seem nit-picky but we really do need to start paying attention to the details at this point and polishing the functionality and work-flow of the new Hob.

I'm torn on the series. As it stands we see errors each time we use Hob in master as the standard Gtk notebook doesn't support some of the API in the Hob notebook that is being called - errors look bad.

However this series has some poor coding style and doesn't match the visual design - these also look bad, though arguably less so.

I don't mind whether we merge this and iterate on the implementation or hold on merging until we fix the implementation but I feel we need to address the issues.

What are others thoughts?

Joshua

Signature · 3 lines
Joshua '贾詡' Lock
        Yocto Project "Johannes factotum"
        Intel Open Source Technology Centre
#5 lore raw

Re: [PATCH 3/3] Hob: show indicators on the tabs of the Hob notebook

In reply to Shane Wang
Quoted 1 line · Shane Wang

This patch is to show the indicators (e.g., the number of the issues) in the build details page to highlight.

My understanding of the design is that warnings are an "Issue" too, can we emit the signal for a warning too?

Quoted 114 lines
---
  bitbake/lib/bb/ui/crumbs/builddetailspage.py |   10 ++++++++++
  bitbake/lib/bb/ui/crumbs/builder.py          |    5 +++++
  bitbake/lib/bb/ui/crumbs/hobwidget.py        |   14 ++++++++++++++
  bitbake/lib/bb/ui/crumbs/runningbuild.py     |    4 ++++
  4 files changed, 33 insertions(+), 0 deletions(-)
bitbake/lib/bb/ui/crumbs/builddetailspage.py +10 -0
index 8ebcf08..4a9f658 100755
@@ -37,6 +37,8 @@ class BuildDetailsPage (HobPage):
      def __init__(self, builder):
          super(BuildDetailsPage, self).__init__(builder, "Building ...")
 
+        self.num_of_issues = 0
+
          # create visual elements
          self.create_visual_elements()
 
@@ -80,6 +82,14 @@ class BuildDetailsPage (HobPage):
          self.back_button.connect("clicked", self.back_button_clicked_cb)
          self.button_box.pack_start(self.back_button, expand=False, fill=False)
 
+    def show_issues(self):
+        self.num_of_issues += 1
+        self.notebook.show_indicator_icon("Issues", self.num_of_issues)
+
+    def reset_issues(self):
+        self.num_of_issues = 0
+        self.notebook.hide_indicator_icon("Issues")
+
      def _remove_all_widget(self):
          children = self.vbox.get_children() or []
          for child in children:
bitbake/lib/bb/ui/crumbs/builder.py +5 -0
index a905030..be8abd9 100755
@@ -212,6 +212,7 @@ class Builder(gtk.Window):
          self.handler.build.connect("build-succeeded",    self.handler_build_succeeded_cb)
          self.handler.build.connect("build-failed",       self.handler_build_failed_cb)
          self.handler.build.connect("task-started",       self.handler_task_started_cb)
+        self.handler.build.connect("log-error",          self.handler_build_failure_cb)
          self.handler.connect("generating-data",          self.handler_generating_data_cb)
          self.handler.connect("data-generated",           self.handler_data_generated_cb)
          self.handler.connect("command-succeeded",        self.handler_command_succeeded_cb)
@@ -518,6 +519,7 @@ class Builder(gtk.Window):
          elif self.current_step == self.PACKAGE_GENERATING:
              fraction = 0
          self.build_details_page.update_progress_bar("Build Started: ", fraction)
+        self.build_details_page.reset_issues()
 
      def build_succeeded(self):
          if self.current_step == self.FAST_IMAGE_GENERATING:
@@ -585,6 +587,9 @@ class Builder(gtk.Window):
                  fraction = 0.2 + 0.8 * fraction
          self.build_details_page.update_progress_bar(title + ": ", fraction)
 
+    def handler_build_failure_cb(self, running_build):
+        self.build_details_page.show_issues()
+
      def destroy_window_cb(self, widget, event):
          lbl = "<b>Do you really want to exit the Hob image creator?</b>"
          dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
bitbake/lib/bb/ui/crumbs/hobwidget.py +14 -0
index 9af19f6..d1557a8 100644
@@ -619,6 +619,20 @@ class HobNotebook(gtk.VBox):
 
          self.tb.show()
 
+    def show_indicator_icon(self, title, number):
+        for i in range(len(self.tabbar.children)):
+            if self.tabbar.children[i]["toggled_page"] == -1:
+                continue
+            if self.tabbar.children[i]["title"] == title:
+                self.tabbar.show_indicator_icon(i, number)
+
+    def hide_indicator_icon(self, title):
+        for i in range(len(self.tabbar.children)):
+            if self.tabbar.children[i]["toggled_page"] == -1:
+                continue
+            if self.tabbar.children[i]["title"] == title:
+                self.tabbar.hide_indicator_icon(i)
+
      def tab_switched_cb(self, widget, page):
          self.notebook.set_current_page(page)
 
bitbake/lib/bb/ui/crumbs/runningbuild.py +4 -0
index eedd8d9..ddac232 100644
@@ -84,6 +84,9 @@ class RunningBuild (gobject.GObject):
            'task-started'   : (gobject.SIGNAL_RUN_LAST,
                                gobject.TYPE_NONE,
                                (gobject.TYPE_PYOBJECT,)),
+          'log-error'      : (gobject.SIGNAL_RUN_LAST,
+                              gobject.TYPE_NONE,
+                              ()),
            }
      pids_to_task = {}
      tasks_to_iter = {}
@@ -134,6 +137,7 @@ class RunningBuild (gobject.GObject):
              if event.levelno>= logging.ERROR:
                  icon = "dialog-error"
                  color = HobColors.ERROR
+                self.emit("log-error")
              elif event.levelno>= logging.WARNING:
                  icon = "dialog-warning"
                  color = HobColors.WARNING
Signature · 3 lines
Joshua '贾詡' Lock
        Yocto Project "Johannes factotum"
        Intel Open Source Technology Centre
#7 lore raw

Re: [PATCH 1/3] Hob: implement a self-defined notebook visual component for Hob

In reply to Joshua Lock
Quoted 6 lines

1) We're doing the range dance rather than just doing the Pythonic thing and iterating over the sequence with "for x in y".
http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#use-in-where-possible-1

I understand its desirable to have the index in some places, but where it isn't we should use the standard pattern. This is good coding practice.

I think I pointed it out before, but it was on a Friday, so I might as well repeat -

If you need the index you can replace something like this:
 for i in range(len(self.children)): # ugly
With:
for i,child in enumerate(self.children): # less ugly!

Best regards,

Oren

#8 lore raw

Re: [PATCH 1/3] Hob: implement a self-defined notebook visual component for Hob

Quoted 7 lines · Oren Leaffer
Quoted 6 lines

1) We're doing the range dance rather than just doing the Pythonic thing and iterating over the sequence with "for x in y".
http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#use-in-where-possible-1

I understand its desirable to have the index in some places, but where it isn't we should use the standard pattern. This is good coding practice.

Quoted 7 lines

I think I pointed it out before, but it was on a Friday, so I might as well repeat -

If you need the index you can replace something like this:
for i in range(len(self.children)): # ugly
With:
for i,child in enumerate(self.children): # less ugly!

Indeed, I'd be happy to see more of that.

Thanks for pointing that out Oren!

Cheers,
Joshua

Signature · 3 lines
Joshua '贾詡' Lock
        Yocto Project "Johannes factotum"
        Intel Open Source Technology Centre