2 #Author: Mirestean Andrei < andrei.mirestean at gmail.com >
4 #This program is free software: you can redistribute it and/or modify
5 #it under the terms of the GNU General Public License as published by
6 #the Free Software Foundation, either version 3 of the License, or
7 #(at your option) any later version.
9 #This program is distributed in the hope that it will be useful,
10 #but WITHOUT ANY WARRANTY; without even the implied warranty of
11 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 #GNU General Public License for more details.
14 #You should have received a copy of the GNU General Public License
15 #along with this program. If not, see <http://www.gnu.org/licenses/>.
20 from datetime import date, timedelta
21 from xml.dom.minidom import getDOMImplementation, parseString
31 PATH = "/apps/pedometerhomewidget"
33 HEIGHT = PATH + "/height"
35 ASPECT = PATH + "/aspect"
36 SECONDVIEW = PATH + "/secondview"
37 GRAPHVIEW = PATH + "/graphview"
38 NOIDLETIME = PATH + "/noidletime"
39 LOGGING = PATH + "/logging"
41 ICONSPATH = "/opt/pedometerhomewidget/"
45 class Singleton(object):
47 def __new__(cls, *args, **kwargs):
49 cls._instance = super(Singleton, cls).__new__(
53 class PedoIntervalCounter(Singleton):
61 #TODO: check if last detected step is at the end of the interval
63 def set_vals(self, coords, tval):
69 def set_mode(self, mode):
70 #runnig, higher threshold to prevent fake steps
73 self.MIN_THRESHOLD = 650
74 self.MIN_TIME_STEPS = 0.35
77 self.MIN_THRESHOLD = 500
78 self.MIN_TIME_STEPS = 0.5
80 def calc_mean(self, vals):
85 return sum / len(vals)
88 def calc_stdev(self, vals):
90 mean = self.calc_mean(vals)
92 rez += pow(abs(mean - i), 2)
93 return math.sqrt(rez / len(vals))
95 def calc_threshold(self, vals):
98 mean = self.calc_mean(vals)
99 threshold = max (abs(mean - vmax), abs(mean - vmin))
102 def count_steps(self, vals, t):
103 threshold = self.MIN_THRESHOLD
104 mean = self.calc_mean(vals)
108 if abs(vals[i] - mean) > threshold:
110 ntime = t[i] + self.MIN_TIME_STEPS
111 while i < len(vals) and t[i] < ntime:
116 def get_best_values(self, x, y, z):
117 dev1 = self.calc_stdev(x)
118 dev2 = self.calc_stdev(y)
119 dev3 = self.calc_stdev(z)
120 dev_max = max(dev1, dev2, dev3)
122 if (abs(dev1 - dev_max) < 0.001):
123 logger.info("X chosen as best axis, stdev %f" % dev1)
125 elif (abs(dev2 - dev_max) < 0.001):
126 logger.info("Y chosen as best axis, stdev %f" % dev2)
129 logger.info("Z chosen as best axis, stdev %f" % dev3)
132 def number_steps(self):
133 vals = self.get_best_values(self.x, self.y, self.z)
134 return self.count_steps(vals, self.t)
137 def __init__(self, time=0, steps=0, dist=0, calories=0):
140 self.calories = calories
144 def __add__(self, other):
145 return PedoValues(self.time + other.time,
146 self.steps + other.steps,
147 self.dist + other.dist,
148 self.calories + other.calories)
150 def get_print_time(self):
152 hours = int(tdelta / 3600)
153 tdelta -= 3600 * hours
154 mins = int(tdelta / 60)
157 strtime = "%.2d:%.2d:%.2d" % (hours, mins, secs)
160 def get_print_distance(self):
163 return "%.2f km" % (self.dist / 1000)
165 return "%.2f mi" % (self.dist / 1609.344)
168 return "%d m" % self.dist
170 return "%d ft" % int(self.dist * 3.2808)
172 def get_avg_speed(self):
181 speed = 1.0 * self.dist / self.time
184 def get_print_avg_speed(self):
195 return "N/A " + suffix
196 speed = 1.0 * self.dist / self.time
197 #convert from meters per second to km/h or mi/h
199 return "%.2f %s" % (speed, suffix)
201 def get_print_steps(self):
202 return str(self.steps)
204 def get_print_calories(self):
205 return str(self.calories)
207 class PedoRepository(Singleton):
211 raise NotImplementedError("Must be implemented by subclass")
214 raise NotImplementedError("Must be implemented by subclass")
216 def reset_values(self):
220 def get_history_count(self):
221 """return the number of days in the log"""
224 def get_values(self):
227 def add_values(self, values, when=date.today()):
228 """add PedoValues values to repository """
230 self.values[when] = self.values[when] + values
232 self.values[when] = values
234 def get_last_7_days(self):
239 ret.append(self.values[day])
241 ret.append(PedoValues())
242 day = day - timedelta(days=1)
245 def get_last_weeks(self):
246 delta = timedelta(days=1)
248 week = int(date.today().strftime("%W"))
253 val += self.values[day]
256 w = int(day.strftime("%W"))
266 def get_alltime_values(self):
268 for k, v in self.values.iteritems():
272 def get_today_values(self):
274 return self.values[date.today()]
278 def get_this_week_values(self):
283 ret += self.values[day]
286 if day.weekday() == 0:
288 day = day - timedelta(days=1)
292 class PedoRepositoryXML(PedoRepository):
293 DIR = os.path.join(os.path.expanduser("~"), ".pedometer")
294 FILE = os.path.join(DIR, "data.xml")
295 FILE2 = os.path.join(DIR, "pickle.log")
297 if not os.path.exists(self.DIR):
298 os.makedirs(self.DIR)
299 PedoRepository.__init__(self)
303 f = open(self.FILE, "r")
304 dom = parseString(f.read())
305 values = dom.getElementsByTagName("pedometer")[0]
306 for v in values.getElementsByTagName("date"):
307 d = int(v.getAttribute("ordinal_day"))
308 steps = int(v.getAttribute("steps"))
309 calories = float(v.getAttribute("calories"))
310 dist = float(v.getAttribute("dist"))
311 time = float(v.getAttribute("time"))
312 day = date.fromordinal(d)
313 self.values[day] = PedoValues(time, steps, dist, calories)
317 logger.error("Error while loading data from xml file: %s" % e)
321 f = open(self.FILE, "w")
323 impl = getDOMImplementation()
325 newdoc = impl.createDocument(None, "pedometer", None)
326 top_element = newdoc.documentElement
327 for k, v in self.values.iteritems():
328 d = newdoc.createElement('date')
329 d.setAttribute("day", str(k.isoformat()))
330 d.setAttribute("ordinal_day", str(k.toordinal()))
331 d.setAttribute("steps", str(v.steps))
332 d.setAttribute("time", str(v.time))
333 d.setAttribute("dist", str(v.dist))
334 d.setAttribute("calories", str(v.calories))
335 top_element.appendChild(d)
337 newdoc.appendChild(top_element)
339 #f.write(newdoc.toprettyxml())
342 logger.error("Error while saving data to xml file: %s" % e)
345 class PedoRepositoryPickle(PedoRepository):
346 DIR = os.path.join(os.path.expanduser("~"), ".pedometer")
347 FILE = os.path.join(DIR, "pickle.log")
350 if not os.path.exists(self.DIR):
351 os.makedirs(self.DIR)
352 PedoRepository.__init__(self)
356 f = open(self.FILE, "rb")
357 self.values = pickle.load(f)
360 logger.error("Error while loading pickle file: %s" % e)
364 f = open(self.FILE, "wb")
365 pickle.dump(self.values, f)
368 logger.error("Error while saving data to pickle: %s" % e)
370 class PedoController(Singleton):
374 #what to display in second view - 0 - alltime, 1 - today, 2 - week
376 callback_update_ui = None
380 #values for the two views in the widget ( current and day/week/alltime)
381 v = [PedoValues(), PedoValues()]
385 graph_controller = None
388 self.pedometer = PedoCounter(self.steps_detected)
389 self.pedometerInterval = PedoIntervalCounter()
390 self.pedometerInterval.set_mode(self.mode)
391 self.repository = PedoRepositoryXML()
392 self.repository.load()
394 self.graph_controller = GraphController()
397 def load_values(self):
398 if self.second_view == 0:
399 self.v[1] = self.repository.get_alltime_values()
400 elif self.second_view == 1:
401 self.v[1] = self.repository.get_today_values()
403 self.v[1] = self.repository.get_this_week_values()
405 def save_values(self):
406 self.repository.add_values(self.v[0])
407 self.repository.save()
410 def start_pedometer(self):
411 self.v[0] = PedoValues()
412 self.last_time = time.time()
413 self.is_running = True
414 self.pedometer.start()
417 def stop_pedometer(self):
418 self.is_running = False
419 self.pedometer.request_stop()
424 def get_second(self):
426 return self.v[0] + self.v[1]
430 def update_current(self):
432 Update distance and calories for current values based on new height, mode values
434 self.v[0].dist = self.get_distance(self.v[0].steps)
435 self.v[0].calories = self.get_calories(self.v[0].steps)
437 def steps_detected(self, cnt, last_steps=False):
438 if not last_steps and cnt == 0 and self.no_idle_time:
439 logger.info("No steps detected, timer is paused")
441 self.v[0].steps += cnt
442 self.v[0].dist += self.get_distance(cnt)
443 self.v[0].calories += self.get_distance(cnt)
444 self.v[0].time += time.time() - self.last_time
450 self.last_time = time.time()
452 def set_mode(self, mode):
454 self.set_height(self.height_interval)
457 def set_unit(self, new_unit):
461 def set_second_view(self, second_view):
462 self.second_view = second_view
466 def set_callback_ui(self, func):
467 self.callback_update_ui = func
469 def set_height(self, height_interval):
470 self.height_inteval = height_interval
471 #set height, will affect the distance
472 if height_interval == 0:
473 self.STEP_LENGTH = 0.59
474 elif height_interval == 1:
475 self.STEP_LENGTH = 0.64
476 elif height_interval == 2:
477 self.STEP_LENGTH = 0.71
478 elif height_interval == 3:
479 self.STEP_LENGTH = 0.77
480 elif height_interval == 4:
481 self.STEP_LENGTH = 0.83
482 #increase step length if RUNNING
484 self.STEP_LENGTH *= 1.45
487 def set_no_idle_time(self, value):
488 self.no_idle_time = value
490 def get_distance(self, steps=None):
493 return self.STEP_LENGTH * steps;
495 def get_calories(self, steps):
498 def notify_UI(self, optional=False):
499 if self.callback_update_ui is not None:
500 self.callback_update_ui()
501 self.graph_controller.update_ui(optional)
503 class PedoCounter(Singleton):
504 COORD_FNAME = "/sys/class/i2c-adapter/i2c-3/3-001d/coord"
505 COORD_FNAME_SDK = "/home/andrei/pedometer-widget-0.1/date.txt"
506 LOGFILE = "/home/user/log_pedometer"
507 #time in ms between two accelerometer data reads
508 COORD_GET_INTERVAL = 10
512 interval_counter = None
513 stop_requested = False
514 update_function = None
518 def __init__(self, update_function=None):
519 if not os.path.exists(self.COORD_FNAME):
520 self.COORD_FNAME = self.COORD_FNAME_SDK
522 self.interval_counter = PedoIntervalCounter()
523 self.update_function = update_function
525 def set_logging(self, value):
528 def get_rotation(self):
529 f = open(self.COORD_FNAME, 'r')
530 coords = [int(w) for w in f.readline().split()]
535 logger.info("Counter started")
536 self.isRunning = True
537 self.stop_requested = False
539 fname = "%d_%d_%d_%d_%d_%d" % time.localtime()[0:6]
540 self.file = open(self.LOGFILE + fname + ".txt", "w")
541 gobject.idle_add(self.run)
544 self.coords = [[], [], []]
545 self.stime = time.time()
547 gobject.timeout_add(self.COORD_GET_INTERVAL, self.read_coords)
550 def read_coords(self):
551 x, y, z = self.get_rotation()
552 self.coords[0].append(int(x))
553 self.coords[1].append(int(y))
554 self.coords[2].append(int(z))
555 now = time.time() - self.stime
557 self.file.write("%d %d %d %f\n" % (self.coords[0][-1], self.coords[1][-1], self.coords[2][-1], now))
562 if self.t[-1] > self.COUNT_INTERVAL or self.stop_requested:
564 gobject.idle_add(self.stop_interval)
567 def stop_interval(self):
568 self.interval_counter.set_vals(self.coords, self.t)
569 cnt = self.interval_counter.number_steps()
571 logger.info("Number of steps detected for last interval %d, number of coords: %d" % (cnt, len(self.t)))
573 gobject.idle_add(self.update_function, cnt, self.stop_requested)
575 if self.stop_requested:
576 gobject.idle_add(self.stop)
578 gobject.idle_add(self.run)
584 logger.info("Counter has finished")
586 def request_stop(self):
587 self.stop_requested = True
588 self.isRunning = False
590 class CustomButton(hildon.Button):
591 def __init__(self, icon):
592 hildon.Button.__init__(self, gtk.HILDON_SIZE_AUTO_WIDTH, hildon.BUTTON_ARRANGEMENT_VERTICAL)
594 self.set_size_request(int(32 * 1.4), int(30 * 1.0))
595 self.retval = self.connect("expose_event", self.expose)
597 def set_icon(self, icon):
600 def expose(self, widget, event):
601 self.context = widget.window.cairo_create()
602 self.context.rectangle(event.area.x, event.area.y,
603 event.area.width, event.area.height)
606 rect = self.get_allocation()
607 self.context.rectangle(rect.x, rect.y, rect.width, rect.height)
608 self.context.set_source_rgba(1, 1, 1, 0)
610 style = self.rc_get_style()
611 color = style.lookup_color("DefaultBackgroundColor")
612 if self.state == gtk.STATE_ACTIVE:
613 style = self.rc_get_style()
614 color = style.lookup_color("SelectionColor")
615 self.context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
618 #img = cairo.ImageSurface.create_from_png(self.icon)
620 #self.context.set_source_surface(img)
621 #self.context.set_source_surface(img, rect.width/2 - img.get_width() /2, 0)
623 img.set_from_file(self.icon)
624 buf = img.get_pixbuf()
625 buf = buf.scale_simple(int(32 * 1.5), int(30 * 1.5), gtk.gdk.INTERP_BILINEAR)
627 self.context.set_source_pixbuf(buf, rect.x + (event.area.width / 2 - 15) - 8, rect.y + 1)
628 self.context.scale(200, 200)
633 class CustomEventBox(gtk.EventBox):
636 gtk.EventBox.__init__(self)
638 def do_expose_event(self, event):
639 self.context = self.window.cairo_create()
640 self.context.rectangle(event.area.x, event.area.y,
641 event.area.width, event.area.height)
644 rect = self.get_allocation()
645 self.context.rectangle(rect.x, rect.y, rect.width, rect.height)
647 if self.state == gtk.STATE_ACTIVE:
648 style = self.rc_get_style()
649 color = style.lookup_color("SelectionColor")
650 self.context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
652 self.context.set_source_rgba(1, 1, 1, 0)
655 gtk.EventBox.do_expose_event(self, event)
657 class GraphController(Singleton):
658 ytitles = ["Steps", "Average Speed", "Distance", "Calories"]
659 xtitles = ["Day", "Week"] # "Today"]
662 self.repository = PedoRepositoryXML()
665 def set_graph(self, widget):
669 def set_current_view(self, view):
671 current_view % len(ytitles) - gives the ytitle
672 current_view / len(ytitles) - gives the xtitle
674 self.current_view = view
676 if self.current_view == len(self.ytitles) * len(self.xtitles):
677 self.current_view = 0
678 self.x_id = self.current_view / len(self.ytitles)
679 self.y_id = self.current_view % len(self.ytitles)
682 self.set_current_view(self.current_view+1)
684 return self.current_view
686 def last_weeks_labels(self):
688 delta = timedelta(days=7)
691 ret.append(d.strftime("Week %W"))
695 def compute_values(self):
698 values = self.repository.get_last_7_days()
700 delta = timedelta(days=1)
702 labels.append(d.ctime().split()[0])
706 values = self.repository.get_last_weeks()
709 labels.append(d.strftime("Week %W"))
710 d = d - timedelta(days=7)
712 values = self.repository.get_today()
716 yvalues = [line.steps for line in values]
718 yvalues = [line.get_avg_speed() for line in values]
720 yvalues = [line.dist for line in values]
722 yvalues = [line.calories for line in values]
724 #determine values for y lines in graph
725 diff = self.get_best_interval_value(max(yvalues))
728 ytext.append(str(int(i*diff)))
730 if self.widget is not None:
733 self.widget.values = yvalues
734 self.widget.ytext = ytext
735 self.widget.xtext = labels
736 self.widget.max_value = diff * 5
737 self.widget.text = self.xtitles[self.x_id] + " / " + self.ytitles[self.y_id]
738 self.widget.queue_draw()
740 logger.error("Widget not set in GraphController")
742 def get_best_interval_value(self, max_value):
743 diff = 1.0 * max_value / 5
744 l = len(str(int(diff)))
745 d = math.pow(10, l/2)
746 val = int(math.ceil(1.0 * diff / d)) * d
751 def update_ui(self, optional=False):
752 """update graph values every x seconds"""
753 if optional and self.last_update - time.time() < 600:
755 if self.widget is None:
758 self.compute_values()
759 self.last_update = time.time()
761 class GraphWidget(gtk.DrawingArea):
764 gtk.DrawingArea.__init__(self)
765 self.set_size_request(-1, 150)
769 self.ytext = [" 0", "1000", "2000", "3000", "4000", "5000"]
770 self.xtext = ["Monday", "Tuesday", "Wednesday","Thursday", "Friday", "Saturday", "Sunday"]
771 self.values = [1500, 3400, 4000, 3600, 3200, 0, 4500]
772 self.max_value = 5000
773 self.text = "All time steps"
775 def do_expose_event(self, event):
776 context = self.window.cairo_create()
778 # set a clip region for the expose event
779 context.rectangle(event.area.x, event.area.y,
780 event.area.width, event.area.height)
785 context.set_operator(cairo.OPERATOR_SOURCE)
786 style = self.rc_get_style()
788 if self.state == gtk.STATE_ACTIVE:
789 color = style.lookup_color("SelectionColor")
791 color = style.lookup_color("DefaultBackgroundColor")
792 context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75)
804 rect = self.get_allocation()
808 cr.select_font_face("Purisa", cairo.FONT_SLANT_NORMAL,
809 cairo.FONT_WEIGHT_NORMAL)
812 #check space needed to display ylabels
813 te = cr.text_extents(self.ytext[-1])
814 border_left = te[2] + 7
816 cr.set_source_rgb(1, 1, 1)
817 cr.move_to(border_left, space_above)
818 cr.line_to(border_left, y-space_below)
822 cr.move_to(border_left, y-space_below)
823 cr.line_to(x-border_right, y-space_below)
827 ydiff = (y-space_above-space_below) / self.yvalues
828 for i in range(self.yvalues):
829 yy = y-space_below-ydiff*(i+1)
830 cr.move_to(border_left, yy)
831 cr.line_to(x-border_right, yy)
832 cr.set_line_width(0.8)
837 yy = y - space_below - ydiff*i + 5
838 te = cr.text_extents(self.ytext[i])
840 cr.move_to(border_left-te[2]-2, yy)
841 cr.show_text(self.ytext[i])
844 te = cr.text_extents(self.text)
845 cr.move_to((x-te[2])/2, y-5)
846 cr.show_text(self.text)
848 graph_x_space = x - border_left - border_right
849 graph_y_space = y - space_below - space_above
850 bar_width = graph_x_space*0.75 / len(self.values)
851 bar_distance = graph_x_space*0.25 / (1+len(self.values))
853 #set dummy max value to avoid exceptions
854 if self.max_value == 0:
856 for i in range(len(self.values)):
857 xx = border_left + (i+1)*bar_distance + i * bar_width
859 height = graph_y_space * (1.0 * self.values[i] / self.max_value)
860 cr.set_source_rgba(1, 1, 1, 0.75)
861 cr.rectangle(int(xx), int(yy-height), int(bar_width), int(height))
864 cr.set_source_rgba(1, 1, 1, 1)
865 cr.select_font_face("Purisa", cairo.FONT_SLANT_NORMAL,
866 cairo.FONT_WEIGHT_NORMAL)
869 cr.rotate(2*math.pi * (-45) / 180)
870 for i in range(len(self.values)):
871 xx = y - space_below - 10
872 yy = border_left + (i+1)*bar_distance + i * bar_width
873 cr.move_to(-xx, yy + bar_width*1.25 / 2)
874 cr.show_text(self.xtext[i])
876 class PedometerHomePlugin(hildondesktop.HomePluginItem):
880 labels = ["timer", "count", "dist", "avgSpeed", "calories"]
885 #second view ( day / week/ alltime)
888 second_view_labels = ["All-time", "Today", "This week"]
892 pedometerInterval = None
893 graph_controller = None
905 hildondesktop.HomePluginItem.__init__(self)
907 gobject.type_register(CustomEventBox)
908 gobject.type_register(GraphWidget)
910 self.client = gconf.client_get_default()
912 self.mode = self.client.get_int(MODE)
913 self.height = self.client.get_int(HEIGHT)
914 self.unit = self.client.get_int(UNIT)
915 self.aspect = self.client.get_int(ASPECT)
916 self.second_view = self.client.get_int(SECONDVIEW)
917 self.graph_view = self.client.get_int(GRAPHVIEW)
918 self.no_idle_time = self.client.get_bool(NOIDLETIME)
919 self.logging = self.client.get_bool(LOGGING)
922 self.client.set_int(MODE, 0)
923 self.client.set_int(HEIGHT, 0)
924 self.client.set_int(UNIT, 0)
925 self.client.set_int(ASPECT, 0)
926 self.client.set_int(SECONDVIEW, 0)
927 self.client.set_int(GRAPHVIEW, 0)
928 self.client.set_bool(NOIDLETIME, False)
929 self.client.set_bool(LOGGING, False)
931 self.controller = PedoController()
932 self.controller.set_height(self.height)
933 self.controller.set_mode(self.mode)
934 self.controller.set_unit(self.unit)
935 self.controller.set_second_view(self.second_view)
936 self.controller.set_callback_ui(self.update_values)
937 self.controller.set_no_idle_time(self.no_idle_time)
939 self.graph_controller = GraphController()
940 self.graph_controller.set_current_view(self.graph_view)
942 self.button = CustomButton(ICONSPATH + "play.png")
943 self.button.connect("clicked", self.button_clicked)
945 self.create_labels(self.labelsC)
946 self.create_labels(self.labelsT)
947 self.label_second_view = self.new_label_heading(self.second_view_labels[self.second_view])
949 self.update_current()
952 mainHBox = gtk.HBox(spacing=1)
954 descVBox = gtk.VBox(spacing=1)
955 descVBox.add(self.new_label_heading())
956 descVBox.add(self.new_label_heading("Time:"))
957 descVBox.add(self.new_label_heading("Steps:"))
958 descVBox.add(self.new_label_heading("Calories:"))
959 descVBox.add(self.new_label_heading("Distance:"))
960 descVBox.add(self.new_label_heading("Avg Speed:"))
962 currentVBox = gtk.VBox(spacing=1)
963 currentVBox.add(self.new_label_heading("Current"))
964 currentVBox.add(self.labelsC["timer"])
965 currentVBox.add(self.labelsC["count"])
966 currentVBox.add(self.labelsC["calories"])
967 currentVBox.add(self.labelsC["dist"])
968 currentVBox.add(self.labelsC["avgSpeed"])
969 self.currentBox = currentVBox
971 totalVBox = gtk.VBox(spacing=1)
972 totalVBox.add(self.label_second_view)
973 totalVBox.add(self.labelsT["timer"])
974 totalVBox.add(self.labelsT["count"])
975 totalVBox.add(self.labelsT["calories"])
976 totalVBox.add(self.labelsT["dist"])
977 totalVBox.add(self.labelsT["avgSpeed"])
978 self.totalBox = totalVBox
980 buttonVBox = gtk.VBox(spacing=1)
981 buttonVBox.add(self.new_label_heading(""))
982 buttonVBox.add(self.button)
983 buttonVBox.add(self.new_label_heading(""))
985 eventBox = CustomEventBox()
986 eventBox.set_visible_window(False)
987 eventBox.add(totalVBox)
988 eventBox.connect("button-press-event", self.eventBox_clicked)
989 eventBox.connect("button-release-event", self.eventBox_clicked_release)
992 mainHBox.add(buttonVBox)
993 mainHBox.add(descVBox)
994 mainHBox.add(currentVBox)
995 mainHBox.add(eventBox)
996 self.mainhbox = mainHBox
998 graph = GraphWidget()
999 self.graph_controller.set_graph(graph)
1001 eventBoxGraph = CustomEventBox()
1002 eventBoxGraph.set_visible_window(False)
1003 eventBoxGraph.add(graph)
1005 eventBoxGraph.connect("button-press-event", self.eventBoxGraph_clicked)
1006 eventBoxGraph.connect("button-release-event", self.eventBoxGraph_clicked_release)
1008 self.mainvbox = gtk.VBox()
1010 self.mainvbox.add(mainHBox)
1011 self.mainvbox.add(eventBoxGraph)
1013 self.mainvbox.show_all()
1014 self.add(self.mainvbox)
1015 self.update_aspect()
1017 self.connect("unrealize", self.close_requested)
1018 self.set_settings(True)
1019 self.connect("show-settings", self.show_settings)
1021 def eventBoxGraph_clicked(self, widget, data=None):
1022 widget.set_state(gtk.STATE_ACTIVE)
1024 def eventBoxGraph_clicked_release(self, widget, data=None):
1025 self.graph_view = self.graph_controller.next_view()
1026 self.client.set_int(GRAPHVIEW, self.graph_view)
1028 widget.set_state(gtk.STATE_NORMAL)
1030 def eventBox_clicked(self, widget, data=None):
1031 widget.set_state(gtk.STATE_ACTIVE)
1033 def eventBox_clicked_release(self, widget, data=None):
1034 widget.set_state(gtk.STATE_NORMAL)
1036 self.second_view = (self.second_view + 1) % 3
1037 self.controller.set_second_view(self.second_view)
1038 self.client.set_int(SECONDVIEW, self.second_view)
1040 def new_label_heading(self, title=""):
1041 l = gtk.Label(title)
1042 hildon.hildon_helper_set_logical_font(l, "SmallSystemFont")
1045 def create_labels(self, new_labels):
1046 for label in self.labels:
1048 hildon.hildon_helper_set_logical_font(l, "SmallSystemFont")
1049 hildon.hildon_helper_set_logical_color(l, gtk.RC_FG, gtk.STATE_NORMAL, "ActiveTextColor")
1050 new_labels[label] = l
1052 def update_aspect(self):
1053 if self.aspect == 0:
1054 self.currentBox.show_all()
1055 self.totalBox.show_all()
1056 elif self.aspect == 1:
1057 self.currentBox.show_all()
1058 self.totalBox.hide_all()
1060 self.currentBox.hide_all()
1061 self.totalBox.show_all()
1063 def update_ui_values(self, labels, values):
1064 labels["timer"].set_label(values.get_print_time())
1065 labels["count"].set_label(values.get_print_steps())
1066 labels["dist"].set_label(values.get_print_distance())
1067 labels["avgSpeed"].set_label(values.get_print_avg_speed())
1068 labels["calories"].set_label(values.get_print_calories())
1070 def update_current(self):
1071 self.update_ui_values(self.labelsC, self.controller.get_first())
1073 def update_total(self):
1074 self.update_ui_values(self.labelsT, self.controller.get_second())
1076 def show_settings(self, widget):
1077 def reset_total_counter(arg):
1078 widget.totalCounter = 0
1079 widget.totalTime = 0
1080 widget.update_total()
1081 hildon.hildon_banner_show_information(self, "None", "Total counter was resetted")
1083 def selector_changed(selector, data):
1084 widget.mode = selector.get_active(0)
1085 widget.client.set_int(MODE, widget.mode)
1086 widget.controller.set_mode(widget.mode)
1088 def selectorH_changed(selector, data):
1089 widget.height = selectorH.get_active(0)
1090 widget.client.set_int(HEIGHT, widget.height)
1091 widget.controller.set_height(widget.height)
1094 def selectorUnit_changed(selector, data):
1095 widget.unit = selectorUnit.get_active(0)
1096 widget.client.set_int(UNIT, widget.unit)
1097 widget.controller.set_unit(widget.unit)
1099 def selectorUI_changed(selector, data):
1100 widget.aspect = selectorUI.get_active(0)
1101 widget.client.set_int(ASPECT, widget.aspect)
1102 widget.update_aspect()
1104 def logButton_changed(checkButton):
1105 widget.logging = checkButton.get_active()
1106 widget.client.set_bool(LOGGING, widget.logging)
1108 def idleButton_changed(idleButton):
1109 widget.no_idle_time = idleButton.get_active()
1110 widget.client.set_bool(NOIDLETIME, widget.no_idle_time)
1111 widget.controller.set_no_idle_time(widget.no_idle_time)
1113 dialog = gtk.Dialog()
1114 dialog.set_title("Settings")
1115 dialog.add_button("OK", gtk.RESPONSE_OK)
1118 button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1119 button.set_title("Reset total counter")
1120 button.set_alignment(0, 0.8, 1, 1)
1121 button.connect("clicked", reset_total_counter)
1123 selector = hildon.TouchSelector(text=True)
1124 selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1125 selector.append_text("Walk")
1126 selector.append_text("Run")
1127 selector.connect("changed", selector_changed)
1129 modePicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1130 modePicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1131 modePicker.set_title("Select mode")
1132 modePicker.set_selector(selector)
1133 modePicker.set_active(widget.mode)
1135 selectorH = hildon.TouchSelector(text=True)
1136 selectorH.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1137 selectorH.append_text("< 1.50 m")
1138 selectorH.append_text("1.50 - 1.65 m")
1139 selectorH.append_text("1.66 - 1.80 m")
1140 selectorH.append_text("1.81 - 1.95 m")
1141 selectorH.append_text(" > 1.95 m")
1142 selectorH.connect("changed", selectorH_changed)
1144 heightPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1145 heightPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1146 heightPicker.set_title("Select height")
1147 heightPicker.set_selector(selectorH)
1148 heightPicker.set_active(widget.height)
1150 selectorUnit = hildon.TouchSelector(text=True)
1151 selectorUnit.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1152 selectorUnit.append_text("Metric (km)")
1153 selectorUnit.append_text("English (mi)")
1154 selectorUnit.connect("changed", selectorUnit_changed)
1156 unitPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1157 unitPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1158 unitPicker.set_title("Units")
1159 unitPicker.set_selector(selectorUnit)
1160 unitPicker.set_active(widget.unit)
1162 selectorUI = hildon.TouchSelector(text=True)
1163 selectorUI = hildon.TouchSelector(text=True)
1164 selectorUI.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1165 selectorUI.append_text("Show current + total")
1166 selectorUI.append_text("Show only current")
1167 selectorUI.append_text("Show only total")
1168 selectorUI.connect("changed", selectorUI_changed)
1170 UIPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1171 UIPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1172 UIPicker.set_title("Widget aspect")
1173 UIPicker.set_selector(selectorUI)
1174 UIPicker.set_active(widget.aspect)
1176 logButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1177 logButton.set_label("Log data")
1178 logButton.set_active(widget.logging)
1179 logButton.connect("toggled", logButton_changed)
1181 idleButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1182 idleButton.set_label("Pause time when not walking")
1183 idleButton.set_active(widget.no_idle_time)
1184 idleButton.connect("toggled", idleButton_changed)
1186 pan_area = hildon.PannableArea()
1189 vbox.add(modePicker)
1190 vbox.add(heightPicker)
1191 vbox.add(unitPicker)
1193 vbox.add(idleButton)
1194 #vbox.add(logButton)
1196 pan_area.add_with_viewport(vbox)
1197 pan_area.set_size_request(-1, 300)
1199 dialog.vbox.add(pan_area)
1201 response = dialog.run()
1202 #hildon.hildon_banner_show_information(self, "None", "You have to Stop/Start the counter to apply the new settings")
1205 def close_requested(self, widget):
1206 if self.pedometer is None:
1209 self.pedometer.request_stop()
1211 def update_values(self):
1212 #TODO: do not update if the widget is not on the active desktop
1213 self.label_second_view.set_label(self.second_view_labels[self.second_view])
1214 self.update_current()
1217 def button_clicked(self, button):
1218 if self.controller.is_running:
1219 self.controller.stop_pedometer()
1220 self.button.set_icon(ICONSPATH + "play.png")
1222 self.controller.start_pedometer()
1223 self.button.set_icon(ICONSPATH + "stop.png")
1225 def do_expose_event(self, event):
1226 cr = self.window.cairo_create()
1227 cr.region(event.window.get_clip_region())
1229 #cr.set_source_rgba(0.4, 0.64, 0.564, 0.5)
1230 style = self.rc_get_style()
1231 color = style.lookup_color("DefaultBackgroundColor")
1232 cr.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
1235 width = self.allocation.width
1236 height = self.allocation.height
1238 x = self.allocation.x
1239 y = self.allocation.y
1241 cr.move_to(x + radius, y)
1242 cr.line_to(x + width - radius, y)
1243 cr.curve_to(x + width - radius, y, x + width, y, x + width, y + radius)
1244 cr.line_to(x + width, y + height - radius)
1245 cr.curve_to(x + width, y + height - radius, x + width, y + height, x + width - radius, y + height)
1246 cr.line_to(x + radius, y + height)
1247 cr.curve_to(x + radius, y + height, x, y + height, x, y + height - radius)
1248 cr.line_to(x, y + radius)
1249 cr.curve_to(x, y + radius, x, y, x + radius, y)
1251 cr.set_operator(cairo.OPERATOR_SOURCE)
1254 color = style.lookup_color("ActiveTextColor")
1255 cr.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.5);
1256 cr.set_line_width(1)
1259 hildondesktop.HomePluginItem.do_expose_event(self, event)
1261 def do_realize(self):
1262 screen = self.get_screen()
1263 self.set_colormap(screen.get_rgba_colormap())
1264 self.set_app_paintable(True)
1265 hildondesktop.HomePluginItem.do_realize(self)
1267 hd_plugin_type = PedometerHomePlugin
1269 # The code below is just for testing purposes.
1270 # It allows to run the widget as a standalone process.
1271 if __name__ == "__main__":
1273 gobject.type_register(hd_plugin_type)
1274 obj = gobject.new(hd_plugin_type, plugin_id="plugin_id")
1278 ############### old pedometer.py ###
1282 logger = logging.getLogger("pedometer")
1283 logger.setLevel(logging.INFO)
1285 ch = logging.StreamHandler()
1286 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
1287 ch.setFormatter(formatter)
1288 logger.addHandler(ch)