[hamster-applet] added bit of interaction the code is a bit disorganised



commit cc6cac7c0eb924b106b165fb1cbe94babbcb6a7d
Author: Toms Bauģis <toms baugis gmail com>
Date:   Thu Dec 31 13:57:09 2009 +0000

    added bit of interaction the code is a bit disorganised

 hamster/charting.py      |   63 +++++++++++++++++----
 hamster/stats_reports.py |  136 ++++++++++++++++++++++++++++++++++++----------
 2 files changed, 159 insertions(+), 40 deletions(-)
---
diff --git a/hamster/charting.py b/hamster/charting.py
index cc3ac36..0d1f314 100644
--- a/hamster/charting.py
+++ b/hamster/charting.py
@@ -33,7 +33,7 @@ http://projecthamster.wordpress.com/
 
 """
 
-import gtk
+import gtk, gobject
 import cairo, pango
 import copy
 import math
@@ -115,6 +115,9 @@ class Chart(graphics.Area):
         self.labels_at_end     = If stack bars are displayed, this allows to
                                  show them at right end of graph.
     """
+    __gsignals__ = {
+        "bar-clicked": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, )),
+    }
     def __init__(self, **args):
         graphics.Area.__init__(self)
 
@@ -136,6 +139,8 @@ class Chart(graphics.Area):
         self.labels_at_end     = args.get("labels_at_end", False)
         self.framerate         = args.get("framerate", 60)
 
+        self.interactive       = args.get("interactive", False) # if the bars are clickable
+
         # other stuff
         self.bars = []
         self.keys = []
@@ -150,7 +155,28 @@ class Chart(graphics.Area):
         self.graph_x, self.graph_y = 0, 0
         self.graph_width, self.graph_height = None, None
         
+        self.mouse_bar = None
+        if self.interactive:
+            self.connect("mouse-over", self.on_mouse_over)
+            self.connect("button-release", self.on_clicked)
+            
+        self.bars_selected = []
+        
+    
+    def on_mouse_over(self, area, region):
+        if region:
+            self.mouse_bar = int(region[0])
+        else:
+            self.mouse_bar = None
+            
+        self.redraw_canvas()
         
+    def on_clicked(self, area, bar):
+        self.emit("bar-clicked", self.mouse_bar)
+    
+    def select_bar(self, index):
+        pass
+
     def get_bar_color(self, index):
         # returns color darkened by it's index
         # the approach reduces contrast by each step
@@ -509,25 +535,32 @@ class HorizontalBarChart(Chart):
         
 
         
-        context.set_line_width(0)
+        context.set_line_width(1)
 
         # bars and labels
         self.layout.set_width(legend_width * pango.SCALE)
         
 
         for i, label in enumerate(keys):
+            if self.interactive:
+                self.register_mouse_region(0,
+                                           positions[label][0],
+                                           self.width,
+                                           positions[label][0] + positions[label][1],
+                                           str(i))
+
             self.layout.set_width(legend_width * pango.SCALE)
-            self.set_color(graphics.Colors.aluminium[5])        
-            
             self.layout.set_text(label)
             label_w, label_h = self.layout.get_pixel_size()
-
+            
+            self.set_color(graphics.Colors.aluminium[5])        
             context.move_to(0, positions[label][0] + (positions[label][1] - label_h) / 2)
             context.show_layout(self.layout)
 
             base_color = self.bar_base_color or (220, 220, 220)
 
             last_color = (255,255,255)
+
             if self.stack_keys:
                 bar_start = 0
 
@@ -550,7 +583,12 @@ class HorizontalBarChart(Chart):
                 bar_size = round(max_bar_size * self.bars[i].size)
                 bar_start = bar_size
 
-                last_color = self.key_colors.get(self.keys[i]) or base_color
+                if i in self.bars_selected:
+                    last_color = self.get_style().bg[gtk.STATE_SELECTED].to_string()
+                elif i == self.mouse_bar:
+                    last_color = self.get_style().bg[gtk.STATE_PRELIGHT].to_string()
+                else:
+                    last_color = self.key_colors.get(self.keys[i]) or base_color
 
                 self.draw_bar(self.graph_x,
                               positions[label][0],
@@ -573,11 +611,14 @@ class HorizontalBarChart(Chart):
                 label_x = self.graph_x + bar_start + vertical_padding
                 self.set_color(graphics.Colors.aluminium[5])        
             else:
-                # we are in the bar so make sure that the font color is distinguishable
-                if colorsys.rgb_to_hls(*graphics.Colors.rgb(last_color))[1] < 150:
-                    self.set_color(graphics.Colors.almost_white)
+                if i in self.bars_selected:
+                    self.set_color(self.get_style().fg[gtk.STATE_SELECTED].to_string())
                 else:
-                    self.set_color(graphics.Colors.aluminium[5])        
+                    # we are in the bar so make sure that the font color is distinguishable
+                    if colorsys.rgb_to_hls(*graphics.Colors.rgb(last_color))[1] < 150:
+                        self.set_color(graphics.Colors.almost_white)
+                    else:
+                        self.set_color(graphics.Colors.aluminium[5])        
                     
                 label_x = self.graph_x + bar_start - label_w - vertical_padding
             
@@ -638,8 +679,6 @@ class HorizontalDayChart(Chart):
             return
 
         
-        self.context.translate(0.5, 0.5)
-
         positions = {}
         y = 0
         bar_width = min(self.graph_height / float(len(self.keys)), self.max_bar_width)
diff --git a/hamster/stats_reports.py b/hamster/stats_reports.py
index c1b7598..e255e59 100644
--- a/hamster/stats_reports.py
+++ b/hamster/stats_reports.py
@@ -58,21 +58,33 @@ class ReportsBox(gtk.VBox):
                                                           max_bar_width = 20,
                                                           legend_width = x_offset,
                                                           value_format = "%.1f",
-                                                          animate = False)
+                                                          interactive = True)
+        self.category_chart.connect("bar-clicked", self.on_category_clicked)
+        self.selected_categories = []
+        self.category_sums = None
+        
         self.get_widget("totals_by_category").add(self.category_chart);
         
         self.activity_chart = charting.HorizontalBarChart(background = self.background,
                                                           max_bar_width = 20,
                                                           legend_width = x_offset,
                                                           value_format = "%.1f",
-                                                          animate = False)
+                                                          interactive = True)
+        self.activity_chart.connect("bar-clicked", self.on_activity_clicked)
+        self.selected_activities = []
+        self.activity_sums = None
+
         self.get_widget("totals_by_activity").add(self.activity_chart);
 
         self.tag_chart = charting.HorizontalBarChart(background = self.background,
                                                           max_bar_width = 20,
                                                           legend_width = x_offset,
                                                           value_format = "%.1f",
-                                                          animate = False)
+                                                          interactive = True)
+        self.tag_chart.connect("bar-clicked", self.on_tag_clicked)
+        self.selected_tags = []
+        self.tag_sums = None
+
         self.get_widget("totals_by_tag").add(self.tag_chart);
 
 
@@ -83,13 +95,47 @@ class ReportsBox(gtk.VBox):
 
         self.report_chooser = None
 
+
+    def on_category_clicked(self, widget, idx):
+        if idx in self.category_chart.bars_selected:
+            self.category_chart.bars_selected.remove(idx)
+            self.selected_categories.remove(self.category_sums[0][idx])
+        else:
+            self.category_chart.bars_selected.append(idx)
+            self.selected_categories.append(self.category_sums[0][idx])
+        self.do_charts()
+
+    def on_activity_clicked(self, widget, idx):
+        if idx in self.activity_chart.bars_selected:
+            self.activity_chart.bars_selected.remove(idx)
+            self.selected_activities.remove(self.activity_sums[0][idx])
+        else:
+            self.activity_chart.bars_selected.append(idx)
+            self.selected_activities.append(self.activity_sums[0][idx])
+        self.do_charts()
+
+    def on_tag_clicked(self, widget, idx):
+        if idx in self.tag_chart.bars_selected:
+            self.tag_chart.bars_selected.remove(idx)
+            self.selected_tags.remove(self.tag_sums[0][idx])
+        else:
+            self.tag_chart.bars_selected.append(idx)
+            self.selected_tags.append(self.tag_sums[0][idx])
+        self.do_charts()
+
+
     def on_reports_box_expose_event(self, box, someth):
         self.do_charts()
 
     def search(self, start_date, end_date, facts):
         self.facts = facts
+        self.category_sums, self.activity_sums, self.tag_sums = [], [], []
+        self.selected_categories, self.selected_activities, self.selected_tags = [], [], []
+        self.category_chart.bars_selected, self.activity_chart.bars_selected, self.tag_chart.bars_selected = [], [], []
+        
         self.start_date = start_date
         self.end_date = end_date
+
         self.do_graph()
 
     def do_graph(self):
@@ -105,46 +151,80 @@ class ReportsBox(gtk.VBox):
     def do_charts(self):
         if not self.facts:
             return
+
+        import copy
+        facts = copy.deepcopy(self.facts)
         
-        #totals by category
-        category_sums = stuff.totals(self.facts,
+        for fact in facts:
+            if self.selected_categories and fact["category"] not in self.selected_categories:
+                fact["delta"] = dt.timedelta()
+            if self.selected_activities and fact["name"] not in self.selected_activities:
+                fact["delta"] = dt.timedelta()
+            if self.selected_tags and len(set(self.selected_tags) - set(fact["tags"])) > 0:
+                fact["delta"] = dt.timedelta()
+
+
+
+        # category totals
+        category_sums = stuff.totals(facts,
                                      lambda fact: (fact["category"]),
                                      lambda fact: stuff.duration_minutes(fact["delta"]) / 60.0)
         if category_sums:
-            category_sums = sorted(category_sums.items(), key=lambda x:x[1], reverse = True)
-            keys, values = zip(*category_sums)
-            self.get_widget("totals_by_category").set_size_request(280, len(keys) * 20)
-            self.category_chart.plot(keys, values)
-        else:
-            self.category_chart.plot([], [])
-        
-
-        #totals by activity
-        activity_sums = stuff.totals(self.facts,
+            if self.category_sums:
+                category_sums = [(key, category_sums[key]) for key in self.category_sums[0]]
+            else:
+                category_sums = sorted(category_sums.items(), key=lambda x:x[1], reverse = True)
+            self.category_sums = zip(*category_sums)
+
+        # activity totals
+        activity_sums = stuff.totals(facts,
                                      lambda fact: (fact["name"]),
                                      lambda fact: stuff.duration_minutes(fact["delta"]) / 60.0)
-        activity_sums = sorted(activity_sums.items(), key=lambda x:x[1], reverse = True)
-        keys, values = zip(*activity_sums)
-        self.get_widget("totals_by_activity").set_size_request(10,10)
-        self.get_widget("totals_by_activity").set_size_request(280, len(keys) * 20)
-        self.activity_chart.plot(keys, values)
+        if self.activity_sums:
+            activity_sums = [(key, activity_sums[key]) for key in self.activity_sums[0]]
+        else:
+            activity_sums = sorted(activity_sums.items(), key=lambda x:x[1], reverse = True)
 
+        self.activity_sums = zip(*activity_sums)
 
+
+        # tag totals
         tag_sums = {}
-        for fact in self.facts:
+        for fact in facts:
             for tag in fact["tags"]:
                 tag_sums.setdefault(tag, 0)
                 tag_sums[tag] += stuff.duration_minutes(fact["delta"]) / 60.0
 
         if tag_sums:        
-            tag_sums = sorted(tag_sums.items(), key=lambda x:x[1], reverse = True)
-            keys, values = zip(*tag_sums)
-            self.get_widget("totals_by_tag").set_size_request(10,10)
-            self.get_widget("totals_by_tag").set_size_request(280, len(keys) * 20)
-            self.tag_chart.plot(keys, values)
+            if self.tag_sums:
+                tag_sums = [(key, tag_sums[key]) for key in self.tag_sums[0]]
+            else:
+                tag_sums = sorted(tag_sums.items(), key=lambda x:x[1], reverse = True)
+            self.tag_sums = zip(*tag_sums)
+
+
+
+
+        self.get_widget("totals_by_category").set_size_request(10,10)
+        if self.category_sums:
+            self.get_widget("totals_by_category").set_size_request(280, len(self.category_sums[0]) * 20)
+            self.category_chart.plot(*self.category_sums)
         else:
-            self.tag_chart.plot([], [])
-        
+            self.get_widget("totals_by_category").set_size_request(280, 10)
+            self.category_chart.plot(([],[]))
+
+        self.get_widget("totals_by_activity").set_size_request(10,10)
+        self.get_widget("totals_by_activity").set_size_request(280, len(self.activity_sums[0]) * 20)
+        self.activity_chart.plot(*self.activity_sums)
+
+        self.get_widget("totals_by_tag").set_size_request(10,10)
+        if self.tag_sums:
+            self.get_widget("totals_by_tag").set_size_request(280, len(self.tag_sums[0]) * 20)
+            self.tag_chart.plot(*self.tag_sums)
+        else:
+            self.get_widget("totals_by_tag").set_size_request(280, 10)
+            self.tag_chart.plot(([],[]))
+
 
     def get_widget(self, name):
         """ skip one variable (huh) """



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]