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
35 PATH = "/apps/pedometerhomewidget"
37 HEIGHT = PATH + "/height"
38 STEP_LENGTH = PATH + "/step_length"
39 WEIGHT = PATH + "/weight"
41 SENSITIVITY = PATH + "/sensitivity"
42 ASPECT = PATH + "/aspect"
43 SECONDVIEW = PATH + "/secondview"
44 GRAPHVIEW = PATH + "/graphview"
45 NOIDLETIME = PATH + "/noidletime"
46 LOGGING = PATH + "/logging"
48 ALARM_PATH = PATH + "/alarm"
49 ALARM_ENABLE = ALARM_PATH + "/enable"
50 ALARM_FNAME = ALARM_PATH + "/fname"
51 ALARM_TYPE = ALARM_PATH + "/type"
52 ALARM_INTERVAL = ALARM_PATH + "/interval"
54 ICONSPATH = "/opt/pedometerhomewidget/"
58 class Singleton(object):
60 def __new__(cls, *args, **kwargs):
62 cls._instance = super(Singleton, cls).__new__(
66 class PedoIntervalCounter(Singleton):
76 #TODO: check if last detected step is at the end of the interval
78 def set_vals(self, coords, tval):
84 def set_mode(self, mode):
85 #runnig, higher threshold to prevent fake steps
88 self.MIN_THRESHOLD = 650.0 * (200 - self.sensitivity) / 100
89 self.MIN_TIME_STEPS = 0.35
92 self.MIN_THRESHOLD = 500.0 * (200 - self.sensitivity) / 100
93 self.MIN_TIME_STEPS = 0.5
95 def set_sensitivity(self, value):
96 self.sensitivity = value
97 self.set_mode(self.mode)
99 def calc_mean(self, vals):
104 return sum / len(vals)
107 def calc_stdev(self, vals):
109 mean = self.calc_mean(vals)
111 rez += pow(abs(mean - i), 2)
112 return math.sqrt(rez / len(vals))
114 def calc_threshold(self, vals):
117 mean = self.calc_mean(vals)
118 threshold = max (abs(mean - vmax), abs(mean - vmin))
121 def count_steps(self, vals, t):
122 threshold = self.MIN_THRESHOLD
123 mean = self.calc_mean(vals)
127 if abs(vals[i] - mean) > threshold:
129 ntime = t[i] + self.MIN_TIME_STEPS
130 while i < len(vals) and t[i] < ntime:
135 def get_best_values(self, x, y, z):
136 dev1 = self.calc_stdev(x)
137 dev2 = self.calc_stdev(y)
138 dev3 = self.calc_stdev(z)
139 dev_max = max(dev1, dev2, dev3)
141 if (abs(dev1 - dev_max) < 0.001):
142 logger.info("X chosen as best axis, stdev %f" % dev1)
144 elif (abs(dev2 - dev_max) < 0.001):
145 logger.info("Y chosen as best axis, stdev %f" % dev2)
148 logger.info("Z chosen as best axis, stdev %f" % dev3)
151 def number_steps(self):
152 vals = self.get_best_values(self.x, self.y, self.z)
153 return self.count_steps(vals, self.t)
156 def __init__(self, time=0, steps=0, dist=0, calories=0):
159 self.calories = calories
162 def __add__(self, other):
163 return PedoValues(self.time + other.time,
164 self.steps + other.steps,
165 self.dist + other.dist,
166 self.calories + other.calories)
168 def __sub__(self, other):
169 return PedoValues(self.time - other.time,
170 self.steps - other.steps,
171 self.dist - other.dist,
172 self.calories - other.calories)
174 def get_print_time(self):
176 hours = int(tdelta / 3600)
177 tdelta -= 3600 * hours
178 mins = int(tdelta / 60)
181 strtime = "%.2d:%.2d:%.2d" % (hours, mins, secs)
184 def get_print_distance(self):
188 return "%.2f km" % (self.dist / 1000)
190 return "%.2f mi" % (self.dist / 1609.344)
193 return "%d m" % self.dist
195 return "%d ft" % int(self.dist * 3.2808)
197 def get_avg_speed(self):
207 speed = 1.0 * self.dist / self.time
210 def get_print_avg_speed(self):
222 return "N/A " + suffix
223 speed = 1.0 * self.dist / self.time
224 #convert from meters per second to km/h or mi/h
226 return "%.2f %s" % (speed, suffix)
228 def get_print_steps(self):
229 return str(self.steps)
231 def get_print_calories(self):
232 return "%.2f" % self.calories
234 class PedoRepository(Singleton):
238 raise NotImplementedError("Must be implemented by subclass")
241 raise NotImplementedError("Must be implemented by subclass")
243 def reset_values(self):
247 def get_history_count(self):
248 """return the number of days in the log"""
251 def get_values(self):
254 def add_values(self, values, when=None):
257 """add PedoValues values to repository """
259 self.values[when] = self.values[when] + values
261 self.values[when] = values
263 def get_last_7_days(self):
268 ret.append(self.values[day])
270 ret.append(PedoValues())
271 day = day - timedelta(days=1)
274 def get_last_weeks(self):
275 delta = timedelta(days=1)
277 week = int(date.today().strftime("%W"))
282 val += self.values[day]
285 w = int(day.strftime("%W"))
295 def get_alltime_values(self):
297 for k, v in self.values.iteritems():
301 def get_today_values(self):
303 return self.values[date.today()]
307 def get_this_week_values(self):
312 ret += self.values[day]
315 if day.weekday() == 0:
317 day = day - timedelta(days=1)
321 class PedoRepositoryXML(PedoRepository):
322 DIR = os.path.join(os.path.expanduser("~"), ".pedometer")
323 FILE = os.path.join(DIR, "data.xml")
324 FILE2 = os.path.join(DIR, "pickle.log")
326 if not os.path.exists(self.DIR):
327 os.makedirs(self.DIR)
328 PedoRepository.__init__(self)
332 f = open(self.FILE, "r")
333 dom = parseString(f.read())
334 values = dom.getElementsByTagName("pedometer")[0]
335 for v in values.getElementsByTagName("date"):
336 d = int(v.getAttribute("ordinal_day"))
337 steps = int(v.getAttribute("steps"))
338 calories = float(v.getAttribute("calories"))
339 dist = float(v.getAttribute("dist"))
340 time = float(v.getAttribute("time"))
341 day = date.fromordinal(d)
342 self.values[day] = PedoValues(time, steps, dist, calories)
346 logger.error("Error while loading data from xml file: %s" % e)
350 f = open(self.FILE, "w")
352 impl = getDOMImplementation()
354 newdoc = impl.createDocument(None, "pedometer", None)
355 top_element = newdoc.documentElement
356 for k, v in self.values.iteritems():
357 d = newdoc.createElement('date')
358 d.setAttribute("day", str(k.isoformat()))
359 d.setAttribute("ordinal_day", str(k.toordinal()))
360 d.setAttribute("steps", str(v.steps))
361 d.setAttribute("time", str(v.time))
362 d.setAttribute("dist", str(v.dist))
363 d.setAttribute("calories", str(v.calories))
364 top_element.appendChild(d)
366 newdoc.appendChild(top_element)
368 #f.write(newdoc.toprettyxml())
371 logger.error("Error while saving data to xml file: %s" % e)
373 class PedoRepositoryPickle(PedoRepository):
374 DIR = os.path.join(os.path.expanduser("~"), ".pedometer")
375 FILE = os.path.join(DIR, "pickle.log")
378 if not os.path.exists(self.DIR):
379 os.makedirs(self.DIR)
380 PedoRepository.__init__(self)
384 f = open(self.FILE, "rb")
385 self.values = pickle.load(f)
388 logger.error("Error while loading pickle file: %s" % e)
392 f = open(self.FILE, "wb")
393 pickle.dump(self.values, f)
396 logger.error("Error while saving data to pickle: %s" % e)
398 class PedoController(Singleton):
402 #what to display in second view - 0 - alltime, 1 - today, 2 - week
404 callback_update_ui = None
408 #values for the two views in the widget ( current and day/week/alltime)
409 v = [PedoValues(), PedoValues()]
417 midnight_source_id = None
420 self.pedometer = PedoCounter(self.steps_detected)
421 self.pedometerInterval = PedoIntervalCounter()
422 self.pedometerInterval.set_mode(self.mode)
423 self.repository = PedoRepositoryXML()
424 self.repository.load()
428 if not self.midnight_set:
429 self.update_at_midnight()
430 self.midnight_set = True
432 def update_at_midnight(self):
433 next_day = date.today() + timedelta(days=1)
434 diff = time.mktime(next_day.timetuple()) - time.time()
436 self.midnight_source_id = gobject.timeout_add_seconds(diff, self.midnight_callback, True)
438 def stop_midnight_callback(self):
439 if self.midnight_source_id is not None:
440 gobject.source_remove(self.midnight_source_id)
442 def midnight_callback(self, first=False):
446 self.midnight_source_id = gobject.timeout_add_seconds(24*3600, self.midnight_callback)
451 def load_values(self):
452 if self.second_view == 0:
453 self.v[1] = self.repository.get_alltime_values()
454 elif self.second_view == 1:
455 self.v[1] = self.repository.get_today_values()
457 self.v[1] = self.repository.get_this_week_values()
459 def save_values(self):
460 self.repository.add_values(self.v[0])
461 self.repository.save()
464 def start_pedometer(self):
465 self.v[0] = PedoValues()
466 self.last_time = time.time()
467 self.is_running = True
468 self.pedometer.start()
471 def reset_all_values(self):
472 self.repository.reset_values()
473 self.v[0] = PedoValues()
474 self.v[1] = PedoValues()
477 def stop_pedometer(self):
478 self.is_running = False
479 self.pedometer.request_stop()
484 def get_second(self):
486 return self.v[0] + self.v[1]
490 def update_current(self):
492 Update distance and calories for current values based on new height, mode values
494 self.v[0].dist = self.get_distance(self.v[0].steps)
495 self.v[0].calories = self.get_calories(self.v[0].steps)
497 def steps_detected(self, cnt, last_steps=False):
498 if not last_steps and cnt == 0 and self.no_idle_time:
499 logger.info("No steps detected, timer is paused")
501 self.v[0].steps += cnt
502 self.v[0].dist += self.get_distance(cnt)
503 self.v[0].calories += self.get_calories(self.get_distance(cnt))
504 self.v[0].time += time.time() - self.last_time
510 self.last_time = time.time()
512 def get_calories(self, distance):
513 """calculate lost calories for the distance and weight given as parameters
515 #different coefficient for running and walking
521 #convert distance from meters to miles
522 distance *= 0.000621371192
525 #convert weight from kg to pounds
528 return weight * distance * coef
530 def set_mode(self, mode):
532 self.set_height(self.height_interval)
533 self.pedometerInterval.set_mode(self.mode)
536 def set_unit(self, new_unit):
542 def get_str_weight_unit(self):
548 def set_weight(self, value):
552 def get_weight(self):
555 def set_sensitivity(self, value):
556 self.sensitivity = value
557 self.pedometerInterval.set_sensitivity(value)
559 def get_sensitivity(self):
560 return self.sensitivity
562 def set_second_view(self, second_view):
563 self.second_view = second_view
567 def set_callback_ui(self, func):
568 self.callback_update_ui = func
570 def set_height(self, height_interval, step_length=None):
571 self.height_interval = height_interval
572 if step_length is None:
573 step_length = self.STEP_LENGTH
574 #set height, will affect the distance
575 if height_interval == 0:
576 self.STEP_LENGTH = 0.59
577 elif height_interval == 1:
578 self.STEP_LENGTH = 0.64
579 elif height_interval == 2:
580 self.STEP_LENGTH = 0.71
581 elif height_interval == 3:
582 self.STEP_LENGTH = 0.77
583 elif height_interval == 4:
584 self.STEP_LENGTH = 0.83
585 elif height_interval == 5:
586 self.STEP_LENGTH = step_length
587 #increase step length if RUNNING
589 self.STEP_LENGTH *= 1.45
592 def set_no_idle_time(self, value):
593 self.no_idle_time = value
595 def get_distance(self, steps=None):
598 return self.STEP_LENGTH * steps;
600 def add_observer(self, func):
602 self.observers.index(func)
604 self.observers.append(func)
606 def remove_observer(self, func):
607 self.observers.remove(func)
609 def notify(self, optional=False):
610 if self.callback_update_ui is not None:
611 self.callback_update_ui()
613 for func in self.observers:
616 class AlarmController(Singleton):
618 fname = "/home/user/MyDocs/.sounds/Ringtones/Bicycle.aac"
624 pedo_controller = None
627 self.client = gconf.client_get_default()
629 self.enable = self.client.get_bool(ALARM_ENABLE)
630 self.fname = self.client.get_string(ALARM_FNAME)
631 self.interval = self.client.get_int(ALARM_INTERVAL)
632 self.type = self.client.get_int(ALARM_TYPE)
634 self.pedo_controller = PedoController()
637 self.pedo_controller.add_observer(self.update)
638 self.start_value = self.pedo_controller.get_first()
640 def init_player(self):
641 self.player = gst.element_factory_make("playbin2", "player")
642 fakesink = gst.element_factory_make("fakesink", "fakesink")
643 self.player.set_property("video-sink", fakesink)
645 bus = self.player.get_bus()
646 bus.add_signal_watch()
647 bus.connect("message", self.on_message)
649 def on_message(self, bus, message):
651 if t == gst.MESSAGE_EOS:
652 self.player.set_state(gst.STATE_NULL)
653 self.is_playing = False
654 elif t == gst.MESSAGE_ERROR:
655 self.player.set_state(gst.STATE_NULL)
656 self.is_playing = False
657 err, debug = message.parse_error()
658 logger.error("ERROR: %s, %s" % (err, debug) )
660 def update(self, optional):
661 diff = self.pedo_controller.get_first() - self.start_value
662 if self.type == 0 and diff.time >= self.interval * 60 or \
663 self.type == 1 and diff.steps >= self.interval or \
664 self.type == 2 and diff.dist >= self.interval or \
665 self.type == 3 and diff.calories >= self.interval:
667 #get new instance of current values
668 self.start_value = PedoValues() + self.pedo_controller.get_first()
669 logger.info("Alarm!")
672 if self.player is None:
675 self.player.set_state(gst.STATE_NULL)
676 self.is_playing = False
678 self.player.set_property("uri", "file://" + self.fname)
679 self.player.set_state(gst.STATE_PLAYING)
680 self.is_playing = True
683 self.player.set_state(gst.STATE_NULL)
685 def set_enable(self, value):
687 self.client.set_bool(ALARM_ENABLE, value)
690 self.pedo_controller.add_observer(self.update)
691 self.start_value = self.pedo_controller.get_first()
695 self.pedo_controller.remove_observer(self.update)
697 def get_enable(self):
700 def set_alarm_file(self, fname):
702 self.client.set_string(ALARM_FNAME, fname)
704 def get_alarm_file(self):
705 if self.fname == None:
709 def set_interval(self, interval):
710 self.interval = interval
711 self.client.set_int(ALARM_INTERVAL, interval)
713 def get_interval(self):
716 def set_type(self, type):
718 self.client.set_int(ALARM_TYPE, type)
723 class PedoCounter(Singleton):
724 COORD_FNAME = "/sys/class/i2c-adapter/i2c-3/3-001d/coord"
725 COORD_FNAME_SDK = "/home/andrei/pedometer-widget-0.1/date.txt"
726 LOGFILE = "/home/user/log_pedometer"
727 #time in ms between two accelerometer data reads
728 COORD_GET_INTERVAL = 25
732 interval_counter = None
733 stop_requested = False
734 update_function = None
738 def __init__(self, update_function=None):
739 if not os.path.exists(self.COORD_FNAME):
740 self.COORD_FNAME = self.COORD_FNAME_SDK
742 self.interval_counter = PedoIntervalCounter()
743 self.update_function = update_function
745 def set_logging(self, value):
748 def get_rotation(self):
749 f = open(self.COORD_FNAME, 'r')
750 coords = [int(w) for w in f.readline().split()]
755 logger.info("Counter started")
756 self.isRunning = True
757 self.stop_requested = False
759 fname = "%d_%d_%d_%d_%d_%d" % time.localtime()[0:6]
760 self.file = open(self.LOGFILE + fname + ".txt", "w")
761 gobject.idle_add(self.run)
764 self.coords = [[], [], []]
765 self.stime = time.time()
767 gobject.timeout_add(self.COORD_GET_INTERVAL, self.read_coords)
770 def read_coords(self):
771 x, y, z = self.get_rotation()
772 self.coords[0].append(int(x))
773 self.coords[1].append(int(y))
774 self.coords[2].append(int(z))
775 now = time.time() - self.stime
777 self.file.write("%d %d %d %f\n" % (self.coords[0][-1], self.coords[1][-1], self.coords[2][-1], now))
782 if self.t[-1] > self.COUNT_INTERVAL or self.stop_requested:
784 gobject.idle_add(self.stop_interval)
787 def stop_interval(self):
788 self.interval_counter.set_vals(self.coords, self.t)
789 cnt = self.interval_counter.number_steps()
791 logger.info("Number of steps detected for last interval %d, number of coords: %d" % (cnt, len(self.t)))
793 gobject.idle_add(self.update_function, cnt, self.stop_requested)
795 if self.stop_requested:
796 gobject.idle_add(self.stop)
798 gobject.idle_add(self.run)
804 logger.info("Counter has finished")
806 def request_stop(self):
807 self.stop_requested = True
808 self.isRunning = False
810 class CustomButton(hildon.Button):
811 def __init__(self, icon):
812 hildon.Button.__init__(self, gtk.HILDON_SIZE_AUTO_WIDTH, hildon.BUTTON_ARRANGEMENT_VERTICAL)
814 self.set_size_request(int(32 * 1.4), int(30 * 1.0))
815 self.retval = self.connect("expose_event", self.expose)
817 def set_icon(self, icon):
820 def expose(self, widget, event):
821 self.context = widget.window.cairo_create()
822 self.context.rectangle(event.area.x, event.area.y,
823 event.area.width, event.area.height)
826 rect = self.get_allocation()
827 self.context.rectangle(rect.x, rect.y, rect.width, rect.height)
828 self.context.set_source_rgba(1, 1, 1, 0)
830 style = self.rc_get_style()
831 color = style.lookup_color("DefaultBackgroundColor")
832 if self.state == gtk.STATE_ACTIVE:
833 style = self.rc_get_style()
834 color = style.lookup_color("SelectionColor")
835 self.context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
838 #img = cairo.ImageSurface.create_from_png(self.icon)
840 #self.context.set_source_surface(img)
841 #self.context.set_source_surface(img, rect.width/2 - img.get_width() /2, 0)
843 img.set_from_file(self.icon)
844 buf = img.get_pixbuf()
845 buf = buf.scale_simple(int(32 * 1.5), int(30 * 1.5), gtk.gdk.INTERP_BILINEAR)
847 self.context.set_source_pixbuf(buf, rect.x + (event.area.width / 2 - 15) - 8, rect.y + 1)
848 self.context.scale(200, 200)
853 class CustomEventBox(gtk.EventBox):
856 gtk.EventBox.__init__(self)
858 def do_expose_event(self, event):
859 self.context = self.window.cairo_create()
860 self.context.rectangle(event.area.x, event.area.y,
861 event.area.width, event.area.height)
864 rect = self.get_allocation()
865 self.context.rectangle(rect.x, rect.y, rect.width, rect.height)
867 if self.state == gtk.STATE_ACTIVE:
868 style = self.rc_get_style()
869 color = style.lookup_color("SelectionColor")
870 self.context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
872 self.context.set_source_rgba(1, 1, 1, 0)
875 gtk.EventBox.do_expose_event(self, event)
877 class GraphController(Singleton):
878 ytitles = ["Steps", "Average Speed", "Distance", "Calories"]
879 xtitles = ["Day", "Week"] # "Today"]
882 self.repository = PedoRepositoryXML()
884 PedoController().add_observer(self.update_ui)
886 def set_graph(self, widget):
890 def set_current_view(self, view):
892 current_view % len(ytitles) - gives the ytitle
893 current_view / len(ytitles) - gives the xtitle
895 self.current_view = view
897 if self.current_view == len(self.ytitles) * len(self.xtitles):
898 self.current_view = 0
899 self.x_id = self.current_view / len(self.ytitles)
900 self.y_id = self.current_view % len(self.ytitles)
903 self.set_current_view(self.current_view+1)
905 return self.current_view
907 def last_weeks_labels(self):
909 delta = timedelta(days=7)
912 ret.append(d.strftime("Week %W"))
916 def compute_values(self):
919 values = self.repository.get_last_7_days()
921 delta = timedelta(days=1)
923 labels.append(d.ctime().split()[0])
927 values = self.repository.get_last_weeks()
930 labels.append(d.strftime("Week %W"))
931 d = d - timedelta(days=7)
933 values = self.repository.get_today()
937 yvalues = [line.steps for line in values]
939 yvalues = [line.get_avg_speed() for line in values]
941 yvalues = [line.dist for line in values]
943 yvalues = [line.calories for line in values]
945 #determine values for y lines in graph
946 diff = self.get_best_interval_value(max(yvalues))
949 ytext.append(str(int(i*diff)))
951 if self.widget is not None:
954 self.widget.values = yvalues
955 self.widget.ytext = ytext
956 self.widget.xtext = labels
957 self.widget.max_value = diff * 5
958 self.widget.text = self.xtitles[self.x_id] + " / " + self.ytitles[self.y_id]
959 self.widget.queue_draw()
961 logger.error("Widget not set in GraphController")
963 def get_best_interval_value(self, max_value):
964 diff = 1.0 * max_value / 5
965 l = len(str(int(diff)))
966 d = math.pow(10, l/2)
967 val = int(math.ceil(1.0 * diff / d)) * d
972 def update_ui(self, optional=False):
973 """update graph values every x seconds"""
974 if optional and self.last_update - time.time() < 600:
976 if self.widget is None:
979 self.compute_values()
980 self.last_update = time.time()
982 class GraphWidget(gtk.DrawingArea):
985 gtk.DrawingArea.__init__(self)
986 self.set_size_request(-1, 150)
990 self.ytext = [" 0", "1000", "2000", "3000", "4000", "5000"]
991 self.xtext = ["Monday", "Tuesday", "Wednesday","Thursday", "Friday", "Saturday", "Sunday"]
992 self.values = [1500, 3400, 4000, 3600, 3200, 0, 4500]
993 self.max_value = 5000
994 self.text = "All time steps"
996 def do_expose_event(self, event):
997 context = self.window.cairo_create()
999 # set a clip region for the expose event
1000 context.rectangle(event.area.x, event.area.y,
1001 event.area.width, event.area.height)
1006 context.set_operator(cairo.OPERATOR_SOURCE)
1007 style = self.rc_get_style()
1009 if self.state == gtk.STATE_ACTIVE:
1010 color = style.lookup_color("SelectionColor")
1012 color = style.lookup_color("DefaultBackgroundColor")
1013 context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75)
1025 rect = self.get_allocation()
1029 cr.select_font_face("Purisa", cairo.FONT_SLANT_NORMAL,
1030 cairo.FONT_WEIGHT_NORMAL)
1031 cr.set_font_size(13)
1033 #check space needed to display ylabels
1034 te = cr.text_extents(self.ytext[-1])
1035 border_left = te[2] + 7
1037 cr.set_source_rgb(1, 1, 1)
1038 cr.move_to(border_left, space_above)
1039 cr.line_to(border_left, y-space_below)
1040 cr.set_line_width(2)
1043 cr.move_to(border_left, y-space_below)
1044 cr.line_to(x-border_right, y-space_below)
1045 cr.set_line_width(2)
1048 ydiff = (y-space_above-space_below) / self.yvalues
1049 for i in range(self.yvalues):
1050 yy = y-space_below-ydiff*(i+1)
1051 cr.move_to(border_left, yy)
1052 cr.line_to(x-border_right, yy)
1053 cr.set_line_width(0.8)
1058 yy = y - space_below - ydiff*i + 5
1059 te = cr.text_extents(self.ytext[i])
1061 cr.move_to(border_left-te[2]-2, yy)
1062 cr.show_text(self.ytext[i])
1064 cr.set_font_size(15)
1065 te = cr.text_extents(self.text)
1066 cr.move_to((x-te[2])/2, y-5)
1067 cr.show_text(self.text)
1069 graph_x_space = x - border_left - border_right
1070 graph_y_space = y - space_below - space_above
1071 bar_width = graph_x_space*0.75 / len(self.values)
1072 bar_distance = graph_x_space*0.25 / (1+len(self.values))
1074 #set dummy max value to avoid exceptions
1075 if self.max_value == 0:
1076 self.max_value = 100
1077 for i in range(len(self.values)):
1078 xx = border_left + (i+1)*bar_distance + i * bar_width
1080 height = graph_y_space * (1.0 * self.values[i] / self.max_value)
1081 cr.set_source_rgba(1, 1, 1, 0.75)
1082 cr.rectangle(int(xx), int(yy-height), int(bar_width), int(height))
1085 cr.set_source_rgba(1, 1, 1, 1)
1086 cr.select_font_face("Purisa", cairo.FONT_SLANT_NORMAL,
1087 cairo.FONT_WEIGHT_NORMAL)
1088 cr.set_font_size(13)
1090 cr.rotate(2*math.pi * (-45) / 180)
1091 for i in range(len(self.values)):
1092 xx = y - space_below - 10
1093 yy = border_left + (i+1)*bar_distance + i * bar_width
1094 cr.move_to(-xx, yy + bar_width*1.25 / 2)
1095 cr.show_text(self.xtext[i])
1097 class PedometerHomePlugin(hildondesktop.HomePluginItem):
1101 labels = ["timer", "count", "dist", "avgSpeed", "calories"]
1106 #second view ( day / week/ alltime)
1109 second_view_labels = ["All-time", "Today", "This week"]
1112 graph_controller = None
1122 no_idle_time = False
1126 hildondesktop.HomePluginItem.__init__(self)
1128 gobject.type_register(CustomEventBox)
1129 gobject.type_register(GraphWidget)
1131 self.client = gconf.client_get_default()
1133 self.mode = self.client.get_int(MODE)
1134 self.height = self.client.get_int(HEIGHT)
1135 self.step_length = self.client.get_float(STEP_LENGTH)
1136 self.weight = self.client.get_int(WEIGHT)
1137 self.unit = self.client.get_int(UNIT)
1138 self.sensitivity = self.client.get_int(SENSITIVITY)
1139 self.aspect = self.client.get_int(ASPECT)
1140 self.second_view = self.client.get_int(SECONDVIEW)
1141 self.graph_view = self.client.get_int(GRAPHVIEW)
1142 self.no_idle_time = self.client.get_bool(NOIDLETIME)
1143 self.logging = self.client.get_bool(LOGGING)
1145 self.controller = PedoController()
1146 self.controller.set_height(self.height, self.step_length)
1147 self.controller.set_weight(self.weight)
1148 self.controller.set_mode(self.mode)
1149 self.controller.set_unit(self.unit)
1150 self.controller.set_sensitivity(self.sensitivity)
1151 self.controller.set_second_view(self.second_view)
1152 self.controller.set_callback_ui(self.update_values)
1153 self.controller.set_no_idle_time(self.no_idle_time)
1155 self.graph_controller = GraphController()
1156 self.graph_controller.set_current_view(self.graph_view)
1158 self.alarm_controller = AlarmController()
1160 self.button = CustomButton(ICONSPATH + "play.png")
1161 self.button.connect("clicked", self.button_clicked)
1163 self.create_labels(self.labelsC)
1164 self.create_labels(self.labelsT)
1165 self.label_second_view = self.new_label_heading(self.second_view_labels[self.second_view])
1167 self.update_current()
1170 mainHBox = gtk.HBox(spacing=1)
1172 descVBox = gtk.VBox(spacing=1)
1173 descVBox.add(self.new_label_heading())
1174 descVBox.add(self.new_label_heading("Time:"))
1175 descVBox.add(self.new_label_heading("Steps:"))
1176 descVBox.add(self.new_label_heading("Calories:"))
1177 descVBox.add(self.new_label_heading("Distance:"))
1178 descVBox.add(self.new_label_heading("Avg Speed:"))
1180 currentVBox = gtk.VBox(spacing=1)
1181 currentVBox.add(self.new_label_heading("Current"))
1182 currentVBox.add(self.labelsC["timer"])
1183 currentVBox.add(self.labelsC["count"])
1184 currentVBox.add(self.labelsC["calories"])
1185 currentVBox.add(self.labelsC["dist"])
1186 currentVBox.add(self.labelsC["avgSpeed"])
1187 self.currentBox = currentVBox
1189 totalVBox = gtk.VBox(spacing=1)
1190 totalVBox.add(self.label_second_view)
1191 totalVBox.add(self.labelsT["timer"])
1192 totalVBox.add(self.labelsT["count"])
1193 totalVBox.add(self.labelsT["calories"])
1194 totalVBox.add(self.labelsT["dist"])
1195 totalVBox.add(self.labelsT["avgSpeed"])
1196 self.totalBox = totalVBox
1198 buttonVBox = gtk.VBox(spacing=1)
1199 buttonVBox.add(self.new_label_heading(""))
1200 buttonVBox.add(self.button)
1201 buttonVBox.add(self.new_label_heading(""))
1203 eventBox = CustomEventBox()
1204 eventBox.set_visible_window(False)
1205 eventBox.add(totalVBox)
1206 eventBox.connect("button-press-event", self.eventBox_clicked)
1207 eventBox.connect("button-release-event", self.eventBox_clicked_release)
1209 mainHBox.add(buttonVBox)
1210 mainHBox.add(descVBox)
1211 mainHBox.add(currentVBox)
1212 mainHBox.add(eventBox)
1213 self.mainhbox = mainHBox
1215 graph = GraphWidget()
1216 self.graph_controller.set_graph(graph)
1218 eventBoxGraph = CustomEventBox()
1219 eventBoxGraph.set_visible_window(False)
1220 eventBoxGraph.add(graph)
1222 eventBoxGraph.connect("button-press-event", self.eventBoxGraph_clicked)
1223 eventBoxGraph.connect("button-release-event", self.eventBoxGraph_clicked_release)
1224 self.graphBox = eventBoxGraph
1226 self.mainvbox = gtk.VBox()
1228 self.mainvbox.add(mainHBox)
1229 self.mainvbox.add(eventBoxGraph)
1231 self.mainvbox.show_all()
1232 self.add(self.mainvbox)
1233 self.update_aspect()
1235 self.connect("unrealize", self.close_requested)
1236 self.set_settings(True)
1237 self.connect("show-settings", self.show_settings)
1239 def eventBoxGraph_clicked(self, widget, data=None):
1240 widget.set_state(gtk.STATE_ACTIVE)
1242 def eventBoxGraph_clicked_release(self, widget, data=None):
1243 self.graph_view = self.graph_controller.next_view()
1244 self.client.set_int(GRAPHVIEW, self.graph_view)
1246 widget.set_state(gtk.STATE_NORMAL)
1248 def eventBox_clicked(self, widget, data=None):
1249 widget.set_state(gtk.STATE_ACTIVE)
1251 def eventBox_clicked_release(self, widget, data=None):
1252 widget.set_state(gtk.STATE_NORMAL)
1254 self.second_view = (self.second_view + 1) % 3
1255 self.controller.set_second_view(self.second_view)
1256 self.client.set_int(SECONDVIEW, self.second_view)
1258 def new_label_heading(self, title=""):
1259 l = gtk.Label(title)
1260 hildon.hildon_helper_set_logical_font(l, "SmallSystemFont")
1263 def create_labels(self, new_labels):
1264 for label in self.labels:
1266 hildon.hildon_helper_set_logical_font(l, "SmallSystemFont")
1267 hildon.hildon_helper_set_logical_color(l, gtk.RC_FG, gtk.STATE_NORMAL, "ActiveTextColor")
1268 new_labels[label] = l
1270 def update_aspect(self):
1273 self.graphBox.hide_all()
1275 self.graphBox.show_all()
1277 if self.aspect == 0 or self.aspect == 1:
1278 self.currentBox.show_all()
1279 self.totalBox.show_all()
1280 elif self.aspect == 2:
1281 self.currentBox.show_all()
1282 self.totalBox.hide_all()
1284 self.currentBox.hide_all()
1285 self.totalBox.show_all()
1287 x,y = self.size_request()
1290 def update_ui_values(self, labels, values):
1291 labels["timer"].set_label(values.get_print_time())
1292 labels["count"].set_label(values.get_print_steps())
1293 labels["dist"].set_label(values.get_print_distance())
1294 labels["avgSpeed"].set_label(values.get_print_avg_speed())
1295 labels["calories"].set_label(values.get_print_calories())
1297 def update_current(self):
1298 self.update_ui_values(self.labelsC, self.controller.get_first())
1300 def update_total(self):
1301 self.update_ui_values(self.labelsT, self.controller.get_second())
1303 def show_alarm_settings(self, main_button):
1304 def choose_file(widget):
1305 file = hildon.FileChooserDialog(self, gtk.FILE_CHOOSER_ACTION_OPEN, hildon.FileSystemModel() )
1307 if ( file.run() == gtk.RESPONSE_OK):
1308 fname = file.get_filename()
1309 widget.set_value(fname)
1310 self.alarm_controller.set_alarm_file(fname)
1313 def test_sound(button):
1315 self.alarm_controller.play()
1316 except Exception, e:
1317 logger.error("Could not play alarm sound: %s" % e)
1318 hildon.hildon_banner_show_information(self, "None", "Could not play alarm sound")
1320 def enableButton_changed(button):
1321 value = button.get_active()
1322 self.alarm_controller.set_enable(value)
1324 main_button.set_value("Enabled")
1326 main_button.set_value("Disabled")
1328 def selectorType_changed(selector, data, labelEntry2):
1329 self.alarm_controller.set_type(selector.get_active(0))
1330 labelEntry2.set_label(suffix[self.alarm_controller.get_type()])
1332 dialog = gtk.Dialog()
1333 dialog.set_title("Alarm settings")
1334 dialog.add_button("OK", gtk.RESPONSE_OK)
1336 enableButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1337 enableButton.set_label("Enable alarm")
1338 enableButton.set_active(self.alarm_controller.get_enable())
1339 enableButton.connect("toggled", enableButton_changed)
1341 testButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1342 testButton.set_alignment(0, 0.8, 1, 1)
1343 testButton.set_title("Test sound")
1344 testButton.connect("pressed", test_sound)
1346 fileButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1347 fileButton.set_alignment(0, 0.8, 1, 1)
1348 fileButton.set_title("Alarm sound")
1349 fileButton.set_value(self.alarm_controller.get_alarm_file())
1350 fileButton.connect("pressed", choose_file)
1352 labelEntry = gtk.Label("Notify every:")
1353 suffix = ["mins", "steps", "m/ft", "calories"]
1354 labelEntry2 = gtk.Label(suffix[self.alarm_controller.get_type()])
1355 intervalEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO_WIDTH)
1356 intervalEntry.set_text(str(self.alarm_controller.get_interval()))
1358 selectorType = hildon.TouchSelector(text=True)
1359 selectorType.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1360 selectorType.append_text("Time")
1361 selectorType.append_text("Steps")
1362 selectorType.append_text("Distance")
1363 selectorType.append_text("Calories")
1364 selectorType.connect("changed", selectorType_changed, labelEntry2)
1366 typePicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1367 typePicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1368 typePicker.set_title("Alarm type")
1369 typePicker.set_selector(selectorType)
1370 typePicker.set_active(self.alarm_controller.get_type())
1373 hbox.add(labelEntry)
1374 hbox.add(intervalEntry)
1375 hbox.add(labelEntry2)
1377 dialog.vbox.add(enableButton)
1378 dialog.vbox.add(fileButton)
1379 dialog.vbox.add(testButton)
1380 dialog.vbox.add(typePicker)
1381 dialog.vbox.add(hbox)
1384 response = dialog.run()
1385 if response != gtk.RESPONSE_OK:
1388 value = int(intervalEntry.get_text())
1389 self.alarm_controller.set_interval(value)
1392 hildon.hildon_banner_show_information(self, "None", "Invalid interval")
1396 def show_settings(self, widget):
1397 def reset_total_counter(arg):
1398 note = hildon.hildon_note_new_confirmation(self.dialog, "Are you sure you want to delete all your pedometer history?")
1400 if ret == gtk.RESPONSE_OK:
1401 self.controller.reset_all_values()
1402 hildon.hildon_banner_show_information(self, "None", "All history was deleted")
1405 def alarmButton_pressed(widget):
1406 self.show_alarm_settings(widget)
1408 def selector_changed(selector, data):
1409 widget.mode = selector.get_active(0)
1410 widget.client.set_int(MODE, widget.mode)
1411 widget.controller.set_mode(widget.mode)
1413 def selectorUnit_changed(selector, data):
1414 widget.unit = selector.get_active(0)
1415 widget.client.set_int(UNIT, widget.unit)
1416 widget.controller.set_unit(widget.unit)
1418 update_weight_button()
1419 stepLengthButton_value_update()
1421 def selectorUI_changed(selector, data):
1422 widget.aspect = selectorUI.get_active(0)
1423 widget.client.set_int(ASPECT, widget.aspect)
1424 widget.update_aspect()
1426 def logButton_changed(checkButton):
1427 widget.logging = checkButton.get_active()
1428 widget.client.set_bool(LOGGING, widget.logging)
1430 def idleButton_changed(idleButton):
1431 widget.no_idle_time = idleButton.get_active()
1432 widget.client.set_bool(NOIDLETIME, widget.no_idle_time)
1433 widget.controller.set_no_idle_time(widget.no_idle_time)
1435 def update_weight_button():
1436 weightButton.set_value(str(self.controller.get_weight()) + \
1437 " " + self.controller.get_str_weight_unit() )
1439 def weight_dialog(button):
1440 dialog = gtk.Dialog("Weight", self.dialog)
1441 dialog.add_button("OK", gtk.RESPONSE_OK)
1443 label = gtk.Label("Weight:")
1445 entry.set_text(str(self.controller.get_weight()))
1447 suffixLabel = gtk.Label(self.controller.get_str_weight_unit())
1452 hbox.add(suffixLabel)
1454 dialog.vbox.add(hbox)
1457 response = dialog.run()
1458 if response != gtk.RESPONSE_OK:
1461 value = int(entry.get_text())
1464 self.controller.set_weight(value)
1465 self.client.set_int(WEIGHT, value)
1466 update_weight_button()
1469 hildon.hildon_banner_show_information(self, "None", "Invalid weight")
1472 def sensitivity_dialog(button):
1473 def seekbar_changed(seekbar):
1474 label.set_text(str(seekbar.get_position()) + " %")
1476 dialog = gtk.Dialog("Sensitivity", self.dialog)
1477 dialog.add_button("OK", gtk.RESPONSE_OK)
1478 seekbar = hildon.Seekbar()
1479 seekbar.set_size_request(400, -1)
1480 seekbar.set_total_time(200)
1481 seekbar.set_position(self.controller.get_sensitivity())
1482 seekbar.connect("value-changed", seekbar_changed)
1486 label = gtk.Label(str(self.controller.get_sensitivity()) + " %")
1487 label.set_size_request(30, -1)
1490 dialog.vbox.add(hbox)
1493 if dialog.run() == gtk.RESPONSE_OK:
1494 value = seekbar.get_position()
1495 self.client.set_int(SENSITIVITY, value)
1496 self.controller.set_sensitivity(value)
1497 widget.sensitivity = value
1498 button.set_value(str(self.controller.get_sensitivity()) + " %")
1502 def stepLengthButton_value_update():
1503 if widget.height == 5:
1504 l_unit = ["m", "ft"]
1505 stepLengthButton.set_value("Custom value: %.2f %s" % (widget.step_length, l_unit[widget.unit]))
1507 h = [ ["< 1.50 m", "1.50 - 1.65 m", "1.66 - 1.80 m", "1.81 - 1.95 m", " > 1.95 m"],
1508 ["< 5 ft", "5 - 5.5 ft", "5.5 - 6 ft", "6 - 6.5 ft", "> 6.5 ft"]]
1509 str = "Using predefined value for height: %s" % h[widget.unit][widget.height]
1510 stepLengthButton.set_value(str)
1512 def stepLength_dialog(button):
1513 def selectorH_changed(selector, data, dialog):
1514 widget.height = selector.get_active(0)
1515 widget.client.set_int(HEIGHT, widget.height)
1516 widget.controller.set_height(widget.height)
1517 stepLengthButton_value_update()
1519 def manualButton_clicked(button, dialog):
1521 dlg.set_title("Custom step length")
1522 dlg.add_button("OK", gtk.RESPONSE_OK)
1524 label = gtk.Label("Length")
1526 entry = hildon.Entry(gtk.HILDON_SIZE_AUTO_WIDTH)
1527 if widget.height == 5:
1528 entry.set_text(str(widget.step_length))
1530 labelSuffix = gtk.Label()
1531 if widget.unit == 0:
1532 labelSuffix.set_label("m")
1534 labelSuffix.set_label("ft")
1538 hbox.add(labelSuffix)
1543 response = dlg.run()
1544 if response != gtk.RESPONSE_OK:
1547 value = float(entry.get_text())
1550 self.controller.set_height(5, value)
1551 self.client.set_int(HEIGHT, 5)
1552 self.client.set_float(STEP_LENGTH, value)
1554 widget.step_length = value
1555 stepLengthButton_value_update()
1558 hildon.hildon_banner_show_information(self, "None", "Invalid length")
1562 def heightButton_clicked(button, dialog):
1565 dialog = gtk.Dialog()
1566 dialog.set_title("Step length")
1568 manualButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1569 manualButton.set_title("Enter custom value")
1570 manualButton.set_alignment(0, 0.8, 1, 1)
1571 manualButton.connect("clicked", manualButton_clicked, dialog)
1573 selectorH = hildon.TouchSelector(text=True)
1574 selectorH.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1575 selectorH.append_text("< 1.50 m")
1576 selectorH.append_text("1.50 - 1.65 m")
1577 selectorH.append_text("1.66 - 1.80 m")
1578 selectorH.append_text("1.81 - 1.95 m")
1579 selectorH.append_text(" > 1.95 m")
1581 selectorH_English = hildon.TouchSelector(text=True)
1582 selectorH_English.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1583 selectorH_English.append_text("< 5 ft")
1584 selectorH_English.append_text("5 - 5.5 ft")
1585 selectorH_English.append_text("5.5 - 6 ft")
1586 selectorH_English.append_text("6 - 6.5 ft")
1587 selectorH_English.append_text("> 6.5 ft")
1589 heightPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1590 heightPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1591 heightPicker.set_title("Use predefined values for height")
1593 if widget.height < 5:
1594 heightPicker.set_active(widget.height)
1596 if widget.unit == 0:
1597 heightPicker.set_selector(selectorH)
1599 heightPicker.set_selector(selectorH_English)
1601 if widget.height < 5:
1602 heightPicker.set_active(widget.height)
1603 heightPicker.get_selector().connect("changed", selectorH_changed, dialog)
1604 heightPicker.connect("value-changed", heightButton_clicked, dialog)
1606 dialog.vbox.add(heightPicker)
1607 dialog.vbox.add(manualButton)
1610 if dialog.run() == gtk.RESPONSE_DELETE_EVENT:
1613 dialog = gtk.Dialog()
1614 dialog.set_title("Settings")
1615 dialog.add_button("OK", gtk.RESPONSE_OK)
1616 self.dialog = dialog
1618 stepLengthButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1619 stepLengthButton.set_title("Step length")
1620 stepLengthButton.set_alignment(0, 0.8, 1, 1)
1621 stepLengthButton.connect("clicked", stepLength_dialog)
1622 stepLengthButton_value_update()
1624 resetButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1625 resetButton.set_title("Reset total counter")
1626 resetButton.set_alignment(0, 0.8, 1, 1)
1627 resetButton.connect("clicked", reset_total_counter)
1629 alarmButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1630 alarmButton.set_title("Alarm")
1631 if self.alarm_controller.get_enable():
1632 alarmButton.set_value("Enabled")
1634 alarmButton.set_value("Disabled")
1635 alarmButton.set_alignment(0, 0.8, 1, 1)
1636 alarmButton.connect("clicked", alarmButton_pressed)
1638 selector = hildon.TouchSelector(text=True)
1639 selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1640 selector.append_text("Walk")
1641 selector.append_text("Run")
1642 selector.connect("changed", selector_changed)
1644 modePicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1645 modePicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1646 modePicker.set_title("Mode")
1647 modePicker.set_selector(selector)
1648 modePicker.set_active(widget.mode)
1651 weightButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1652 weightButton.set_title("Weight")
1653 weightButton.set_alignment(0, 0.8, 1, 1)
1654 weightButton.set_value(str(self.controller.get_weight()) + " " + self.controller.get_str_weight_unit() )
1655 weightButton.connect("clicked", weight_dialog)
1657 selectorUnit = hildon.TouchSelector(text=True)
1658 selectorUnit.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1659 selectorUnit.append_text("Metric (km)")
1660 selectorUnit.append_text("English (mi)")
1661 selectorUnit.connect("changed", selectorUnit_changed)
1663 unitPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1664 unitPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1665 unitPicker.set_title("Unit")
1666 unitPicker.set_selector(selectorUnit)
1667 unitPicker.set_active(widget.unit)
1669 selectorUI = hildon.TouchSelector(text=True)
1670 selectorUI = hildon.TouchSelector(text=True)
1671 selectorUI.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1672 selectorUI.append_text("Show current + total + graph")
1673 selectorUI.append_text("Show current + total")
1674 selectorUI.append_text("Show only current")
1675 selectorUI.append_text("Show only total")
1676 selectorUI.connect("changed", selectorUI_changed)
1678 UIPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1679 UIPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1680 UIPicker.set_title("Widget aspect")
1681 UIPicker.set_selector(selectorUI)
1682 UIPicker.set_active(widget.aspect)
1684 sensitivityButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1685 sensitivityButton.set_title("Sensitivity")
1686 sensitivityButton.set_alignment(0, 0.8, 1, 1)
1687 sensitivityButton.set_value(str(self.controller.get_sensitivity()) + " %")
1688 sensitivityButton.connect("clicked", sensitivity_dialog)
1691 logButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1692 logButton.set_label("Log data")
1693 logButton.set_active(widget.logging)
1694 logButton.connect("toggled", logButton_changed)
1696 idleButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1697 idleButton.set_label("Pause time when not walking")
1698 idleButton.set_active(widget.no_idle_time)
1699 idleButton.connect("toggled", idleButton_changed)
1701 pan_area = hildon.PannableArea()
1703 vbox.add(alarmButton)
1704 vbox.add(modePicker)
1705 vbox.add(stepLengthButton)
1706 vbox.add(weightButton)
1707 vbox.add(unitPicker)
1708 vbox.add(sensitivityButton)
1710 vbox.add(idleButton)
1711 vbox.add(resetButton)
1712 #vbox.add(logButton)
1714 pan_area.add_with_viewport(vbox)
1715 pan_area.set_size_request(-1, 300)
1717 dialog.vbox.add(pan_area)
1720 response = dialog.run()
1723 def close_requested(self, widget):
1724 if self.controller.is_running:
1725 self.controller.stop_pedometer()
1726 self.controller.stop_midnight_callback()
1728 def update_values(self):
1729 #TODO: do not update if the widget is not on the active desktop
1730 self.label_second_view.set_label(self.second_view_labels[self.second_view])
1731 self.update_current()
1734 def button_clicked(self, button):
1735 if self.controller.is_running:
1736 self.controller.stop_pedometer()
1737 self.button.set_icon(ICONSPATH + "play.png")
1739 self.controller.start_pedometer()
1740 self.button.set_icon(ICONSPATH + "stop.png")
1741 hildon.hildon_banner_show_information(self, "None", "Keep the N900 in a pocket close to your hip for best results")
1743 def do_expose_event(self, event):
1744 cr = self.window.cairo_create()
1745 cr.region(event.window.get_clip_region())
1747 #cr.set_source_rgba(0.4, 0.64, 0.564, 0.5)
1748 style = self.rc_get_style()
1749 color = style.lookup_color("DefaultBackgroundColor")
1750 cr.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
1753 width = self.allocation.width
1754 height = self.allocation.height
1756 x = self.allocation.x
1757 y = self.allocation.y
1759 cr.move_to(x + radius, y)
1760 cr.line_to(x + width - radius, y)
1761 cr.curve_to(x + width - radius, y, x + width, y, x + width, y + radius)
1762 cr.line_to(x + width, y + height - radius)
1763 cr.curve_to(x + width, y + height - radius, x + width, y + height, x + width - radius, y + height)
1764 cr.line_to(x + radius, y + height)
1765 cr.curve_to(x + radius, y + height, x, y + height, x, y + height - radius)
1766 cr.line_to(x, y + radius)
1767 cr.curve_to(x, y + radius, x, y, x + radius, y)
1769 cr.set_operator(cairo.OPERATOR_SOURCE)
1772 color = style.lookup_color("ActiveTextColor")
1773 cr.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.5);
1774 cr.set_line_width(1)
1777 hildondesktop.HomePluginItem.do_expose_event(self, event)
1779 def do_realize(self):
1780 screen = self.get_screen()
1781 self.set_colormap(screen.get_rgba_colormap())
1782 self.set_app_paintable(True)
1783 hildondesktop.HomePluginItem.do_realize(self)
1785 hd_plugin_type = PedometerHomePlugin
1790 logger = logging.getLogger("pedometer")
1791 logger.setLevel(logging.INFO)
1793 ch = logging.StreamHandler()
1794 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
1795 ch.setFormatter(formatter)
1796 logger.addHandler(ch)
1798 # The code below is just for testing purposes.
1799 # It allows to run the widget as a standalone process.
1800 if __name__ == "__main__":
1802 gobject.type_register(hd_plugin_type)
1803 obj = gobject.new(hd_plugin_type, plugin_id="plugin_id")