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):
61 def __new__(cls, *args, **kwargs):
64 cls._instance = super(Singleton, cls).__new__(
68 class PedoIntervalCounter(Singleton):
78 #TODO: check if last detected step is at the end of the interval
80 def set_vals(self, coords, tval):
86 def set_mode(self, mode):
87 #runnig, higher threshold to prevent fake steps
90 self.MIN_THRESHOLD = 650.0 * (200 - self.sensitivity) / 100
91 self.MIN_TIME_STEPS = 0.35
94 self.MIN_THRESHOLD = 500.0 * (200 - self.sensitivity) / 100
95 self.MIN_TIME_STEPS = 0.5
97 def set_sensitivity(self, value):
98 self.sensitivity = value
99 self.set_mode(self.mode)
101 def calc_mean(self, vals):
106 return sum / len(vals)
109 def calc_stdev(self, vals):
111 mean = self.calc_mean(vals)
113 rez += pow(abs(mean - i), 2)
114 return math.sqrt(rez / len(vals))
116 def calc_threshold(self, vals):
119 mean = self.calc_mean(vals)
120 threshold = max (abs(mean - vmax), abs(mean - vmin))
123 def count_steps(self, vals, t):
124 threshold = self.MIN_THRESHOLD
125 mean = self.calc_mean(vals)
129 if abs(vals[i] - mean) > threshold:
131 ntime = t[i] + self.MIN_TIME_STEPS
132 while i < len(vals) and t[i] < ntime:
137 def get_best_values(self, x, y, z):
138 dev1 = self.calc_stdev(x)
139 dev2 = self.calc_stdev(y)
140 dev3 = self.calc_stdev(z)
141 dev_max = max(dev1, dev2, dev3)
143 if (abs(dev1 - dev_max) < 0.001):
144 logger.info("X chosen as best axis, stdev %f" % dev1)
146 elif (abs(dev2 - dev_max) < 0.001):
147 logger.info("Y chosen as best axis, stdev %f" % dev2)
150 logger.info("Z chosen as best axis, stdev %f" % dev3)
153 def number_steps(self):
154 vals = self.get_best_values(self.x, self.y, self.z)
155 return self.count_steps(vals, self.t)
158 def __init__(self, time=0, steps=0, dist=0, calories=0):
161 self.calories = calories
164 def __add__(self, other):
165 return PedoValues(self.time + other.time,
166 self.steps + other.steps,
167 self.dist + other.dist,
168 self.calories + other.calories)
170 def __sub__(self, other):
171 return PedoValues(self.time - other.time,
172 self.steps - other.steps,
173 self.dist - other.dist,
174 self.calories - other.calories)
176 def get_print_time(self):
178 hours = int(tdelta / 3600)
179 tdelta -= 3600 * hours
180 mins = int(tdelta / 60)
183 strtime = "%.2d:%.2d:%.2d" % (hours, mins, secs)
186 def get_print_distance(self):
190 return "%.2f km" % (self.dist / 1000)
192 return "%.2f mi" % (self.dist / 1609.344)
195 return "%d m" % self.dist
197 return "%d ft" % int(self.dist * 3.2808)
199 def get_avg_speed(self):
209 speed = 1.0 * self.dist / self.time
212 def get_print_avg_speed(self):
224 return "N/A " + suffix
225 speed = 1.0 * self.dist / self.time
226 #convert from meters per second to km/h or mi/h
228 return "%.2f %s" % (speed, suffix)
230 def get_print_steps(self):
231 return str(self.steps)
233 def get_print_calories(self):
234 return "%.2f" % self.calories
236 class PedoRepository(Singleton):
240 raise NotImplementedError("Must be implemented by subclass")
243 raise NotImplementedError("Must be implemented by subclass")
245 def reset_values(self):
249 def get_history_count(self):
250 """return the number of days in the log"""
253 def get_values(self):
256 def add_values(self, values, when=None):
259 """add PedoValues values to repository """
261 self.values[when] = self.values[when] + values
263 self.values[when] = values
265 def get_last_7_days(self):
270 ret.append(self.values[day])
272 ret.append(PedoValues())
273 day = day - timedelta(days=1)
276 def get_last_weeks(self):
277 delta = timedelta(days=1)
279 week = int(date.today().strftime("%W"))
284 val += self.values[day]
287 w = int(day.strftime("%W"))
297 def get_alltime_values(self):
299 for k, v in self.values.iteritems():
303 def get_today_values(self):
305 return self.values[date.today()]
309 def get_this_week_values(self):
314 ret += self.values[day]
317 if day.weekday() == 0:
319 day = day - timedelta(days=1)
323 class PedoRepositoryXML(PedoRepository):
324 DIR = os.path.join(os.path.expanduser("~"), ".pedometer")
325 FILE = os.path.join(DIR, "data.xml")
326 FILE2 = os.path.join(DIR, "pickle.log")
328 if not os.path.exists(self.DIR):
329 os.makedirs(self.DIR)
330 PedoRepository.__init__(self)
334 f = open(self.FILE, "r")
335 dom = parseString(f.read())
336 values = dom.getElementsByTagName("pedometer")[0]
337 for v in values.getElementsByTagName("date"):
338 d = int(v.getAttribute("ordinal_day"))
339 steps = int(v.getAttribute("steps"))
340 calories = float(v.getAttribute("calories"))
341 dist = float(v.getAttribute("dist"))
342 time = float(v.getAttribute("time"))
343 day = date.fromordinal(d)
344 self.values[day] = PedoValues(time, steps, dist, calories)
348 logger.error("Error while loading data from xml file: %s" % e)
352 f = open(self.FILE, "w")
354 impl = getDOMImplementation()
356 newdoc = impl.createDocument(None, "pedometer", None)
357 top_element = newdoc.documentElement
358 for k, v in self.values.iteritems():
359 d = newdoc.createElement('date')
360 d.setAttribute("day", str(k.isoformat()))
361 d.setAttribute("ordinal_day", str(k.toordinal()))
362 d.setAttribute("steps", str(v.steps))
363 d.setAttribute("time", str(v.time))
364 d.setAttribute("dist", str(v.dist))
365 d.setAttribute("calories", str(v.calories))
366 top_element.appendChild(d)
368 newdoc.appendChild(top_element)
370 #f.write(newdoc.toprettyxml())
373 logger.error("Error while saving data to xml file: %s" % e)
375 class PedoRepositoryPickle(PedoRepository):
376 DIR = os.path.join(os.path.expanduser("~"), ".pedometer")
377 FILE = os.path.join(DIR, "pickle.log")
380 if not os.path.exists(self.DIR):
381 os.makedirs(self.DIR)
382 PedoRepository.__init__(self)
386 f = open(self.FILE, "rb")
387 self.values = pickle.load(f)
390 logger.error("Error while loading pickle file: %s" % e)
394 f = open(self.FILE, "wb")
395 pickle.dump(self.values, f)
398 logger.error("Error while saving data to pickle: %s" % e)
400 class PedoController(Singleton):
406 #what to display in second view - 0 - alltime, 1 - today, 2 - week
408 callback_update_ui = None
413 #The interval(number of steps) between two file updates
414 BUFFER_STEPS_INTERVAL = 100
415 #values for the two views in the widget ( current and day/week/alltime)
416 #third value to count the steps that were not yet written to file
417 v = [PedoValues(), PedoValues(), PedoValues()]
425 midnight_source_id = None
429 self.pedometer = PedoCounter(self.steps_detected)
430 self.pedometerInterval = PedoIntervalCounter()
431 self.pedometerInterval.set_mode(self.mode)
432 self.repository = PedoRepositoryXML()
433 self.repository.load()
437 if not self.midnight_set:
438 self.update_at_midnight()
439 self.midnight_set = True
441 self.config = Config()
442 self.config.add_observer(self.load_config)
444 def update_at_midnight(self):
445 next_day = date.today() + timedelta(days=1)
446 diff = time.mktime(next_day.timetuple()) - time.time()
448 self.midnight_source_id = gobject.timeout_add_seconds(diff, self.midnight_callback, True)
450 def stop_midnight_callback(self):
451 if self.midnight_source_id is not None:
452 gobject.source_remove(self.midnight_source_id)
454 def midnight_callback(self, first=False):
458 self.midnight_source_id = gobject.timeout_add_seconds(24*3600, self.midnight_callback)
463 def load_config(self):
464 self.set_height(self.config.get_height(), self.config.get_step_length())
465 self.set_mode(self.config.get_mode())
466 self.set_unit(self.config.get_unit())
467 self.set_weight(self.config.get_weight())
468 self.set_second_view(self.config.get_secondview())
469 self.set_no_idle_time(self.config.get_noidletime())
470 self.set_sensitivity(self.config.get_sensitivity())
472 def load_values(self):
473 if self.second_view == 0:
474 self.v[1] = self.repository.get_alltime_values()
475 elif self.second_view == 1:
476 self.v[1] = self.repository.get_today_values()
478 self.v[1] = self.repository.get_this_week_values()
480 def save_values(self):
481 logger.info("Saving values to file")
482 self.repository.add_values(self.v[2])
483 self.repository.save()
486 def start_pedometer(self):
487 self.v[0] = PedoValues()
488 self.v[2] = PedoValues()
489 self.last_time = time.time()
490 self.is_running = True
491 self.pedometer.start()
494 def reset_all_values(self):
495 self.repository.reset_values()
496 self.v[0] = PedoValues()
497 self.v[1] = PedoValues()
498 self.v[2] = PedoValues()
501 def stop_pedometer(self):
502 self.is_running = False
503 self.pedometer.request_stop()
508 def get_second(self):
510 return self.v[2] + self.v[1]
514 def update_current(self):
516 Update distance and calories for current values based on new height, mode values
518 self.v[0].dist = self.get_distance(self.v[0].steps)
519 self.v[0].calories = self.get_calories(self.v[0].steps)
521 def steps_detected(self, cnt, last_steps=False):
522 if not last_steps and cnt == 0 and self.no_idle_time:
523 logger.info("No steps detected, timer is paused")
525 self.v[0].steps += cnt
526 self.v[0].dist += self.get_distance(cnt)
527 self.v[0].calories += self.get_calories(self.get_distance(cnt))
528 self.v[0].time += time.time() - self.last_time
530 self.v[2].steps += cnt
531 self.v[2].dist += self.get_distance(cnt)
532 self.v[2].calories += self.get_calories(self.get_distance(cnt))
533 self.v[2].time += time.time() - self.last_time
535 if not last_steps and self.v[2].steps > self.BUFFER_STEPS_INTERVAL:
538 self.v[2] = PedoValues()
545 self.last_time = time.time()
547 def get_calories(self, distance):
548 """calculate lost calories for the distance and weight given as parameters
550 #different coefficient for running and walking
556 #convert distance from meters to miles
557 distance *= 0.000621371192
560 #convert weight from kg to pounds
563 return weight * distance * coef
565 def set_mode(self, mode):
567 self.set_height(self.height_interval)
568 self.pedometerInterval.set_mode(self.mode)
571 def set_unit(self, new_unit):
577 def get_str_weight_unit(self, unit=None):
585 def set_weight(self, value):
589 def get_weight(self):
592 def set_sensitivity(self, value):
593 self.sensitivity = value
594 self.pedometerInterval.set_sensitivity(value)
596 def get_sensitivity(self):
597 return self.sensitivity
599 def set_second_view(self, second_view):
600 self.second_view = second_view
604 def set_callback_ui(self, func):
605 self.callback_update_ui = func
607 def set_height(self, height_interval, step_length=None):
608 self.height_interval = height_interval
610 if step_length is None:
611 step_length = self.STEP_LENGTH
612 #set height, will affect the distance
613 if height_interval == 0:
614 self.STEP_LENGTH = 0.59
615 elif height_interval == 1:
616 self.STEP_LENGTH = 0.64
617 elif height_interval == 2:
618 self.STEP_LENGTH = 0.71
619 elif height_interval == 3:
620 self.STEP_LENGTH = 0.77
621 elif height_interval == 4:
622 self.STEP_LENGTH = 0.83
623 elif height_interval == 5:
624 self.STEP_LENGTH = step_length
625 #increase step length if RUNNING
627 self.STEP_LENGTH *= 1.45
630 def set_no_idle_time(self, value):
631 self.no_idle_time = value
633 def get_distance(self, steps=None):
636 return self.STEP_LENGTH * steps;
638 def add_observer(self, func):
640 self.observers.index(func)
642 self.observers.append(func)
644 def remove_observer(self, func):
645 self.observers.remove(func)
647 def notify(self, optional=False):
648 if self.callback_update_ui is not None:
649 self.callback_update_ui()
651 for func in self.observers:
654 class AlarmController(Singleton):
656 fname = "/home/user/MyDocs/.sounds/Ringtones/Bicycle.aac"
662 pedo_controller = None
665 self.client = gconf.client_get_default()
666 self.config = Config()
667 self.config.add_observer(self.load_config)
669 self.pedo_controller = PedoController()
672 self.pedo_controller.add_observer(self.update)
673 self.start_value = self.pedo_controller.get_first()
675 def init_player(self):
676 self.player = gst.element_factory_make("playbin2", "player")
677 fakesink = gst.element_factory_make("fakesink", "fakesink")
678 self.player.set_property("video-sink", fakesink)
680 bus = self.player.get_bus()
681 bus.add_signal_watch()
682 bus.connect("message", self.on_message)
684 def on_message(self, bus, message):
686 if t == gst.MESSAGE_EOS:
687 self.player.set_state(gst.STATE_NULL)
688 self.is_playing = False
689 elif t == gst.MESSAGE_ERROR:
690 self.player.set_state(gst.STATE_NULL)
691 self.is_playing = False
692 err, debug = message.parse_error()
693 logger.error("ERROR: %s, %s" % (err, debug) )
695 def update(self, optional):
696 diff = self.pedo_controller.get_first() - self.start_value
697 if self.type == 0 and diff.time >= self.interval * 60 or \
698 self.type == 1 and diff.steps >= self.interval or \
699 self.type == 2 and diff.dist >= self.interval or \
700 self.type == 3 and diff.calories >= self.interval:
702 #get new instance of current values
703 self.start_value = PedoValues() + self.pedo_controller.get_first()
704 logger.info("Alarm!")
707 if self.player is None:
710 self.player.set_state(gst.STATE_NULL)
711 self.is_playing = False
713 self.player.set_property("uri", "file://" + self.fname)
714 self.player.set_state(gst.STATE_PLAYING)
715 self.is_playing = True
718 if self.player is not None:
719 self.player.set_state(gst.STATE_NULL)
721 def load_config(self):
722 self.enable = self.config.get_alarm_enable()
723 self.set_alarm_file(self.config.get_alarm_fname())
724 self.set_interval(self.config.get_alarm_interval())
725 self.set_type(self.config.get_alarm_type())
727 def set_enable(self, value):
731 self.pedo_controller.add_observer(self.update)
732 self.start_value = self.pedo_controller.get_first()
736 self.pedo_controller.remove_observer(self.update)
738 def get_enable(self):
741 def set_alarm_file(self, fname):
744 def get_alarm_file(self):
745 if self.fname == None:
749 def set_interval(self, interval):
750 self.interval = interval
752 def get_interval(self):
755 def set_type(self, type):
761 class PedoCounter(Singleton):
762 COORD_FNAME = "/sys/class/i2c-adapter/i2c-3/3-001d/coord"
763 COORD_FNAME_SDK = "/home/andrei/pedometer-widget-0.1/date.txt"
764 LOGFILE = "/home/user/log_pedometer"
765 #time in ms between two accelerometer data reads
766 COORD_GET_INTERVAL = 25
770 interval_counter = None
771 stop_requested = False
772 update_function = None
776 def __init__(self, update_function=None):
777 if not os.path.exists(self.COORD_FNAME):
778 self.COORD_FNAME = self.COORD_FNAME_SDK
780 self.interval_counter = PedoIntervalCounter()
781 self.update_function = update_function
783 def set_logging(self, value):
786 def get_rotation(self):
787 f = open(self.COORD_FNAME, 'r')
788 coords = [int(w) for w in f.readline().split()]
793 logger.info("Counter started")
794 self.isRunning = True
795 self.stop_requested = False
797 fname = "%d_%d_%d_%d_%d_%d" % time.localtime()[0:6]
798 self.file = open(self.LOGFILE + fname + ".txt", "w")
799 gobject.idle_add(self.run)
802 self.coords = [[], [], []]
803 self.stime = time.time()
805 gobject.timeout_add(self.COORD_GET_INTERVAL, self.read_coords)
808 def read_coords(self):
809 x, y, z = self.get_rotation()
810 self.coords[0].append(int(x))
811 self.coords[1].append(int(y))
812 self.coords[2].append(int(z))
813 now = time.time() - self.stime
815 self.file.write("%d %d %d %f\n" % (self.coords[0][-1], self.coords[1][-1], self.coords[2][-1], now))
820 if self.t[-1] > self.COUNT_INTERVAL or self.stop_requested:
822 gobject.idle_add(self.stop_interval)
825 def stop_interval(self):
826 self.interval_counter.set_vals(self.coords, self.t)
827 cnt = self.interval_counter.number_steps()
829 logger.info("Number of steps detected for last interval %d, number of coords: %d" % (cnt, len(self.t)))
831 gobject.idle_add(self.update_function, cnt, self.stop_requested)
833 if self.stop_requested:
834 gobject.idle_add(self.stop)
836 gobject.idle_add(self.run)
842 logger.info("Counter has finished")
844 def request_stop(self):
845 self.stop_requested = True
846 self.isRunning = False
848 class CustomButton(hildon.Button):
849 def __init__(self, icon):
850 hildon.Button.__init__(self, gtk.HILDON_SIZE_AUTO_WIDTH, hildon.BUTTON_ARRANGEMENT_VERTICAL)
852 self.set_size_request(int(32 * 1.4), int(30 * 1.0))
853 self.retval = self.connect("expose_event", self.expose)
855 def set_icon(self, icon):
858 def expose(self, widget, event):
859 self.context = widget.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)
866 self.context.set_source_rgba(1, 1, 1, 0)
868 style = self.rc_get_style()
869 color = style.lookup_color("DefaultBackgroundColor")
870 if self.state == gtk.STATE_ACTIVE:
871 style = self.rc_get_style()
872 color = style.lookup_color("SelectionColor")
873 self.context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
876 #img = cairo.ImageSurface.create_from_png(self.icon)
878 #self.context.set_source_surface(img)
879 #self.context.set_source_surface(img, rect.width/2 - img.get_width() /2, 0)
881 img.set_from_file(self.icon)
882 buf = img.get_pixbuf()
883 buf = buf.scale_simple(int(32 * 1.5), int(30 * 1.5), gtk.gdk.INTERP_BILINEAR)
885 self.context.set_source_pixbuf(buf, rect.x + (event.area.width / 2 - 15) - 8, rect.y + 1)
886 self.context.scale(200, 200)
891 class CustomEventBox(gtk.EventBox):
894 gtk.EventBox.__init__(self)
896 def do_expose_event(self, event):
897 self.context = self.window.cairo_create()
898 self.context.rectangle(event.area.x, event.area.y,
899 event.area.width, event.area.height)
902 rect = self.get_allocation()
903 self.context.rectangle(rect.x, rect.y, rect.width, rect.height)
905 if self.state == gtk.STATE_ACTIVE:
906 style = self.rc_get_style()
907 color = style.lookup_color("SelectionColor")
908 self.context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
910 self.context.set_source_rgba(1, 1, 1, 0)
913 gtk.EventBox.do_expose_event(self, event)
915 class GraphController(Singleton):
916 ytitles = ["Steps", "Average Speed", "Distance", "Calories"]
917 xtitles = ["Day", "Week"] # "Today"]
923 self.repository = PedoRepositoryXML()
925 PedoController().add_observer(self.update_ui)
926 self.config = Config()
927 self.config.add_observer(self.load_config)
929 def load_config(self):
930 self.set_current_view(self.config.get_graphview())
932 def set_graph(self, widget):
936 def set_current_view(self, view):
938 current_view % len(ytitles) - gives the ytitle
939 current_view / len(ytitles) - gives the xtitle
941 self.x_id = view / len(self.ytitles)
942 self.y_id = view % len(self.ytitles)
946 current_view = self.config.get_graphview() + 1
947 if current_view == len(self.ytitles) * len(self.xtitles):
949 self.config.set_graphview(current_view)
951 def last_weeks_labels(self):
953 delta = timedelta(days=7)
956 ret.append(d.strftime("Week %W"))
960 def compute_values(self):
963 values = self.repository.get_last_7_days()
965 delta = timedelta(days=1)
967 labels.append(d.ctime().split()[0])
971 values = self.repository.get_last_weeks()
974 labels.append(d.strftime("Week %W"))
975 d = d - timedelta(days=7)
977 values = self.repository.get_today()
981 yvalues = [line.steps for line in values]
983 yvalues = [line.get_avg_speed() for line in values]
985 yvalues = [line.dist for line in values]
987 yvalues = [line.calories for line in values]
989 #determine values for y lines in graph
990 diff = self.get_best_interval_value(max(yvalues))
993 ytext.append(str(int(i*diff)))
995 if self.widget is not None:
998 self.widget.values = yvalues
999 self.widget.ytext = ytext
1000 self.widget.xtext = labels
1001 self.widget.max_value = diff * 5
1002 self.widget.text = self.xtitles[self.x_id] + " / " + self.ytitles[self.y_id]
1003 self.widget.queue_draw()
1005 logger.error("Widget not set in GraphController")
1007 def get_best_interval_value(self, max_value):
1008 diff = 1.0 * max_value / 5
1009 l = len(str(int(diff)))
1010 d = math.pow(10, l/2)
1011 val = int(math.ceil(1.0 * diff / d)) * d
1016 def update_ui(self, optional=False):
1017 """update graph values every x seconds"""
1018 if optional and self.last_update - time.time() < 600:
1020 if self.widget is None:
1023 self.compute_values()
1024 self.last_update = time.time()
1026 class GraphWidget(gtk.DrawingArea):
1029 gtk.DrawingArea.__init__(self)
1030 self.set_size_request(-1, 150)
1034 self.ytext = [" 0", "1000", "2000", "3000", "4000", "5000"]
1035 self.xtext = ["Monday", "Tuesday", "Wednesday","Thursday", "Friday", "Saturday", "Sunday"]
1036 self.values = [1500, 3400, 4000, 3600, 3200, 0, 4500]
1037 self.max_value = 5000
1038 self.text = "All time steps"
1040 def do_expose_event(self, event):
1041 context = self.window.cairo_create()
1043 # set a clip region for the expose event
1044 context.rectangle(event.area.x, event.area.y,
1045 event.area.width, event.area.height)
1050 context.set_operator(cairo.OPERATOR_SOURCE)
1051 style = self.rc_get_style()
1053 if self.state == gtk.STATE_ACTIVE:
1054 color = style.lookup_color("SelectionColor")
1056 color = style.lookup_color("DefaultBackgroundColor")
1057 context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75)
1069 rect = self.get_allocation()
1073 cr.select_font_face("Purisa", cairo.FONT_SLANT_NORMAL,
1074 cairo.FONT_WEIGHT_NORMAL)
1075 cr.set_font_size(13)
1077 #check space needed to display ylabels
1078 te = cr.text_extents(self.ytext[-1])
1079 border_left = te[2] + 7
1081 cr.set_source_rgb(1, 1, 1)
1082 cr.move_to(border_left, space_above)
1083 cr.line_to(border_left, y-space_below)
1084 cr.set_line_width(2)
1087 cr.move_to(border_left, y-space_below)
1088 cr.line_to(x-border_right, y-space_below)
1089 cr.set_line_width(2)
1092 ydiff = (y-space_above-space_below) / self.yvalues
1093 for i in range(self.yvalues):
1094 yy = y-space_below-ydiff*(i+1)
1095 cr.move_to(border_left, yy)
1096 cr.line_to(x-border_right, yy)
1097 cr.set_line_width(0.8)
1102 yy = y - space_below - ydiff*i + 5
1103 te = cr.text_extents(self.ytext[i])
1105 cr.move_to(border_left-te[2]-2, yy)
1106 cr.show_text(self.ytext[i])
1108 cr.set_font_size(15)
1109 te = cr.text_extents(self.text)
1110 cr.move_to((x-te[2])/2, y-5)
1111 cr.show_text(self.text)
1113 graph_x_space = x - border_left - border_right
1114 graph_y_space = y - space_below - space_above
1115 bar_width = graph_x_space*0.75 / len(self.values)
1116 bar_distance = graph_x_space*0.25 / (1+len(self.values))
1118 #set dummy max value to avoid exceptions
1119 if self.max_value == 0:
1120 self.max_value = 100
1121 for i in range(len(self.values)):
1122 xx = border_left + (i+1)*bar_distance + i * bar_width
1124 height = graph_y_space * (1.0 * self.values[i] / self.max_value)
1125 cr.set_source_rgba(1, 1, 1, 0.75)
1126 cr.rectangle(int(xx), int(yy-height), int(bar_width), int(height))
1129 cr.set_source_rgba(1, 1, 1, 1)
1130 cr.select_font_face("Purisa", cairo.FONT_SLANT_NORMAL,
1131 cairo.FONT_WEIGHT_NORMAL)
1132 cr.set_font_size(13)
1134 cr.rotate(2*math.pi * (-45) / 180)
1135 for i in range(len(self.values)):
1136 xx = y - space_below - 10
1137 yy = border_left + (i+1)*bar_distance + i * bar_width
1138 cr.move_to(-xx, yy + bar_width*1.25 / 2)
1139 cr.show_text(self.xtext[i])
1141 class Config(Singleton):
1152 no_idle_time = False
1155 alarm_enable = False
1156 alarm_fname = "/home/user/MyDocs/.sounds/Ringtones/Bicycle.aac"
1163 if self._references > 1:
1165 self.client = gconf.client_get_default()
1166 self.client.add_dir('/apps/pedometerhomewidget', gconf.CLIENT_PRELOAD_RECURSIVE)
1167 self.notify_id = self.client.notify_add('/apps/pedometerhomewidget', self.gconf_changed)
1169 def add_observer(self, func):
1171 self.observers.index(func)
1173 self.observers.append(func)
1176 def remove_observer(self, func):
1177 self.observers.remove(func)
1179 def gconf_changed(self, client, *args, **kargs):
1184 for func in self.observers:
1187 logger.info("Update took: %f seconds" % (t2-t1))
1190 return self.client.get_int(MODE)
1192 def set_mode(self, value):
1193 self.client.set_int(MODE, value)
1195 def get_height(self):
1196 return self.client.get_int(HEIGHT)
1198 def set_height(self, value):
1199 self.client.set_int(HEIGHT, value)
1201 def get_step_length(self):
1202 return self.client.get_float(STEP_LENGTH)
1204 def set_step_length(self, value):
1205 self.client.set_float(STEP_LENGTH, value)
1207 def get_weight(self):
1208 return self.client.get_int(WEIGHT)
1210 def set_weight(self, value):
1211 self.client.set_int(WEIGHT, value)
1213 def get_sensitivity(self):
1214 return self.client.get_int(SENSITIVITY)
1216 def set_sensitivity(self, value):
1217 self.client.set_int(SENSITIVITY, value)
1220 return self.client.get_int(UNIT)
1222 def set_unit(self, value):
1223 self.client.set_int(UNIT, value)
1225 def get_aspect(self):
1226 return self.client.get_int(ASPECT)
1228 def set_aspect(self, value):
1229 self.client.set_int(ASPECT, value)
1231 def get_secondview(self):
1232 value = self.client.get_int(SECONDVIEW)
1233 if value < 0 or value > 2:
1235 logger.error("Invalid secondview value read from Gconf. Using default value")
1239 def set_secondview(self, value):
1240 self.client.set_int(SECONDVIEW, value)
1242 def get_graphview(self):
1243 return self.client.get_int(GRAPHVIEW)
1245 def set_graphview(self, value):
1246 self.client.set_int(GRAPHVIEW, value)
1248 def get_noidletime(self):
1249 return self.client.get_bool(NOIDLETIME)
1251 def set_noidletime(self, value):
1252 self.client.set_bool(NOIDLETIME, value)
1254 def get_logging(self):
1255 return self.client.get_bool(LOGGING)
1257 def set_logging(self, value):
1258 self.client.set_bool(LOGGING, value)
1260 def get_alarm_enable(self):
1261 return self.client.get_bool(ALARM_ENABLE)
1263 def set_alarm_enable(self, value):
1264 self.client.set_bool(ALARM_ENABLE, value)
1266 def get_alarm_fname(self):
1267 return self.client.get_string(ALARM_FNAME)
1269 def set_alarm_fname(self, value):
1270 self.client.set_string(ALARM_FNAME, value)
1272 def get_alarm_interval(self):
1273 return self.client.get_int(ALARM_INTERVAL)
1275 def set_alarrm_interval(self, value):
1276 self.client.set_int(ALARM_INTERVAL, value)
1278 def get_alarm_type(self):
1279 return self.client.get_int(ALARM_TYPE)
1281 def set_alarm_type(self, value):
1282 self.client.set_int(ALARM_TYPE, value)
1284 class PedometerHomePlugin(hildondesktop.HomePluginItem):
1288 labels = ["timer", "count", "dist", "avgSpeed", "calories"]
1293 #second view ( day / week/ alltime)
1296 second_view_labels = ["All-time", "Today", "This week"]
1299 graph_controller = None
1304 hildondesktop.HomePluginItem.__init__(self)
1306 gobject.type_register(CustomEventBox)
1307 gobject.type_register(GraphWidget)
1309 self.config = Config()
1311 self.button = CustomButton(ICONSPATH + "play.png")
1312 self.button.connect("clicked", self.button_clicked)
1314 self.create_labels(self.labelsC)
1315 self.create_labels(self.labelsT)
1316 self.label_second_view = self.new_label_heading(self.second_view_labels[self.config.get_secondview()])
1318 self.controller = PedoController()
1319 self.controller.set_callback_ui(self.update_values)
1321 self.graph_controller = GraphController()
1322 self.alarm_controller = AlarmController()
1324 self.update_current()
1327 mainHBox = gtk.HBox(spacing=1)
1329 descVBox = gtk.VBox(spacing=1)
1330 descVBox.add(self.new_label_heading())
1331 descVBox.add(self.new_label_heading("Time:"))
1332 descVBox.add(self.new_label_heading("Steps:"))
1333 descVBox.add(self.new_label_heading("Calories:"))
1334 descVBox.add(self.new_label_heading("Distance:"))
1335 descVBox.add(self.new_label_heading("Avg Speed:"))
1337 currentVBox = gtk.VBox(spacing=1)
1338 currentVBox.add(self.new_label_heading("Current"))
1339 currentVBox.add(self.labelsC["timer"])
1340 currentVBox.add(self.labelsC["count"])
1341 currentVBox.add(self.labelsC["calories"])
1342 currentVBox.add(self.labelsC["dist"])
1343 currentVBox.add(self.labelsC["avgSpeed"])
1344 self.currentBox = currentVBox
1346 totalVBox = gtk.VBox(spacing=1)
1347 totalVBox.add(self.label_second_view)
1348 totalVBox.add(self.labelsT["timer"])
1349 totalVBox.add(self.labelsT["count"])
1350 totalVBox.add(self.labelsT["calories"])
1351 totalVBox.add(self.labelsT["dist"])
1352 totalVBox.add(self.labelsT["avgSpeed"])
1353 self.totalBox = totalVBox
1355 buttonVBox = gtk.VBox(spacing=1)
1356 buttonVBox.add(self.new_label_heading(""))
1357 buttonVBox.add(self.button)
1358 buttonVBox.add(self.new_label_heading(""))
1360 eventBox = CustomEventBox()
1361 eventBox.set_visible_window(False)
1362 eventBox.add(totalVBox)
1363 eventBox.connect("button-press-event", self.eventBox_clicked)
1364 eventBox.connect("button-release-event", self.eventBox_clicked_release)
1366 mainHBox.add(buttonVBox)
1367 mainHBox.add(descVBox)
1368 mainHBox.add(currentVBox)
1369 mainHBox.add(eventBox)
1370 self.mainhbox = mainHBox
1372 graph = GraphWidget()
1373 self.graph_controller.set_graph(graph)
1375 eventBoxGraph = CustomEventBox()
1376 eventBoxGraph.set_visible_window(False)
1377 eventBoxGraph.add(graph)
1379 eventBoxGraph.connect("button-press-event", self.eventBoxGraph_clicked)
1380 eventBoxGraph.connect("button-release-event", self.eventBoxGraph_clicked_release)
1381 self.graphBox = eventBoxGraph
1383 self.mainvbox = gtk.VBox()
1385 self.mainvbox.add(mainHBox)
1386 self.mainvbox.add(eventBoxGraph)
1388 self.mainvbox.show_all()
1389 self.add(self.mainvbox)
1390 self.update_aspect()
1392 self.connect("unrealize", self.close_requested)
1393 self.set_settings(True)
1394 self.connect("show-settings", self.show_settings)
1396 def eventBoxGraph_clicked(self, widget, data=None):
1397 widget.set_state(gtk.STATE_ACTIVE)
1399 def eventBoxGraph_clicked_release(self, widget, data=None):
1400 self.graph_controller.next_view()
1401 widget.set_state(gtk.STATE_NORMAL)
1403 def eventBox_clicked(self, widget, data=None):
1404 widget.set_state(gtk.STATE_ACTIVE)
1406 def eventBox_clicked_release(self, widget, data=None):
1407 widget.set_state(gtk.STATE_NORMAL)
1409 second_view = self.config.get_secondview()
1410 second_view = (second_view + 1) % 3
1411 self.config.set_secondview(second_view)
1413 def new_label_heading(self, title=""):
1414 l = gtk.Label(title)
1415 hildon.hildon_helper_set_logical_font(l, "SmallSystemFont")
1418 def create_labels(self, new_labels):
1419 for label in self.labels:
1421 hildon.hildon_helper_set_logical_font(l, "SmallSystemFont")
1422 hildon.hildon_helper_set_logical_color(l, gtk.RC_FG, gtk.STATE_NORMAL, "ActiveTextColor")
1423 new_labels[label] = l
1425 def update_aspect(self):
1426 aspect = self.config.get_aspect()
1428 self.graphBox.hide_all()
1430 self.graphBox.show_all()
1432 if aspect == 0 or aspect == 1:
1433 self.currentBox.show_all()
1434 self.totalBox.show_all()
1436 self.currentBox.show_all()
1437 self.totalBox.hide_all()
1439 self.currentBox.hide_all()
1440 self.totalBox.show_all()
1442 x,y = self.size_request()
1445 def update_ui_values(self, labels, values):
1446 labels["timer"].set_label(values.get_print_time())
1447 labels["count"].set_label(values.get_print_steps())
1448 labels["dist"].set_label(values.get_print_distance())
1449 labels["avgSpeed"].set_label(values.get_print_avg_speed())
1450 labels["calories"].set_label(values.get_print_calories())
1452 def update_current(self):
1453 self.update_ui_values(self.labelsC, self.controller.get_first())
1455 def update_total(self):
1456 self.update_ui_values(self.labelsT, self.controller.get_second())
1458 def show_alarm_settings(self, main_button):
1459 def choose_file(widget):
1460 file = hildon.FileChooserDialog(self, gtk.FILE_CHOOSER_ACTION_OPEN, hildon.FileSystemModel() )
1462 if ( file.run() == gtk.RESPONSE_OK):
1463 fname = file.get_filename()
1464 widget.set_value(fname)
1465 self.config.set_alarm_fname(fname)
1468 def test_sound(button):
1470 self.alarm_controller.play()
1471 except Exception, e:
1472 logger.error("Could not play alarm sound: %s" % e)
1473 hildon.hildon_banner_show_information(self, "None", "Could not play alarm sound")
1475 def enableButton_changed(button):
1476 value = button.get_active()
1477 self.config.set_alarm_enable(value)
1479 main_button.set_value("Enabled")
1481 main_button.set_value("Disabled")
1483 def selectorType_changed(selector, data, labelEntry2):
1484 type = selector.get_active(0)
1485 self.config.set_alarm_type(type)
1486 labelEntry2.set_label(suffix[type])
1488 dialog = gtk.Dialog()
1489 dialog.set_title("Alarm settings")
1490 dialog.add_button("OK", gtk.RESPONSE_OK)
1492 enableButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1493 enableButton.set_label("Enable alarm")
1494 enableButton.set_active(self.alarm_controller.get_enable())
1495 enableButton.connect("toggled", enableButton_changed)
1497 testButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1498 testButton.set_alignment(0, 0.8, 1, 1)
1499 testButton.set_title("Test sound")
1500 testButton.connect("pressed", test_sound)
1502 fileButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1503 fileButton.set_alignment(0, 0.8, 1, 1)
1504 fileButton.set_title("Alarm sound")
1505 fileButton.set_value(self.alarm_controller.get_alarm_file())
1506 fileButton.connect("pressed", choose_file)
1508 labelEntry = gtk.Label("Notify every:")
1509 suffix = ["mins", "steps", "m/ft", "calories"]
1510 labelEntry2 = gtk.Label(suffix[self.alarm_controller.get_type()])
1511 intervalEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO_WIDTH)
1512 intervalEntry.set_text(str(self.alarm_controller.get_interval()))
1514 selectorType = hildon.TouchSelector(text=True)
1515 selectorType.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1516 selectorType.append_text("Time")
1517 selectorType.append_text("Steps")
1518 selectorType.append_text("Distance")
1519 selectorType.append_text("Calories")
1520 selectorType.connect("changed", selectorType_changed, labelEntry2)
1522 typePicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1523 typePicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1524 typePicker.set_title("Alarm type")
1525 typePicker.set_selector(selectorType)
1526 typePicker.set_active(self.alarm_controller.get_type())
1529 hbox.add(labelEntry)
1530 hbox.add(intervalEntry)
1531 hbox.add(labelEntry2)
1533 dialog.vbox.add(enableButton)
1534 dialog.vbox.add(fileButton)
1535 dialog.vbox.add(testButton)
1536 dialog.vbox.add(typePicker)
1537 dialog.vbox.add(hbox)
1540 response = dialog.run()
1541 if response != gtk.RESPONSE_OK:
1544 value = int(intervalEntry.get_text())
1545 self.config.set_alarrm_interval(value)
1548 hildon.hildon_banner_show_information(self, "None", "Invalid interval")
1552 def show_settings(self, widget):
1553 def reset_total_counter(arg):
1554 note = hildon.hildon_note_new_confirmation(self.dialog, "Are you sure you want to delete all your pedometer history?")
1556 if ret == gtk.RESPONSE_OK:
1557 self.controller.reset_all_values()
1558 hildon.hildon_banner_show_information(self, "None", "All history was deleted")
1561 def alarmButton_pressed(widget):
1562 self.show_alarm_settings(widget)
1564 def selector_changed(selector, data):
1565 mode = selector.get_active(0)
1566 self.config.set_mode(mode)
1568 def selectorUnit_changed(selector, data):
1569 unit = selector.get_active(0)
1570 self.config.set_unit(unit)
1572 update_weight_button()
1573 stepLengthButton_value_update()
1575 def selectorUI_changed(selector, data):
1576 aspect = selectorUI.get_active(0)
1577 widget.update_aspect()
1579 def logButton_changed(checkButton):
1580 logging = checkButton.get_active()
1581 self.config.set_logging(logging)
1583 def idleButton_changed(idleButton):
1584 no_idle_time = idleButton.get_active()
1585 self.config.set_noidletime(no_idle_time)
1587 def update_weight_button():
1588 weightButton.set_value(str(self.config.get_weight()) + \
1589 " " + self.controller.get_str_weight_unit(self.config.get_unit()) )
1591 def weight_dialog(button):
1592 dialog = gtk.Dialog("Weight", self.dialog)
1593 dialog.add_button("OK", gtk.RESPONSE_OK)
1595 label = gtk.Label("Weight:")
1597 entry.set_text(str(self.config.get_weight()))
1599 suffixLabel = gtk.Label(self.controller.get_str_weight_unit(self.config.get_unit()))
1604 hbox.add(suffixLabel)
1606 dialog.vbox.add(hbox)
1609 response = dialog.run()
1610 if response != gtk.RESPONSE_OK:
1613 value = int(entry.get_text())
1616 self.config.set_weight(value)
1617 update_weight_button()
1620 hildon.hildon_banner_show_information(self, "None", "Invalid weight")
1623 def sensitivity_dialog(button):
1624 def seekbar_changed(seekbar):
1625 label.set_text(str(seekbar.get_position()) + " %")
1627 dialog = gtk.Dialog("Sensitivity", self.dialog)
1628 dialog.add_button("OK", gtk.RESPONSE_OK)
1629 seekbar = hildon.Seekbar()
1630 seekbar.set_size_request(400, -1)
1631 seekbar.set_total_time(200)
1632 seekbar.set_position(self.config.get_sensitivity())
1633 seekbar.connect("value-changed", seekbar_changed)
1637 label = gtk.Label(str(self.config.get_sensitivity()) + " %")
1638 label.set_size_request(30, -1)
1641 dialog.vbox.add(hbox)
1644 if dialog.run() == gtk.RESPONSE_OK:
1645 value = seekbar.get_position()
1646 self.config.set_sensitivity(value)
1647 button.set_value(str(value) + " %")
1651 def stepLengthButton_value_update():
1652 if self.config.get_height() == 5:
1653 l_unit = ["m", "ft"]
1654 stepLengthButton.set_value("Custom value: %.2f %s" % (self.config.get_step_length(), l_unit[self.config.get_unit()]))
1656 h = [ ["< 1.50 m", "1.50 - 1.65 m", "1.66 - 1.80 m", "1.81 - 1.95 m", " > 1.95 m"],
1657 ["< 5 ft", "5 - 5.5 ft", "5.5 - 6 ft", "6 - 6.5 ft", "> 6.5 ft"]]
1658 str = "Using predefined value for height: %s" % h[self.config.get_unit()][self.config.get_height()]
1659 stepLengthButton.set_value(str)
1661 def stepLength_dialog(button):
1662 def selectorH_changed(selector, data, dialog):
1663 height = selector.get_active(0)
1664 self.config.set_height(height)
1665 stepLengthButton_value_update()
1667 def manualButton_clicked(button, dialog):
1669 dlg.set_title("Custom step length")
1670 dlg.add_button("OK", gtk.RESPONSE_OK)
1672 label = gtk.Label("Length")
1674 entry = hildon.Entry(gtk.HILDON_SIZE_AUTO_WIDTH)
1675 if self.config.get_height() == 5:
1676 entry.set_text(str(self.config.get_step_length()))
1678 labelSuffix = gtk.Label()
1679 if self.config.get_unit() == 0:
1680 labelSuffix.set_label("m")
1682 labelSuffix.set_label("ft")
1686 hbox.add(labelSuffix)
1691 response = dlg.run()
1692 if response != gtk.RESPONSE_OK:
1695 value = float(entry.get_text())
1698 self.config.set_step_length(value)
1699 self.config.set_height(5)
1700 stepLengthButton_value_update()
1703 hildon.hildon_banner_show_information(self, "None", "Invalid length")
1707 def heightButton_clicked(button, dialog):
1710 dialog = gtk.Dialog()
1711 dialog.set_title("Step length")
1713 manualButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1714 manualButton.set_title("Enter custom value")
1715 manualButton.set_alignment(0, 0.8, 1, 1)
1716 manualButton.connect("clicked", manualButton_clicked, dialog)
1718 selectorH = hildon.TouchSelector(text=True)
1719 selectorH.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1720 selectorH.append_text("< 1.50 m")
1721 selectorH.append_text("1.50 - 1.65 m")
1722 selectorH.append_text("1.66 - 1.80 m")
1723 selectorH.append_text("1.81 - 1.95 m")
1724 selectorH.append_text(" > 1.95 m")
1726 selectorH_English = hildon.TouchSelector(text=True)
1727 selectorH_English.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1728 selectorH_English.append_text("< 5 ft")
1729 selectorH_English.append_text("5 - 5.5 ft")
1730 selectorH_English.append_text("5.5 - 6 ft")
1731 selectorH_English.append_text("6 - 6.5 ft")
1732 selectorH_English.append_text("> 6.5 ft")
1734 heightPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1735 heightPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1736 heightPicker.set_title("Use predefined values for height")
1739 unit = self.config.get_unit()
1741 heightPicker.set_selector(selectorH)
1743 heightPicker.set_selector(selectorH_English)
1745 height = self.config.get_height()
1747 heightPicker.set_active(height)
1749 heightPicker.get_selector().connect("changed", selectorH_changed, dialog)
1750 heightPicker.connect("value-changed", heightButton_clicked, dialog)
1752 dialog.vbox.add(heightPicker)
1753 dialog.vbox.add(manualButton)
1756 if dialog.run() == gtk.RESPONSE_DELETE_EVENT:
1759 def donateButton_clicked(button, dialog):
1760 url = "https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=BKE6E9SLK7NP4&lc=RO&item_name=Pedometer%20Widget¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted"
1761 command = "dbus-send --system --type=method_call --dest=\"com.nokia.osso_browser\" --print-reply /com/nokia/osso_browser/request com.nokia.osso_browser.load_url string:\"%s\"" % url
1764 dialog = gtk.Dialog()
1765 dialog.set_title("Settings")
1766 dialog.add_button("OK", gtk.RESPONSE_OK)
1767 self.dialog = dialog
1769 stepLengthButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1770 stepLengthButton.set_title("Step length")
1771 stepLengthButton.set_alignment(0, 0.8, 1, 1)
1772 stepLengthButton.connect("clicked", stepLength_dialog)
1773 stepLengthButton_value_update()
1775 resetButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1776 resetButton.set_title("Reset total counter")
1777 resetButton.set_alignment(0, 0.8, 1, 1)
1778 resetButton.connect("clicked", reset_total_counter)
1780 alarmButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1781 alarmButton.set_title("Alarm")
1782 if self.config.get_alarm_enable():
1783 alarmButton.set_value("Enabled")
1785 alarmButton.set_value("Disabled")
1786 alarmButton.set_alignment(0, 0.8, 1, 1)
1787 alarmButton.connect("clicked", alarmButton_pressed)
1789 selector = hildon.TouchSelector(text=True)
1790 selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1791 selector.append_text("Walk")
1792 selector.append_text("Run")
1793 selector.connect("changed", selector_changed)
1795 modePicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1796 modePicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1797 modePicker.set_title("Mode")
1798 modePicker.set_selector(selector)
1799 modePicker.set_active(self.config.get_mode())
1801 weightButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1802 weightButton.set_title("Weight")
1803 weightButton.set_alignment(0, 0.8, 1, 1)
1804 update_weight_button()
1805 weightButton.connect("clicked", weight_dialog)
1807 selectorUnit = hildon.TouchSelector(text=True)
1808 selectorUnit.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1809 selectorUnit.append_text("Metric (km)")
1810 selectorUnit.append_text("English (mi)")
1811 selectorUnit.connect("changed", selectorUnit_changed)
1813 unitPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1814 unitPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1815 unitPicker.set_title("Unit")
1816 unitPicker.set_selector(selectorUnit)
1817 unitPicker.set_active(self.config.get_unit())
1819 selectorUI = hildon.TouchSelector(text=True)
1820 selectorUI = hildon.TouchSelector(text=True)
1821 selectorUI.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1822 selectorUI.append_text("Show current + total + graph")
1823 selectorUI.append_text("Show current + total")
1824 selectorUI.append_text("Show only current")
1825 selectorUI.append_text("Show only total")
1826 selectorUI.connect("changed", selectorUI_changed)
1828 UIPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1829 UIPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1830 UIPicker.set_title("Widget aspect")
1831 UIPicker.set_selector(selectorUI)
1832 UIPicker.set_active(self.config.get_aspect())
1834 sensitivityButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1835 sensitivityButton.set_title("Sensitivity")
1836 sensitivityButton.set_alignment(0, 0.8, 1, 1)
1837 sensitivityButton.set_value(str(self.config.get_sensitivity()) + " %")
1838 sensitivityButton.connect("clicked", sensitivity_dialog)
1840 donateButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1841 donateButton.set_title("Donate")
1842 donateButton.set_alignment(0, 0.8, 1, 1)
1843 donateButton.connect("clicked", donateButton_clicked, dialog)
1845 logButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1846 logButton.set_label("Log data")
1847 logButton.set_active(self.config.get_logging())
1848 logButton.connect("toggled", logButton_changed)
1850 idleButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1851 idleButton.set_label("Pause time when not walking")
1852 idleButton.set_active(self.config.get_noidletime())
1853 idleButton.connect("toggled", idleButton_changed)
1855 pan_area = hildon.PannableArea()
1857 vbox.add(alarmButton)
1858 vbox.add(modePicker)
1859 vbox.add(stepLengthButton)
1860 vbox.add(weightButton)
1861 vbox.add(unitPicker)
1862 vbox.add(sensitivityButton)
1864 vbox.add(idleButton)
1865 vbox.add(resetButton)
1866 vbox.add(donateButton)
1867 #vbox.add(logButton)
1869 pan_area.add_with_viewport(vbox)
1870 pan_area.set_size_request(-1, 300)
1872 dialog.vbox.add(pan_area)
1875 response = dialog.run()
1878 def close_requested(self, widget):
1879 if self.controller.is_running:
1880 self.controller.stop_pedometer()
1881 self.controller.stop_midnight_callback()
1883 def update_values(self):
1884 #TODO: do not update if the widget is not on the active desktop
1885 self.label_second_view.set_label(self.second_view_labels[self.config.get_secondview()])
1886 self.update_current()
1889 def button_clicked(self, button):
1890 if self.controller.is_running:
1891 self.controller.stop_pedometer()
1892 self.button.set_icon(ICONSPATH + "play.png")
1894 self.controller.start_pedometer()
1895 self.button.set_icon(ICONSPATH + "stop.png")
1896 hildon.hildon_banner_show_information(self, "None", "Keep the N900 in a pocket close to your hip for best results")
1898 def do_expose_event(self, event):
1899 cr = self.window.cairo_create()
1900 cr.region(event.window.get_clip_region())
1902 #cr.set_source_rgba(0.4, 0.64, 0.564, 0.5)
1903 style = self.rc_get_style()
1904 color = style.lookup_color("DefaultBackgroundColor")
1905 cr.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
1908 width = self.allocation.width
1909 height = self.allocation.height
1911 x = self.allocation.x
1912 y = self.allocation.y
1914 cr.move_to(x + radius, y)
1915 cr.line_to(x + width - radius, y)
1916 cr.curve_to(x + width - radius, y, x + width, y, x + width, y + radius)
1917 cr.line_to(x + width, y + height - radius)
1918 cr.curve_to(x + width, y + height - radius, x + width, y + height, x + width - radius, y + height)
1919 cr.line_to(x + radius, y + height)
1920 cr.curve_to(x + radius, y + height, x, y + height, x, y + height - radius)
1921 cr.line_to(x, y + radius)
1922 cr.curve_to(x, y + radius, x, y, x + radius, y)
1924 cr.set_operator(cairo.OPERATOR_SOURCE)
1927 color = style.lookup_color("ActiveTextColor")
1928 cr.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.5);
1929 cr.set_line_width(1)
1932 hildondesktop.HomePluginItem.do_expose_event(self, event)
1934 def do_realize(self):
1935 screen = self.get_screen()
1936 self.set_colormap(screen.get_rgba_colormap())
1937 self.set_app_paintable(True)
1938 hildondesktop.HomePluginItem.do_realize(self)
1940 hd_plugin_type = PedometerHomePlugin
1945 logger = logging.getLogger("pedometer")
1946 logger.setLevel(logging.INFO)
1948 ch = logging.StreamHandler()
1949 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
1950 ch.setFormatter(formatter)
1951 logger.addHandler(ch)
1953 # The code below is just for testing purposes.
1954 # It allows to run the widget as a standalone process.
1955 if __name__ == "__main__":
1957 gobject.type_register(hd_plugin_type)
1958 obj = gobject.new(hd_plugin_type, plugin_id="plugin_id")