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
412 #values for the two views in the widget ( current and day/week/alltime)
413 v = [PedoValues(), PedoValues()]
421 midnight_source_id = None
425 self.pedometer = PedoCounter(self.steps_detected)
426 self.pedometerInterval = PedoIntervalCounter()
427 self.pedometerInterval.set_mode(self.mode)
428 self.repository = PedoRepositoryXML()
429 self.repository.load()
433 if not self.midnight_set:
434 self.update_at_midnight()
435 self.midnight_set = True
437 self.config = Config()
438 self.config.add_observer(self.load_config)
440 def update_at_midnight(self):
441 next_day = date.today() + timedelta(days=1)
442 diff = time.mktime(next_day.timetuple()) - time.time()
444 self.midnight_source_id = gobject.timeout_add_seconds(diff, self.midnight_callback, True)
446 def stop_midnight_callback(self):
447 if self.midnight_source_id is not None:
448 gobject.source_remove(self.midnight_source_id)
450 def midnight_callback(self, first=False):
454 self.midnight_source_id = gobject.timeout_add_seconds(24*3600, self.midnight_callback)
459 def load_config(self):
460 self.set_height(self.config.get_height(), self.config.get_step_length())
461 self.set_mode(self.config.get_mode())
462 self.set_unit(self.config.get_unit())
463 self.set_weight(self.config.get_weight())
464 self.set_second_view(self.config.get_secondview())
465 self.set_no_idle_time(self.config.get_noidletime())
466 self.set_sensitivity(self.config.get_sensitivity())
468 def load_values(self):
469 if self.second_view == 0:
470 self.v[1] = self.repository.get_alltime_values()
471 elif self.second_view == 1:
472 self.v[1] = self.repository.get_today_values()
474 self.v[1] = self.repository.get_this_week_values()
476 def save_values(self):
477 self.repository.add_values(self.v[0])
478 self.repository.save()
481 def start_pedometer(self):
482 self.v[0] = PedoValues()
483 self.last_time = time.time()
484 self.is_running = True
485 self.pedometer.start()
488 def reset_all_values(self):
489 self.repository.reset_values()
490 self.v[0] = PedoValues()
491 self.v[1] = PedoValues()
494 def stop_pedometer(self):
495 self.is_running = False
496 self.pedometer.request_stop()
501 def get_second(self):
503 return self.v[0] + self.v[1]
507 def update_current(self):
509 Update distance and calories for current values based on new height, mode values
511 self.v[0].dist = self.get_distance(self.v[0].steps)
512 self.v[0].calories = self.get_calories(self.v[0].steps)
514 def steps_detected(self, cnt, last_steps=False):
515 if not last_steps and cnt == 0 and self.no_idle_time:
516 logger.info("No steps detected, timer is paused")
518 self.v[0].steps += cnt
519 self.v[0].dist += self.get_distance(cnt)
520 self.v[0].calories += self.get_calories(self.get_distance(cnt))
521 self.v[0].time += time.time() - self.last_time
527 self.last_time = time.time()
529 def get_calories(self, distance):
530 """calculate lost calories for the distance and weight given as parameters
532 #different coefficient for running and walking
538 #convert distance from meters to miles
539 distance *= 0.000621371192
542 #convert weight from kg to pounds
545 return weight * distance * coef
547 def set_mode(self, mode):
549 self.set_height(self.height_interval)
550 self.pedometerInterval.set_mode(self.mode)
553 def set_unit(self, new_unit):
559 def get_str_weight_unit(self, unit=None):
567 def set_weight(self, value):
571 def get_weight(self):
574 def set_sensitivity(self, value):
575 self.sensitivity = value
576 self.pedometerInterval.set_sensitivity(value)
578 def get_sensitivity(self):
579 return self.sensitivity
581 def set_second_view(self, second_view):
582 self.second_view = second_view
586 def set_callback_ui(self, func):
587 self.callback_update_ui = func
589 def set_height(self, height_interval, step_length=None):
590 self.height_interval = height_interval
592 if step_length is None:
593 step_length = self.STEP_LENGTH
594 #set height, will affect the distance
595 if height_interval == 0:
596 self.STEP_LENGTH = 0.59
597 elif height_interval == 1:
598 self.STEP_LENGTH = 0.64
599 elif height_interval == 2:
600 self.STEP_LENGTH = 0.71
601 elif height_interval == 3:
602 self.STEP_LENGTH = 0.77
603 elif height_interval == 4:
604 self.STEP_LENGTH = 0.83
605 elif height_interval == 5:
606 self.STEP_LENGTH = step_length
607 #increase step length if RUNNING
609 self.STEP_LENGTH *= 1.45
612 def set_no_idle_time(self, value):
613 self.no_idle_time = value
615 def get_distance(self, steps=None):
618 return self.STEP_LENGTH * steps;
620 def add_observer(self, func):
622 self.observers.index(func)
624 self.observers.append(func)
626 def remove_observer(self, func):
627 self.observers.remove(func)
629 def notify(self, optional=False):
630 if self.callback_update_ui is not None:
631 self.callback_update_ui()
633 for func in self.observers:
636 class AlarmController(Singleton):
638 fname = "/home/user/MyDocs/.sounds/Ringtones/Bicycle.aac"
644 pedo_controller = None
647 self.client = gconf.client_get_default()
648 self.config = Config()
649 self.config.add_observer(self.load_config)
651 self.pedo_controller = PedoController()
654 self.pedo_controller.add_observer(self.update)
655 self.start_value = self.pedo_controller.get_first()
657 def init_player(self):
658 self.player = gst.element_factory_make("playbin2", "player")
659 fakesink = gst.element_factory_make("fakesink", "fakesink")
660 self.player.set_property("video-sink", fakesink)
662 bus = self.player.get_bus()
663 bus.add_signal_watch()
664 bus.connect("message", self.on_message)
666 def on_message(self, bus, message):
668 if t == gst.MESSAGE_EOS:
669 self.player.set_state(gst.STATE_NULL)
670 self.is_playing = False
671 elif t == gst.MESSAGE_ERROR:
672 self.player.set_state(gst.STATE_NULL)
673 self.is_playing = False
674 err, debug = message.parse_error()
675 logger.error("ERROR: %s, %s" % (err, debug) )
677 def update(self, optional):
678 diff = self.pedo_controller.get_first() - self.start_value
679 if self.type == 0 and diff.time >= self.interval * 60 or \
680 self.type == 1 and diff.steps >= self.interval or \
681 self.type == 2 and diff.dist >= self.interval or \
682 self.type == 3 and diff.calories >= self.interval:
684 #get new instance of current values
685 self.start_value = PedoValues() + self.pedo_controller.get_first()
686 logger.info("Alarm!")
689 if self.player is None:
692 self.player.set_state(gst.STATE_NULL)
693 self.is_playing = False
695 self.player.set_property("uri", "file://" + self.fname)
696 self.player.set_state(gst.STATE_PLAYING)
697 self.is_playing = True
700 if self.player is not None:
701 self.player.set_state(gst.STATE_NULL)
703 def load_config(self):
704 self.enable = self.config.get_alarm_enable()
705 self.set_alarm_file(self.config.get_alarm_fname())
706 self.set_interval(self.config.get_alarm_interval())
707 self.set_type(self.config.get_alarm_type())
709 def set_enable(self, value):
713 self.pedo_controller.add_observer(self.update)
714 self.start_value = self.pedo_controller.get_first()
718 self.pedo_controller.remove_observer(self.update)
720 def get_enable(self):
723 def set_alarm_file(self, fname):
726 def get_alarm_file(self):
727 if self.fname == None:
731 def set_interval(self, interval):
732 self.interval = interval
734 def get_interval(self):
737 def set_type(self, type):
743 class PedoCounter(Singleton):
744 COORD_FNAME = "/sys/class/i2c-adapter/i2c-3/3-001d/coord"
745 COORD_FNAME_SDK = "/home/andrei/pedometer-widget-0.1/date.txt"
746 LOGFILE = "/home/user/log_pedometer"
747 #time in ms between two accelerometer data reads
748 COORD_GET_INTERVAL = 25
752 interval_counter = None
753 stop_requested = False
754 update_function = None
758 def __init__(self, update_function=None):
759 if not os.path.exists(self.COORD_FNAME):
760 self.COORD_FNAME = self.COORD_FNAME_SDK
762 self.interval_counter = PedoIntervalCounter()
763 self.update_function = update_function
765 def set_logging(self, value):
768 def get_rotation(self):
769 f = open(self.COORD_FNAME, 'r')
770 coords = [int(w) for w in f.readline().split()]
775 logger.info("Counter started")
776 self.isRunning = True
777 self.stop_requested = False
779 fname = "%d_%d_%d_%d_%d_%d" % time.localtime()[0:6]
780 self.file = open(self.LOGFILE + fname + ".txt", "w")
781 gobject.idle_add(self.run)
784 self.coords = [[], [], []]
785 self.stime = time.time()
787 gobject.timeout_add(self.COORD_GET_INTERVAL, self.read_coords)
790 def read_coords(self):
791 x, y, z = self.get_rotation()
792 self.coords[0].append(int(x))
793 self.coords[1].append(int(y))
794 self.coords[2].append(int(z))
795 now = time.time() - self.stime
797 self.file.write("%d %d %d %f\n" % (self.coords[0][-1], self.coords[1][-1], self.coords[2][-1], now))
802 if self.t[-1] > self.COUNT_INTERVAL or self.stop_requested:
804 gobject.idle_add(self.stop_interval)
807 def stop_interval(self):
808 self.interval_counter.set_vals(self.coords, self.t)
809 cnt = self.interval_counter.number_steps()
811 logger.info("Number of steps detected for last interval %d, number of coords: %d" % (cnt, len(self.t)))
813 gobject.idle_add(self.update_function, cnt, self.stop_requested)
815 if self.stop_requested:
816 gobject.idle_add(self.stop)
818 gobject.idle_add(self.run)
824 logger.info("Counter has finished")
826 def request_stop(self):
827 self.stop_requested = True
828 self.isRunning = False
830 class CustomButton(hildon.Button):
831 def __init__(self, icon):
832 hildon.Button.__init__(self, gtk.HILDON_SIZE_AUTO_WIDTH, hildon.BUTTON_ARRANGEMENT_VERTICAL)
834 self.set_size_request(int(32 * 1.4), int(30 * 1.0))
835 self.retval = self.connect("expose_event", self.expose)
837 def set_icon(self, icon):
840 def expose(self, widget, event):
841 self.context = widget.window.cairo_create()
842 self.context.rectangle(event.area.x, event.area.y,
843 event.area.width, event.area.height)
846 rect = self.get_allocation()
847 self.context.rectangle(rect.x, rect.y, rect.width, rect.height)
848 self.context.set_source_rgba(1, 1, 1, 0)
850 style = self.rc_get_style()
851 color = style.lookup_color("DefaultBackgroundColor")
852 if self.state == gtk.STATE_ACTIVE:
853 style = self.rc_get_style()
854 color = style.lookup_color("SelectionColor")
855 self.context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
858 #img = cairo.ImageSurface.create_from_png(self.icon)
860 #self.context.set_source_surface(img)
861 #self.context.set_source_surface(img, rect.width/2 - img.get_width() /2, 0)
863 img.set_from_file(self.icon)
864 buf = img.get_pixbuf()
865 buf = buf.scale_simple(int(32 * 1.5), int(30 * 1.5), gtk.gdk.INTERP_BILINEAR)
867 self.context.set_source_pixbuf(buf, rect.x + (event.area.width / 2 - 15) - 8, rect.y + 1)
868 self.context.scale(200, 200)
873 class CustomEventBox(gtk.EventBox):
876 gtk.EventBox.__init__(self)
878 def do_expose_event(self, event):
879 self.context = self.window.cairo_create()
880 self.context.rectangle(event.area.x, event.area.y,
881 event.area.width, event.area.height)
884 rect = self.get_allocation()
885 self.context.rectangle(rect.x, rect.y, rect.width, rect.height)
887 if self.state == gtk.STATE_ACTIVE:
888 style = self.rc_get_style()
889 color = style.lookup_color("SelectionColor")
890 self.context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
892 self.context.set_source_rgba(1, 1, 1, 0)
895 gtk.EventBox.do_expose_event(self, event)
897 class GraphController(Singleton):
898 ytitles = ["Steps", "Average Speed", "Distance", "Calories"]
899 xtitles = ["Day", "Week"] # "Today"]
905 self.repository = PedoRepositoryXML()
907 PedoController().add_observer(self.update_ui)
908 self.config = Config()
909 self.config.add_observer(self.load_config)
911 def load_config(self):
912 self.set_current_view(self.config.get_graphview())
914 def set_graph(self, widget):
918 def set_current_view(self, view):
920 current_view % len(ytitles) - gives the ytitle
921 current_view / len(ytitles) - gives the xtitle
923 self.x_id = view / len(self.ytitles)
924 self.y_id = view % len(self.ytitles)
928 current_view = self.config.get_graphview() + 1
929 if current_view == len(self.ytitles) * len(self.xtitles):
931 self.config.set_graphview(current_view)
933 def last_weeks_labels(self):
935 delta = timedelta(days=7)
938 ret.append(d.strftime("Week %W"))
942 def compute_values(self):
945 values = self.repository.get_last_7_days()
947 delta = timedelta(days=1)
949 labels.append(d.ctime().split()[0])
953 values = self.repository.get_last_weeks()
956 labels.append(d.strftime("Week %W"))
957 d = d - timedelta(days=7)
959 values = self.repository.get_today()
963 yvalues = [line.steps for line in values]
965 yvalues = [line.get_avg_speed() for line in values]
967 yvalues = [line.dist for line in values]
969 yvalues = [line.calories for line in values]
971 #determine values for y lines in graph
972 diff = self.get_best_interval_value(max(yvalues))
975 ytext.append(str(int(i*diff)))
977 if self.widget is not None:
980 self.widget.values = yvalues
981 self.widget.ytext = ytext
982 self.widget.xtext = labels
983 self.widget.max_value = diff * 5
984 self.widget.text = self.xtitles[self.x_id] + " / " + self.ytitles[self.y_id]
985 self.widget.queue_draw()
987 logger.error("Widget not set in GraphController")
989 def get_best_interval_value(self, max_value):
990 diff = 1.0 * max_value / 5
991 l = len(str(int(diff)))
992 d = math.pow(10, l/2)
993 val = int(math.ceil(1.0 * diff / d)) * d
998 def update_ui(self, optional=False):
999 """update graph values every x seconds"""
1000 if optional and self.last_update - time.time() < 600:
1002 if self.widget is None:
1005 self.compute_values()
1006 self.last_update = time.time()
1008 class GraphWidget(gtk.DrawingArea):
1011 gtk.DrawingArea.__init__(self)
1012 self.set_size_request(-1, 150)
1016 self.ytext = [" 0", "1000", "2000", "3000", "4000", "5000"]
1017 self.xtext = ["Monday", "Tuesday", "Wednesday","Thursday", "Friday", "Saturday", "Sunday"]
1018 self.values = [1500, 3400, 4000, 3600, 3200, 0, 4500]
1019 self.max_value = 5000
1020 self.text = "All time steps"
1022 def do_expose_event(self, event):
1023 context = self.window.cairo_create()
1025 # set a clip region for the expose event
1026 context.rectangle(event.area.x, event.area.y,
1027 event.area.width, event.area.height)
1032 context.set_operator(cairo.OPERATOR_SOURCE)
1033 style = self.rc_get_style()
1035 if self.state == gtk.STATE_ACTIVE:
1036 color = style.lookup_color("SelectionColor")
1038 color = style.lookup_color("DefaultBackgroundColor")
1039 context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75)
1051 rect = self.get_allocation()
1055 cr.select_font_face("Purisa", cairo.FONT_SLANT_NORMAL,
1056 cairo.FONT_WEIGHT_NORMAL)
1057 cr.set_font_size(13)
1059 #check space needed to display ylabels
1060 te = cr.text_extents(self.ytext[-1])
1061 border_left = te[2] + 7
1063 cr.set_source_rgb(1, 1, 1)
1064 cr.move_to(border_left, space_above)
1065 cr.line_to(border_left, y-space_below)
1066 cr.set_line_width(2)
1069 cr.move_to(border_left, y-space_below)
1070 cr.line_to(x-border_right, y-space_below)
1071 cr.set_line_width(2)
1074 ydiff = (y-space_above-space_below) / self.yvalues
1075 for i in range(self.yvalues):
1076 yy = y-space_below-ydiff*(i+1)
1077 cr.move_to(border_left, yy)
1078 cr.line_to(x-border_right, yy)
1079 cr.set_line_width(0.8)
1084 yy = y - space_below - ydiff*i + 5
1085 te = cr.text_extents(self.ytext[i])
1087 cr.move_to(border_left-te[2]-2, yy)
1088 cr.show_text(self.ytext[i])
1090 cr.set_font_size(15)
1091 te = cr.text_extents(self.text)
1092 cr.move_to((x-te[2])/2, y-5)
1093 cr.show_text(self.text)
1095 graph_x_space = x - border_left - border_right
1096 graph_y_space = y - space_below - space_above
1097 bar_width = graph_x_space*0.75 / len(self.values)
1098 bar_distance = graph_x_space*0.25 / (1+len(self.values))
1100 #set dummy max value to avoid exceptions
1101 if self.max_value == 0:
1102 self.max_value = 100
1103 for i in range(len(self.values)):
1104 xx = border_left + (i+1)*bar_distance + i * bar_width
1106 height = graph_y_space * (1.0 * self.values[i] / self.max_value)
1107 cr.set_source_rgba(1, 1, 1, 0.75)
1108 cr.rectangle(int(xx), int(yy-height), int(bar_width), int(height))
1111 cr.set_source_rgba(1, 1, 1, 1)
1112 cr.select_font_face("Purisa", cairo.FONT_SLANT_NORMAL,
1113 cairo.FONT_WEIGHT_NORMAL)
1114 cr.set_font_size(13)
1116 cr.rotate(2*math.pi * (-45) / 180)
1117 for i in range(len(self.values)):
1118 xx = y - space_below - 10
1119 yy = border_left + (i+1)*bar_distance + i * bar_width
1120 cr.move_to(-xx, yy + bar_width*1.25 / 2)
1121 cr.show_text(self.xtext[i])
1123 class Config(Singleton):
1134 no_idle_time = False
1137 alarm_enable = False
1138 alarm_fname = "/home/user/MyDocs/.sounds/Ringtones/Bicycle.aac"
1145 if self._references > 1:
1147 self.client = gconf.client_get_default()
1148 self.client.add_dir('/apps/pedometerhomewidget', gconf.CLIENT_PRELOAD_RECURSIVE)
1149 self.notify_id = self.client.notify_add('/apps/pedometerhomewidget', self.gconf_changed)
1151 def add_observer(self, func):
1153 self.observers.index(func)
1155 self.observers.append(func)
1158 def remove_observer(self, func):
1159 self.observers.remove(func)
1161 def gconf_changed(self, client, *args, **kargs):
1166 for func in self.observers:
1169 logger.info("Update took: %f seconds" % (t2-t1))
1172 return self.client.get_int(MODE)
1174 def set_mode(self, value):
1175 self.client.set_int(MODE, value)
1177 def get_height(self):
1178 return self.client.get_int(HEIGHT)
1180 def set_height(self, value):
1181 self.client.set_int(HEIGHT, value)
1183 def get_step_length(self):
1184 return self.client.get_float(STEP_LENGTH)
1186 def set_step_length(self, value):
1187 self.client.set_float(STEP_LENGTH, value)
1189 def get_weight(self):
1190 return self.client.get_int(WEIGHT)
1192 def set_weight(self, value):
1193 self.client.set_int(WEIGHT, value)
1195 def get_sensitivity(self):
1196 return self.client.get_int(SENSITIVITY)
1198 def set_sensitivity(self, value):
1199 self.client.set_int(SENSITIVITY, value)
1202 return self.client.get_int(UNIT)
1204 def set_unit(self, value):
1205 self.client.set_int(UNIT, value)
1207 def get_aspect(self):
1208 return self.client.get_int(ASPECT)
1210 def set_aspect(self, value):
1211 self.client.set_int(ASPECT, value)
1213 def get_secondview(self):
1214 value = self.client.get_int(SECONDVIEW)
1215 if value < 0 or value > 2:
1217 logger.error("Invalid secondview value read from Gconf. Using default value")
1221 def set_secondview(self, value):
1222 self.client.set_int(SECONDVIEW, value)
1224 def get_graphview(self):
1225 return self.client.get_int(GRAPHVIEW)
1227 def set_graphview(self, value):
1228 self.client.set_int(GRAPHVIEW, value)
1230 def get_noidletime(self):
1231 return self.client.get_bool(NOIDLETIME)
1233 def set_noidletime(self, value):
1234 self.client.set_bool(NOIDLETIME, value)
1236 def get_logging(self):
1237 return self.client.get_bool(LOGGING)
1239 def set_logging(self, value):
1240 self.client.set_bool(LOGGING, value)
1242 def get_alarm_enable(self):
1243 return self.client.get_bool(ALARM_ENABLE)
1245 def set_alarm_enable(self, value):
1246 self.client.set_bool(ALARM_ENABLE, value)
1248 def get_alarm_fname(self):
1249 return self.client.get_string(ALARM_FNAME)
1251 def set_alarm_fname(self, value):
1252 self.client.set_string(ALARM_FNAME, value)
1254 def get_alarm_interval(self):
1255 return self.client.get_int(ALARM_INTERVAL)
1257 def set_alarrm_interval(self, value):
1258 self.client.set_int(ALARM_INTERVAL, value)
1260 def get_alarm_type(self):
1261 return self.client.get_int(ALARM_TYPE)
1263 def set_alarm_type(self, value):
1264 self.client.set_int(ALARM_TYPE, value)
1266 class PedometerHomePlugin(hildondesktop.HomePluginItem):
1270 labels = ["timer", "count", "dist", "avgSpeed", "calories"]
1275 #second view ( day / week/ alltime)
1278 second_view_labels = ["All-time", "Today", "This week"]
1281 graph_controller = None
1286 hildondesktop.HomePluginItem.__init__(self)
1288 gobject.type_register(CustomEventBox)
1289 gobject.type_register(GraphWidget)
1291 self.config = Config()
1293 self.button = CustomButton(ICONSPATH + "play.png")
1294 self.button.connect("clicked", self.button_clicked)
1296 self.create_labels(self.labelsC)
1297 self.create_labels(self.labelsT)
1298 self.label_second_view = self.new_label_heading(self.second_view_labels[self.config.get_secondview()])
1300 self.controller = PedoController()
1301 self.controller.set_callback_ui(self.update_values)
1303 self.graph_controller = GraphController()
1304 self.alarm_controller = AlarmController()
1306 self.update_current()
1309 mainHBox = gtk.HBox(spacing=1)
1311 descVBox = gtk.VBox(spacing=1)
1312 descVBox.add(self.new_label_heading())
1313 descVBox.add(self.new_label_heading("Time:"))
1314 descVBox.add(self.new_label_heading("Steps:"))
1315 descVBox.add(self.new_label_heading("Calories:"))
1316 descVBox.add(self.new_label_heading("Distance:"))
1317 descVBox.add(self.new_label_heading("Avg Speed:"))
1319 currentVBox = gtk.VBox(spacing=1)
1320 currentVBox.add(self.new_label_heading("Current"))
1321 currentVBox.add(self.labelsC["timer"])
1322 currentVBox.add(self.labelsC["count"])
1323 currentVBox.add(self.labelsC["calories"])
1324 currentVBox.add(self.labelsC["dist"])
1325 currentVBox.add(self.labelsC["avgSpeed"])
1326 self.currentBox = currentVBox
1328 totalVBox = gtk.VBox(spacing=1)
1329 totalVBox.add(self.label_second_view)
1330 totalVBox.add(self.labelsT["timer"])
1331 totalVBox.add(self.labelsT["count"])
1332 totalVBox.add(self.labelsT["calories"])
1333 totalVBox.add(self.labelsT["dist"])
1334 totalVBox.add(self.labelsT["avgSpeed"])
1335 self.totalBox = totalVBox
1337 buttonVBox = gtk.VBox(spacing=1)
1338 buttonVBox.add(self.new_label_heading(""))
1339 buttonVBox.add(self.button)
1340 buttonVBox.add(self.new_label_heading(""))
1342 eventBox = CustomEventBox()
1343 eventBox.set_visible_window(False)
1344 eventBox.add(totalVBox)
1345 eventBox.connect("button-press-event", self.eventBox_clicked)
1346 eventBox.connect("button-release-event", self.eventBox_clicked_release)
1348 mainHBox.add(buttonVBox)
1349 mainHBox.add(descVBox)
1350 mainHBox.add(currentVBox)
1351 mainHBox.add(eventBox)
1352 self.mainhbox = mainHBox
1354 graph = GraphWidget()
1355 self.graph_controller.set_graph(graph)
1357 eventBoxGraph = CustomEventBox()
1358 eventBoxGraph.set_visible_window(False)
1359 eventBoxGraph.add(graph)
1361 eventBoxGraph.connect("button-press-event", self.eventBoxGraph_clicked)
1362 eventBoxGraph.connect("button-release-event", self.eventBoxGraph_clicked_release)
1363 self.graphBox = eventBoxGraph
1365 self.mainvbox = gtk.VBox()
1367 self.mainvbox.add(mainHBox)
1368 self.mainvbox.add(eventBoxGraph)
1370 self.mainvbox.show_all()
1371 self.add(self.mainvbox)
1372 self.update_aspect()
1374 self.connect("unrealize", self.close_requested)
1375 self.set_settings(True)
1376 self.connect("show-settings", self.show_settings)
1378 def eventBoxGraph_clicked(self, widget, data=None):
1379 widget.set_state(gtk.STATE_ACTIVE)
1381 def eventBoxGraph_clicked_release(self, widget, data=None):
1382 self.graph_controller.next_view()
1383 widget.set_state(gtk.STATE_NORMAL)
1385 def eventBox_clicked(self, widget, data=None):
1386 widget.set_state(gtk.STATE_ACTIVE)
1388 def eventBox_clicked_release(self, widget, data=None):
1389 widget.set_state(gtk.STATE_NORMAL)
1391 second_view = self.config.get_secondview()
1392 second_view = (second_view + 1) % 3
1393 self.config.set_secondview(second_view)
1395 def new_label_heading(self, title=""):
1396 l = gtk.Label(title)
1397 hildon.hildon_helper_set_logical_font(l, "SmallSystemFont")
1400 def create_labels(self, new_labels):
1401 for label in self.labels:
1403 hildon.hildon_helper_set_logical_font(l, "SmallSystemFont")
1404 hildon.hildon_helper_set_logical_color(l, gtk.RC_FG, gtk.STATE_NORMAL, "ActiveTextColor")
1405 new_labels[label] = l
1407 def update_aspect(self):
1408 aspect = self.config.get_aspect()
1410 self.graphBox.hide_all()
1412 self.graphBox.show_all()
1414 if aspect == 0 or aspect == 1:
1415 self.currentBox.show_all()
1416 self.totalBox.show_all()
1418 self.currentBox.show_all()
1419 self.totalBox.hide_all()
1421 self.currentBox.hide_all()
1422 self.totalBox.show_all()
1424 x,y = self.size_request()
1427 def update_ui_values(self, labels, values):
1428 labels["timer"].set_label(values.get_print_time())
1429 labels["count"].set_label(values.get_print_steps())
1430 labels["dist"].set_label(values.get_print_distance())
1431 labels["avgSpeed"].set_label(values.get_print_avg_speed())
1432 labels["calories"].set_label(values.get_print_calories())
1434 def update_current(self):
1435 self.update_ui_values(self.labelsC, self.controller.get_first())
1437 def update_total(self):
1438 self.update_ui_values(self.labelsT, self.controller.get_second())
1440 def show_alarm_settings(self, main_button):
1441 def choose_file(widget):
1442 file = hildon.FileChooserDialog(self, gtk.FILE_CHOOSER_ACTION_OPEN, hildon.FileSystemModel() )
1444 if ( file.run() == gtk.RESPONSE_OK):
1445 fname = file.get_filename()
1446 widget.set_value(fname)
1447 self.config.set_alarm_fname(fname)
1450 def test_sound(button):
1452 self.alarm_controller.play()
1453 except Exception, e:
1454 logger.error("Could not play alarm sound: %s" % e)
1455 hildon.hildon_banner_show_information(self, "None", "Could not play alarm sound")
1457 def enableButton_changed(button):
1458 value = button.get_active()
1459 self.config.set_alarm_enable(value)
1461 main_button.set_value("Enabled")
1463 main_button.set_value("Disabled")
1465 def selectorType_changed(selector, data, labelEntry2):
1466 type = selector.get_active(0)
1467 self.config.set_alarm_type(type)
1468 labelEntry2.set_label(suffix[type])
1470 dialog = gtk.Dialog()
1471 dialog.set_title("Alarm settings")
1472 dialog.add_button("OK", gtk.RESPONSE_OK)
1474 enableButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1475 enableButton.set_label("Enable alarm")
1476 enableButton.set_active(self.alarm_controller.get_enable())
1477 enableButton.connect("toggled", enableButton_changed)
1479 testButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1480 testButton.set_alignment(0, 0.8, 1, 1)
1481 testButton.set_title("Test sound")
1482 testButton.connect("pressed", test_sound)
1484 fileButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1485 fileButton.set_alignment(0, 0.8, 1, 1)
1486 fileButton.set_title("Alarm sound")
1487 fileButton.set_value(self.alarm_controller.get_alarm_file())
1488 fileButton.connect("pressed", choose_file)
1490 labelEntry = gtk.Label("Notify every:")
1491 suffix = ["mins", "steps", "m/ft", "calories"]
1492 labelEntry2 = gtk.Label(suffix[self.alarm_controller.get_type()])
1493 intervalEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO_WIDTH)
1494 intervalEntry.set_text(str(self.alarm_controller.get_interval()))
1496 selectorType = hildon.TouchSelector(text=True)
1497 selectorType.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1498 selectorType.append_text("Time")
1499 selectorType.append_text("Steps")
1500 selectorType.append_text("Distance")
1501 selectorType.append_text("Calories")
1502 selectorType.connect("changed", selectorType_changed, labelEntry2)
1504 typePicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1505 typePicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1506 typePicker.set_title("Alarm type")
1507 typePicker.set_selector(selectorType)
1508 typePicker.set_active(self.alarm_controller.get_type())
1511 hbox.add(labelEntry)
1512 hbox.add(intervalEntry)
1513 hbox.add(labelEntry2)
1515 dialog.vbox.add(enableButton)
1516 dialog.vbox.add(fileButton)
1517 dialog.vbox.add(testButton)
1518 dialog.vbox.add(typePicker)
1519 dialog.vbox.add(hbox)
1522 response = dialog.run()
1523 if response != gtk.RESPONSE_OK:
1526 value = int(intervalEntry.get_text())
1527 self.config.set_alarrm_interval(value)
1530 hildon.hildon_banner_show_information(self, "None", "Invalid interval")
1534 def show_settings(self, widget):
1535 def reset_total_counter(arg):
1536 note = hildon.hildon_note_new_confirmation(self.dialog, "Are you sure you want to delete all your pedometer history?")
1538 if ret == gtk.RESPONSE_OK:
1539 self.controller.reset_all_values()
1540 hildon.hildon_banner_show_information(self, "None", "All history was deleted")
1543 def alarmButton_pressed(widget):
1544 self.show_alarm_settings(widget)
1546 def selector_changed(selector, data):
1547 mode = selector.get_active(0)
1548 self.config.set_mode(mode)
1550 def selectorUnit_changed(selector, data):
1551 unit = selector.get_active(0)
1552 self.config.set_unit(unit)
1554 update_weight_button()
1555 stepLengthButton_value_update()
1557 def selectorUI_changed(selector, data):
1558 aspect = selectorUI.get_active(0)
1559 widget.update_aspect()
1561 def logButton_changed(checkButton):
1562 logging = checkButton.get_active()
1563 self.config.set_logging(logging)
1565 def idleButton_changed(idleButton):
1566 no_idle_time = idleButton.get_active()
1567 self.config.set_noidletime(no_idle_time)
1569 def update_weight_button():
1570 weightButton.set_value(str(self.config.get_weight()) + \
1571 " " + self.controller.get_str_weight_unit(self.config.get_unit()) )
1573 def weight_dialog(button):
1574 dialog = gtk.Dialog("Weight", self.dialog)
1575 dialog.add_button("OK", gtk.RESPONSE_OK)
1577 label = gtk.Label("Weight:")
1579 entry.set_text(str(self.config.get_weight()))
1581 suffixLabel = gtk.Label(self.controller.get_str_weight_unit(self.config.get_unit()))
1586 hbox.add(suffixLabel)
1588 dialog.vbox.add(hbox)
1591 response = dialog.run()
1592 if response != gtk.RESPONSE_OK:
1595 value = int(entry.get_text())
1598 self.config.set_weight(value)
1599 update_weight_button()
1602 hildon.hildon_banner_show_information(self, "None", "Invalid weight")
1605 def sensitivity_dialog(button):
1606 def seekbar_changed(seekbar):
1607 label.set_text(str(seekbar.get_position()) + " %")
1609 dialog = gtk.Dialog("Sensitivity", self.dialog)
1610 dialog.add_button("OK", gtk.RESPONSE_OK)
1611 seekbar = hildon.Seekbar()
1612 seekbar.set_size_request(400, -1)
1613 seekbar.set_total_time(200)
1614 seekbar.set_position(self.config.get_sensitivity())
1615 seekbar.connect("value-changed", seekbar_changed)
1619 label = gtk.Label(str(self.config.get_sensitivity()) + " %")
1620 label.set_size_request(30, -1)
1623 dialog.vbox.add(hbox)
1626 if dialog.run() == gtk.RESPONSE_OK:
1627 value = seekbar.get_position()
1628 self.config.set_sensitivity(value)
1629 button.set_value(str(value) + " %")
1633 def stepLengthButton_value_update():
1634 if self.config.get_height() == 5:
1635 l_unit = ["m", "ft"]
1636 stepLengthButton.set_value("Custom value: %.2f %s" % (self.config.get_step_length(), l_unit[self.config.get_unit()]))
1638 h = [ ["< 1.50 m", "1.50 - 1.65 m", "1.66 - 1.80 m", "1.81 - 1.95 m", " > 1.95 m"],
1639 ["< 5 ft", "5 - 5.5 ft", "5.5 - 6 ft", "6 - 6.5 ft", "> 6.5 ft"]]
1640 str = "Using predefined value for height: %s" % h[self.config.get_unit()][self.config.get_height()]
1641 stepLengthButton.set_value(str)
1643 def stepLength_dialog(button):
1644 def selectorH_changed(selector, data, dialog):
1645 height = selector.get_active(0)
1646 self.config.set_height(height)
1647 stepLengthButton_value_update()
1649 def manualButton_clicked(button, dialog):
1651 dlg.set_title("Custom step length")
1652 dlg.add_button("OK", gtk.RESPONSE_OK)
1654 label = gtk.Label("Length")
1656 entry = hildon.Entry(gtk.HILDON_SIZE_AUTO_WIDTH)
1657 if self.config.get_height() == 5:
1658 entry.set_text(str(self.config.get_step_length()))
1660 labelSuffix = gtk.Label()
1661 if self.config.get_unit() == 0:
1662 labelSuffix.set_label("m")
1664 labelSuffix.set_label("ft")
1668 hbox.add(labelSuffix)
1673 response = dlg.run()
1674 if response != gtk.RESPONSE_OK:
1677 value = float(entry.get_text())
1680 self.config.set_step_length(value)
1681 self.config.set_height(5)
1682 stepLengthButton_value_update()
1685 hildon.hildon_banner_show_information(self, "None", "Invalid length")
1689 def heightButton_clicked(button, dialog):
1692 dialog = gtk.Dialog()
1693 dialog.set_title("Step length")
1695 manualButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1696 manualButton.set_title("Enter custom value")
1697 manualButton.set_alignment(0, 0.8, 1, 1)
1698 manualButton.connect("clicked", manualButton_clicked, dialog)
1700 selectorH = hildon.TouchSelector(text=True)
1701 selectorH.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1702 selectorH.append_text("< 1.50 m")
1703 selectorH.append_text("1.50 - 1.65 m")
1704 selectorH.append_text("1.66 - 1.80 m")
1705 selectorH.append_text("1.81 - 1.95 m")
1706 selectorH.append_text(" > 1.95 m")
1708 selectorH_English = hildon.TouchSelector(text=True)
1709 selectorH_English.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1710 selectorH_English.append_text("< 5 ft")
1711 selectorH_English.append_text("5 - 5.5 ft")
1712 selectorH_English.append_text("5.5 - 6 ft")
1713 selectorH_English.append_text("6 - 6.5 ft")
1714 selectorH_English.append_text("> 6.5 ft")
1716 heightPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1717 heightPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1718 heightPicker.set_title("Use predefined values for height")
1721 unit = self.config.get_unit()
1723 heightPicker.set_selector(selectorH)
1725 heightPicker.set_selector(selectorH_English)
1727 height = self.config.get_height()
1729 heightPicker.set_active(height)
1731 heightPicker.get_selector().connect("changed", selectorH_changed, dialog)
1732 heightPicker.connect("value-changed", heightButton_clicked, dialog)
1734 dialog.vbox.add(heightPicker)
1735 dialog.vbox.add(manualButton)
1738 if dialog.run() == gtk.RESPONSE_DELETE_EVENT:
1741 def donateButton_clicked(button, dialog):
1742 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"
1743 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
1746 dialog = gtk.Dialog()
1747 dialog.set_title("Settings")
1748 dialog.add_button("OK", gtk.RESPONSE_OK)
1749 self.dialog = dialog
1751 stepLengthButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1752 stepLengthButton.set_title("Step length")
1753 stepLengthButton.set_alignment(0, 0.8, 1, 1)
1754 stepLengthButton.connect("clicked", stepLength_dialog)
1755 stepLengthButton_value_update()
1757 resetButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1758 resetButton.set_title("Reset total counter")
1759 resetButton.set_alignment(0, 0.8, 1, 1)
1760 resetButton.connect("clicked", reset_total_counter)
1762 alarmButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1763 alarmButton.set_title("Alarm")
1764 if self.config.get_alarm_enable():
1765 alarmButton.set_value("Enabled")
1767 alarmButton.set_value("Disabled")
1768 alarmButton.set_alignment(0, 0.8, 1, 1)
1769 alarmButton.connect("clicked", alarmButton_pressed)
1771 selector = hildon.TouchSelector(text=True)
1772 selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1773 selector.append_text("Walk")
1774 selector.append_text("Run")
1775 selector.connect("changed", selector_changed)
1777 modePicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1778 modePicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1779 modePicker.set_title("Mode")
1780 modePicker.set_selector(selector)
1781 modePicker.set_active(self.config.get_mode())
1783 weightButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1784 weightButton.set_title("Weight")
1785 weightButton.set_alignment(0, 0.8, 1, 1)
1786 update_weight_button()
1787 weightButton.connect("clicked", weight_dialog)
1789 selectorUnit = hildon.TouchSelector(text=True)
1790 selectorUnit.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1791 selectorUnit.append_text("Metric (km)")
1792 selectorUnit.append_text("English (mi)")
1793 selectorUnit.connect("changed", selectorUnit_changed)
1795 unitPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1796 unitPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1797 unitPicker.set_title("Unit")
1798 unitPicker.set_selector(selectorUnit)
1799 unitPicker.set_active(self.config.get_unit())
1801 selectorUI = hildon.TouchSelector(text=True)
1802 selectorUI = hildon.TouchSelector(text=True)
1803 selectorUI.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1804 selectorUI.append_text("Show current + total + graph")
1805 selectorUI.append_text("Show current + total")
1806 selectorUI.append_text("Show only current")
1807 selectorUI.append_text("Show only total")
1808 selectorUI.connect("changed", selectorUI_changed)
1810 UIPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1811 UIPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1812 UIPicker.set_title("Widget aspect")
1813 UIPicker.set_selector(selectorUI)
1814 UIPicker.set_active(self.config.get_aspect())
1816 sensitivityButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1817 sensitivityButton.set_title("Sensitivity")
1818 sensitivityButton.set_alignment(0, 0.8, 1, 1)
1819 sensitivityButton.set_value(str(self.config.get_sensitivity()) + " %")
1820 sensitivityButton.connect("clicked", sensitivity_dialog)
1822 donateButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1823 donateButton.set_title("Donate")
1824 donateButton.set_alignment(0, 0.8, 1, 1)
1825 donateButton.connect("clicked", donateButton_clicked, dialog)
1827 logButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1828 logButton.set_label("Log data")
1829 logButton.set_active(self.config.get_logging())
1830 logButton.connect("toggled", logButton_changed)
1832 idleButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1833 idleButton.set_label("Pause time when not walking")
1834 idleButton.set_active(self.config.get_noidletime())
1835 idleButton.connect("toggled", idleButton_changed)
1837 pan_area = hildon.PannableArea()
1839 vbox.add(alarmButton)
1840 vbox.add(modePicker)
1841 vbox.add(stepLengthButton)
1842 vbox.add(weightButton)
1843 vbox.add(unitPicker)
1844 vbox.add(sensitivityButton)
1846 vbox.add(idleButton)
1847 vbox.add(resetButton)
1848 vbox.add(donateButton)
1849 #vbox.add(logButton)
1851 pan_area.add_with_viewport(vbox)
1852 pan_area.set_size_request(-1, 300)
1854 dialog.vbox.add(pan_area)
1857 response = dialog.run()
1860 def close_requested(self, widget):
1861 if self.controller.is_running:
1862 self.controller.stop_pedometer()
1863 self.controller.stop_midnight_callback()
1865 def update_values(self):
1866 #TODO: do not update if the widget is not on the active desktop
1867 self.label_second_view.set_label(self.second_view_labels[self.config.get_secondview()])
1868 self.update_current()
1871 def button_clicked(self, button):
1872 if self.controller.is_running:
1873 self.controller.stop_pedometer()
1874 self.button.set_icon(ICONSPATH + "play.png")
1876 self.controller.start_pedometer()
1877 self.button.set_icon(ICONSPATH + "stop.png")
1878 hildon.hildon_banner_show_information(self, "None", "Keep the N900 in a pocket close to your hip for best results")
1880 def do_expose_event(self, event):
1881 cr = self.window.cairo_create()
1882 cr.region(event.window.get_clip_region())
1884 #cr.set_source_rgba(0.4, 0.64, 0.564, 0.5)
1885 style = self.rc_get_style()
1886 color = style.lookup_color("DefaultBackgroundColor")
1887 cr.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
1890 width = self.allocation.width
1891 height = self.allocation.height
1893 x = self.allocation.x
1894 y = self.allocation.y
1896 cr.move_to(x + radius, y)
1897 cr.line_to(x + width - radius, y)
1898 cr.curve_to(x + width - radius, y, x + width, y, x + width, y + radius)
1899 cr.line_to(x + width, y + height - radius)
1900 cr.curve_to(x + width, y + height - radius, x + width, y + height, x + width - radius, y + height)
1901 cr.line_to(x + radius, y + height)
1902 cr.curve_to(x + radius, y + height, x, y + height, x, y + height - radius)
1903 cr.line_to(x, y + radius)
1904 cr.curve_to(x, y + radius, x, y, x + radius, y)
1906 cr.set_operator(cairo.OPERATOR_SOURCE)
1909 color = style.lookup_color("ActiveTextColor")
1910 cr.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.5);
1911 cr.set_line_width(1)
1914 hildondesktop.HomePluginItem.do_expose_event(self, event)
1916 def do_realize(self):
1917 screen = self.get_screen()
1918 self.set_colormap(screen.get_rgba_colormap())
1919 self.set_app_paintable(True)
1920 hildondesktop.HomePluginItem.do_realize(self)
1922 hd_plugin_type = PedometerHomePlugin
1927 logger = logging.getLogger("pedometer")
1928 logger.setLevel(logging.INFO)
1930 ch = logging.StreamHandler()
1931 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
1932 ch.setFormatter(formatter)
1933 logger.addHandler(ch)
1935 # The code below is just for testing purposes.
1936 # It allows to run the widget as a standalone process.
1937 if __name__ == "__main__":
1939 gobject.type_register(hd_plugin_type)
1940 obj = gobject.new(hd_plugin_type, plugin_id="plugin_id")