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
426 midnight_before_source_id = None
430 self.pedometer = PedoCounter(self.steps_detected)
431 self.pedometerInterval = PedoIntervalCounter()
432 self.pedometerInterval.set_mode(self.mode)
433 self.repository = PedoRepositoryXML()
434 self.repository.load()
438 if not self.midnight_set:
439 self.update_at_midnight()
440 self.midnight_set = True
442 self.config = Config()
443 self.config.add_observer(self.load_config)
445 def update_at_midnight(self):
446 next_day = date.today() + timedelta(days=1)
447 diff = int(time.mktime(next_day.timetuple()) - time.time())
448 diff_before = diff - 5
449 diff_after = diff + 5
450 self.midnight_source_id = gobject.timeout_add_seconds(diff_after, self.midnight_callback, True)
451 self.midnight_before_source_id = gobject.timeout_add_seconds(diff_before, self.midnight_before_callback, True)
453 def stop_midnight_callback(self):
454 if self.midnight_source_id is not None:
455 gobject.source_remove(self.midnight_source_id)
456 if self.midnight_before_source_id is not None:
457 gobject.source_remove(self.midnight_before_source_id)
459 def midnight_before_callback(self, first=False):
460 logger.info("Before midnight callback")
462 self.stop_pedometer()
463 self.start_pedometer()
465 self.midnight_before_source_id = gobject.timeout_add_seconds(24*3600, self.midnight_before_callback)
470 def midnight_callback(self, first=False):
471 logger.info("Midnight callback")
475 self.midnight_source_id = gobject.timeout_add_seconds(24*3600, self.midnight_callback)
480 def load_config(self):
481 self.set_height(self.config.get_height(), self.config.get_step_length())
482 self.set_mode(self.config.get_mode())
483 self.set_unit(self.config.get_unit())
484 self.set_weight(self.config.get_weight())
485 self.set_second_view(self.config.get_secondview())
486 self.set_no_idle_time(self.config.get_noidletime())
487 self.set_sensitivity(self.config.get_sensitivity())
489 def load_values(self):
490 if self.second_view == 0:
491 self.v[1] = self.repository.get_alltime_values()
492 elif self.second_view == 1:
493 self.v[1] = self.repository.get_today_values()
495 self.v[1] = self.repository.get_this_week_values()
497 def save_values(self):
498 logger.info("Saving values to file")
499 self.repository.add_values(self.v[2])
500 self.repository.save()
503 def start_pedometer(self):
504 self.v[0] = PedoValues()
505 self.v[2] = PedoValues()
506 self.last_time = time.time()
507 self.is_running = True
508 self.pedometer.start()
511 def reset_all_values(self):
512 self.repository.reset_values()
513 self.v[0] = PedoValues()
514 self.v[1] = PedoValues()
515 self.v[2] = PedoValues()
518 def stop_pedometer(self):
519 self.is_running = False
520 self.pedometer.request_stop()
525 def get_second(self):
527 return self.v[2] + self.v[1]
531 def update_current(self):
533 Update distance and calories for current values based on new height, mode values
535 self.v[0].dist = self.get_distance(self.v[0].steps)
536 self.v[0].calories = self.get_calories(self.v[0].steps)
538 def steps_detected(self, cnt, last_steps=False):
539 if not last_steps and cnt == 0 and self.no_idle_time:
540 logger.info("No steps detected, timer is paused")
542 self.v[0].steps += cnt
543 self.v[0].dist += self.get_distance(cnt)
544 self.v[0].calories += self.get_calories(self.get_distance(cnt))
545 self.v[0].time += time.time() - self.last_time
547 self.v[2].steps += cnt
548 self.v[2].dist += self.get_distance(cnt)
549 self.v[2].calories += self.get_calories(self.get_distance(cnt))
550 self.v[2].time += time.time() - self.last_time
552 if not last_steps and self.v[2].steps > self.BUFFER_STEPS_INTERVAL:
555 self.v[2] = PedoValues()
562 self.last_time = time.time()
564 def get_calories(self, distance):
565 """calculate lost calories for the distance and weight given as parameters
567 #different coefficient for running and walking
573 #convert distance from meters to miles
574 distance *= 0.000621371192
577 #convert weight from kg to pounds
580 return weight * distance * coef
582 def set_mode(self, mode):
584 self.set_height(self.height_interval)
585 self.pedometerInterval.set_mode(self.mode)
588 def set_unit(self, new_unit):
594 def get_str_weight_unit(self, unit=None):
602 def set_weight(self, value):
606 def get_weight(self):
609 def set_sensitivity(self, value):
610 self.sensitivity = value
611 self.pedometerInterval.set_sensitivity(value)
613 def get_sensitivity(self):
614 return self.sensitivity
616 def set_second_view(self, second_view):
617 self.second_view = second_view
621 def set_callback_ui(self, func):
622 self.callback_update_ui = func
624 def set_height(self, height_interval, step_length=None):
625 self.height_interval = height_interval
627 if step_length is None:
628 step_length = self.STEP_LENGTH
629 #set height, will affect the distance
630 if height_interval == 0:
631 self.STEP_LENGTH = 0.59
632 elif height_interval == 1:
633 self.STEP_LENGTH = 0.64
634 elif height_interval == 2:
635 self.STEP_LENGTH = 0.71
636 elif height_interval == 3:
637 self.STEP_LENGTH = 0.77
638 elif height_interval == 4:
639 self.STEP_LENGTH = 0.83
640 elif height_interval == 5:
641 self.STEP_LENGTH = step_length
642 #increase step length if RUNNING
644 self.STEP_LENGTH *= 1.45
647 def set_no_idle_time(self, value):
648 self.no_idle_time = value
650 def get_distance(self, steps=None):
653 return self.STEP_LENGTH * steps;
655 def add_observer(self, func):
657 self.observers.index(func)
659 self.observers.append(func)
661 def remove_observer(self, func):
662 self.observers.remove(func)
664 def notify(self, optional=False):
665 if self.callback_update_ui is not None:
666 self.callback_update_ui()
668 for func in self.observers:
671 class AlarmController(Singleton):
673 fname = "/home/user/MyDocs/.sounds/Ringtones/Bicycle.aac"
679 pedo_controller = None
682 self.client = gconf.client_get_default()
683 self.config = Config()
684 self.config.add_observer(self.load_config)
686 self.pedo_controller = PedoController()
689 self.pedo_controller.add_observer(self.update)
690 self.start_value = self.pedo_controller.get_first()
692 def init_player(self):
693 self.player = gst.element_factory_make("playbin2", "player")
694 fakesink = gst.element_factory_make("fakesink", "fakesink")
695 self.player.set_property("video-sink", fakesink)
697 bus = self.player.get_bus()
698 bus.add_signal_watch()
699 bus.connect("message", self.on_message)
701 def on_message(self, bus, message):
703 if t == gst.MESSAGE_EOS:
704 self.player.set_state(gst.STATE_NULL)
705 self.is_playing = False
706 elif t == gst.MESSAGE_ERROR:
707 self.player.set_state(gst.STATE_NULL)
708 self.is_playing = False
709 err, debug = message.parse_error()
710 logger.error("ERROR: %s, %s" % (err, debug) )
712 def update(self, optional):
713 diff = self.pedo_controller.get_first() - self.start_value
714 if self.type == 0 and diff.time >= self.interval * 60 or \
715 self.type == 1 and diff.steps >= self.interval or \
716 self.type == 2 and diff.dist >= self.interval or \
717 self.type == 3 and diff.calories >= self.interval:
719 #get new instance of current values
720 self.start_value = PedoValues() + self.pedo_controller.get_first()
721 logger.info("Alarm!")
724 if self.player is None:
727 self.player.set_state(gst.STATE_NULL)
728 self.is_playing = False
730 self.player.set_property("uri", "file://" + self.fname)
731 self.player.set_state(gst.STATE_PLAYING)
732 self.is_playing = True
735 if self.player is not None:
736 self.player.set_state(gst.STATE_NULL)
738 def load_config(self):
739 self.enable = self.config.get_alarm_enable()
740 self.set_alarm_file(self.config.get_alarm_fname())
741 self.set_interval(self.config.get_alarm_interval())
742 self.set_type(self.config.get_alarm_type())
744 def set_enable(self, value):
748 self.pedo_controller.add_observer(self.update)
749 self.start_value = self.pedo_controller.get_first()
753 self.pedo_controller.remove_observer(self.update)
755 def get_enable(self):
758 def set_alarm_file(self, fname):
761 def get_alarm_file(self):
762 if self.fname == None:
766 def set_interval(self, interval):
767 self.interval = interval
769 def get_interval(self):
772 def set_type(self, type):
778 class PedoCounter(Singleton):
779 COORD_FNAME = "/sys/class/i2c-adapter/i2c-3/3-001d/coord"
780 COORD_FNAME_SDK = "/home/andrei/pedometer-widget-0.1/date.txt"
781 LOGFILE = "/home/user/log_pedometer"
782 #time in ms between two accelerometer data reads
783 COORD_GET_INTERVAL = 25
787 interval_counter = None
788 stop_requested = False
789 update_function = None
793 def __init__(self, update_function=None):
794 if not os.path.exists(self.COORD_FNAME):
795 self.COORD_FNAME = self.COORD_FNAME_SDK
797 self.interval_counter = PedoIntervalCounter()
798 self.update_function = update_function
800 def set_logging(self, value):
803 def get_rotation(self):
804 f = open(self.COORD_FNAME, 'r')
805 coords = [int(w) for w in f.readline().split()]
810 logger.info("Counter started")
811 self.isRunning = True
812 self.stop_requested = False
814 fname = "%d_%d_%d_%d_%d_%d" % time.localtime()[0:6]
815 self.file = open(self.LOGFILE + fname + ".txt", "w")
816 gobject.idle_add(self.run)
819 self.coords = [[], [], []]
820 self.stime = time.time()
822 gobject.timeout_add(self.COORD_GET_INTERVAL, self.read_coords)
825 def read_coords(self):
826 x, y, z = self.get_rotation()
827 self.coords[0].append(int(x))
828 self.coords[1].append(int(y))
829 self.coords[2].append(int(z))
830 now = time.time() - self.stime
832 self.file.write("%d %d %d %f\n" % (self.coords[0][-1], self.coords[1][-1], self.coords[2][-1], now))
837 if self.t[-1] > self.COUNT_INTERVAL or self.stop_requested:
839 gobject.idle_add(self.stop_interval)
842 def stop_interval(self):
843 self.interval_counter.set_vals(self.coords, self.t)
844 cnt = self.interval_counter.number_steps()
846 logger.info("Number of steps detected for last interval %d, number of coords: %d" % (cnt, len(self.t)))
848 gobject.idle_add(self.update_function, cnt, self.stop_requested)
850 if self.stop_requested:
851 gobject.idle_add(self.stop)
853 gobject.idle_add(self.run)
859 logger.info("Counter has finished")
861 def request_stop(self):
862 self.stop_requested = True
863 self.isRunning = False
865 class CustomButton(hildon.Button):
866 def __init__(self, icon):
867 hildon.Button.__init__(self, gtk.HILDON_SIZE_AUTO_WIDTH, hildon.BUTTON_ARRANGEMENT_VERTICAL)
869 self.set_size_request(int(32 * 1.4), int(30 * 1.0))
870 self.retval = self.connect("expose_event", self.expose)
872 def set_icon(self, icon):
875 def expose(self, widget, event):
876 self.context = widget.window.cairo_create()
877 self.context.rectangle(event.area.x, event.area.y,
878 event.area.width, event.area.height)
881 rect = self.get_allocation()
882 self.context.rectangle(rect.x, rect.y, rect.width, rect.height)
883 self.context.set_source_rgba(1, 1, 1, 0)
885 style = self.rc_get_style()
886 color = style.lookup_color("DefaultBackgroundColor")
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);
893 #img = cairo.ImageSurface.create_from_png(self.icon)
895 #self.context.set_source_surface(img)
896 #self.context.set_source_surface(img, rect.width/2 - img.get_width() /2, 0)
898 img.set_from_file(self.icon)
899 buf = img.get_pixbuf()
900 buf = buf.scale_simple(int(32 * 1.5), int(30 * 1.5), gtk.gdk.INTERP_BILINEAR)
902 self.context.set_source_pixbuf(buf, rect.x + (event.area.width / 2 - 15) - 8, rect.y + 1)
903 self.context.scale(200, 200)
908 class CustomEventBox(gtk.EventBox):
911 gtk.EventBox.__init__(self)
913 def do_expose_event(self, event):
914 self.context = self.window.cairo_create()
915 self.context.rectangle(event.area.x, event.area.y,
916 event.area.width, event.area.height)
919 rect = self.get_allocation()
920 self.context.rectangle(rect.x, rect.y, rect.width, rect.height)
922 if self.state == gtk.STATE_ACTIVE:
923 style = self.rc_get_style()
924 color = style.lookup_color("SelectionColor")
925 self.context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
927 self.context.set_source_rgba(1, 1, 1, 0)
930 gtk.EventBox.do_expose_event(self, event)
932 class GraphController(Singleton):
933 ytitles = ["Steps", "Average Speed", "Distance", "Calories"]
934 xtitles = ["Day", "Week"] # "Today"]
940 self.repository = PedoRepositoryXML()
942 PedoController().add_observer(self.update_ui)
943 self.config = Config()
944 self.config.add_observer(self.load_config)
946 def load_config(self):
947 self.set_current_view(self.config.get_graphview())
949 def set_graph(self, widget):
953 def set_current_view(self, view):
955 current_view % len(ytitles) - gives the ytitle
956 current_view / len(ytitles) - gives the xtitle
958 self.x_id = view / len(self.ytitles)
959 self.y_id = view % len(self.ytitles)
963 current_view = self.config.get_graphview() + 1
964 if current_view == len(self.ytitles) * len(self.xtitles):
966 self.config.set_graphview(current_view)
968 def last_weeks_labels(self):
970 delta = timedelta(days=7)
973 ret.append(d.strftime("Week %W"))
977 def compute_values(self):
980 values = self.repository.get_last_7_days()
982 delta = timedelta(days=1)
984 labels.append(d.ctime().split()[0])
988 values = self.repository.get_last_weeks()
991 labels.append(d.strftime("Week %W"))
992 d = d - timedelta(days=7)
994 values = self.repository.get_today()
998 yvalues = [line.steps for line in values]
1000 yvalues = [line.get_avg_speed() for line in values]
1001 elif self.y_id == 2:
1002 yvalues = [line.dist for line in values]
1004 yvalues = [line.calories for line in values]
1006 #determine values for y lines in graph
1007 diff = self.get_best_interval_value(max(yvalues))
1010 ytext.append(str(int(i*diff)))
1012 if self.widget is not None:
1015 self.widget.values = yvalues
1016 self.widget.ytext = ytext
1017 self.widget.xtext = labels
1018 self.widget.max_value = diff * 5
1019 self.widget.text = self.xtitles[self.x_id] + " / " + self.ytitles[self.y_id]
1020 self.widget.queue_draw()
1022 logger.error("Widget not set in GraphController")
1024 def get_best_interval_value(self, max_value):
1025 diff = 1.0 * max_value / 5
1026 l = len(str(int(diff)))
1027 d = math.pow(10, l/2)
1028 val = int(math.ceil(1.0 * diff / d)) * d
1033 def update_ui(self, optional=False):
1034 """update graph values every x seconds"""
1035 if optional and self.last_update - time.time() < 600:
1037 if self.widget is None:
1040 self.compute_values()
1041 self.last_update = time.time()
1043 class GraphWidget(gtk.DrawingArea):
1046 gtk.DrawingArea.__init__(self)
1047 self.set_size_request(-1, 150)
1051 self.ytext = [" 0", "1000", "2000", "3000", "4000", "5000"]
1052 self.xtext = ["Monday", "Tuesday", "Wednesday","Thursday", "Friday", "Saturday", "Sunday"]
1053 self.values = [1500, 3400, 4000, 3600, 3200, 0, 4500]
1054 self.max_value = 5000
1055 self.text = "All time steps"
1057 def do_expose_event(self, event):
1058 context = self.window.cairo_create()
1060 # set a clip region for the expose event
1061 context.rectangle(event.area.x, event.area.y,
1062 event.area.width, event.area.height)
1067 context.set_operator(cairo.OPERATOR_SOURCE)
1068 style = self.rc_get_style()
1070 if self.state == gtk.STATE_ACTIVE:
1071 color = style.lookup_color("SelectionColor")
1073 color = style.lookup_color("DefaultBackgroundColor")
1074 context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75)
1086 rect = self.get_allocation()
1090 cr.select_font_face("Purisa", cairo.FONT_SLANT_NORMAL,
1091 cairo.FONT_WEIGHT_NORMAL)
1092 cr.set_font_size(13)
1094 #check space needed to display ylabels
1095 te = cr.text_extents(self.ytext[-1])
1096 border_left = te[2] + 7
1098 cr.set_source_rgb(1, 1, 1)
1099 cr.move_to(border_left, space_above)
1100 cr.line_to(border_left, y-space_below)
1101 cr.set_line_width(2)
1104 cr.move_to(border_left, y-space_below)
1105 cr.line_to(x-border_right, y-space_below)
1106 cr.set_line_width(2)
1109 ydiff = (y-space_above-space_below) / self.yvalues
1110 for i in range(self.yvalues):
1111 yy = y-space_below-ydiff*(i+1)
1112 cr.move_to(border_left, yy)
1113 cr.line_to(x-border_right, yy)
1114 cr.set_line_width(0.8)
1119 yy = y - space_below - ydiff*i + 5
1120 te = cr.text_extents(self.ytext[i])
1122 cr.move_to(border_left-te[2]-2, yy)
1123 cr.show_text(self.ytext[i])
1125 cr.set_font_size(15)
1126 te = cr.text_extents(self.text)
1127 cr.move_to((x-te[2])/2, y-5)
1128 cr.show_text(self.text)
1130 graph_x_space = x - border_left - border_right
1131 graph_y_space = y - space_below - space_above
1132 bar_width = graph_x_space*0.75 / len(self.values)
1133 bar_distance = graph_x_space*0.25 / (1+len(self.values))
1135 #set dummy max value to avoid exceptions
1136 if self.max_value == 0:
1137 self.max_value = 100
1138 for i in range(len(self.values)):
1139 xx = border_left + (i+1)*bar_distance + i * bar_width
1141 height = graph_y_space * (1.0 * self.values[i] / self.max_value)
1142 cr.set_source_rgba(1, 1, 1, 0.75)
1143 cr.rectangle(int(xx), int(yy-height), int(bar_width), int(height))
1146 cr.set_source_rgba(1, 1, 1, 1)
1147 cr.select_font_face("Purisa", cairo.FONT_SLANT_NORMAL,
1148 cairo.FONT_WEIGHT_NORMAL)
1149 cr.set_font_size(13)
1151 cr.rotate(2*math.pi * (-45) / 180)
1152 for i in range(len(self.values)):
1153 xx = y - space_below - 10
1154 yy = border_left + (i+1)*bar_distance + i * bar_width
1155 cr.move_to(-xx, yy + bar_width*1.25 / 2)
1156 cr.show_text(self.xtext[i])
1158 class Config(Singleton):
1169 no_idle_time = False
1172 alarm_enable = False
1173 alarm_fname = "/home/user/MyDocs/.sounds/Ringtones/Bicycle.aac"
1180 if self._references > 1:
1182 self.client = gconf.client_get_default()
1183 self.client.add_dir('/apps/pedometerhomewidget', gconf.CLIENT_PRELOAD_RECURSIVE)
1184 self.notify_id = self.client.notify_add('/apps/pedometerhomewidget', self.gconf_changed)
1186 def add_observer(self, func):
1188 self.observers.index(func)
1190 self.observers.append(func)
1193 def remove_observer(self, func):
1194 self.observers.remove(func)
1196 def gconf_changed(self, client, *args, **kargs):
1201 for func in self.observers:
1204 logger.info("Update took: %f seconds" % (t2-t1))
1207 return self.client.get_int(MODE)
1209 def set_mode(self, value):
1210 self.client.set_int(MODE, value)
1212 def get_height(self):
1213 return self.client.get_int(HEIGHT)
1215 def set_height(self, value):
1216 self.client.set_int(HEIGHT, value)
1218 def get_step_length(self):
1219 return self.client.get_float(STEP_LENGTH)
1221 def set_step_length(self, value):
1222 self.client.set_float(STEP_LENGTH, value)
1224 def get_weight(self):
1225 return self.client.get_int(WEIGHT)
1227 def set_weight(self, value):
1228 self.client.set_int(WEIGHT, value)
1230 def get_sensitivity(self):
1231 return self.client.get_int(SENSITIVITY)
1233 def set_sensitivity(self, value):
1234 self.client.set_int(SENSITIVITY, value)
1237 return self.client.get_int(UNIT)
1239 def set_unit(self, value):
1240 self.client.set_int(UNIT, value)
1242 def get_aspect(self):
1243 return self.client.get_int(ASPECT)
1245 def set_aspect(self, value):
1246 self.client.set_int(ASPECT, value)
1248 def get_secondview(self):
1249 value = self.client.get_int(SECONDVIEW)
1250 if value < 0 or value > 2:
1252 logger.error("Invalid secondview value read from Gconf. Using default value")
1256 def set_secondview(self, value):
1257 self.client.set_int(SECONDVIEW, value)
1259 def get_graphview(self):
1260 return self.client.get_int(GRAPHVIEW)
1262 def set_graphview(self, value):
1263 self.client.set_int(GRAPHVIEW, value)
1265 def get_noidletime(self):
1266 return self.client.get_bool(NOIDLETIME)
1268 def set_noidletime(self, value):
1269 self.client.set_bool(NOIDLETIME, value)
1271 def get_logging(self):
1272 return self.client.get_bool(LOGGING)
1274 def set_logging(self, value):
1275 self.client.set_bool(LOGGING, value)
1277 def get_alarm_enable(self):
1278 return self.client.get_bool(ALARM_ENABLE)
1280 def set_alarm_enable(self, value):
1281 self.client.set_bool(ALARM_ENABLE, value)
1283 def get_alarm_fname(self):
1284 return self.client.get_string(ALARM_FNAME)
1286 def set_alarm_fname(self, value):
1287 self.client.set_string(ALARM_FNAME, value)
1289 def get_alarm_interval(self):
1290 return self.client.get_int(ALARM_INTERVAL)
1292 def set_alarrm_interval(self, value):
1293 self.client.set_int(ALARM_INTERVAL, value)
1295 def get_alarm_type(self):
1296 return self.client.get_int(ALARM_TYPE)
1298 def set_alarm_type(self, value):
1299 self.client.set_int(ALARM_TYPE, value)
1301 class PedometerHomePlugin(hildondesktop.HomePluginItem):
1305 labels = ["timer", "count", "dist", "avgSpeed", "calories"]
1310 #second view ( day / week/ alltime)
1313 second_view_labels = ["All-time", "Today", "This week"]
1316 graph_controller = None
1321 hildondesktop.HomePluginItem.__init__(self)
1323 gobject.type_register(CustomEventBox)
1324 gobject.type_register(GraphWidget)
1326 self.config = Config()
1328 self.button = CustomButton(ICONSPATH + "play.png")
1329 self.button.connect("clicked", self.button_clicked)
1331 self.create_labels(self.labelsC)
1332 self.create_labels(self.labelsT)
1333 self.label_second_view = self.new_label_heading(self.second_view_labels[self.config.get_secondview()])
1335 self.controller = PedoController()
1336 self.controller.set_callback_ui(self.update_values)
1338 self.graph_controller = GraphController()
1339 self.alarm_controller = AlarmController()
1341 self.update_current()
1344 mainHBox = gtk.HBox(spacing=1)
1346 descVBox = gtk.VBox(spacing=1)
1347 descVBox.add(self.new_label_heading())
1348 descVBox.add(self.new_label_heading("Time:"))
1349 descVBox.add(self.new_label_heading("Steps:"))
1350 descVBox.add(self.new_label_heading("Calories:"))
1351 descVBox.add(self.new_label_heading("Distance:"))
1352 descVBox.add(self.new_label_heading("Avg Speed:"))
1354 currentVBox = gtk.VBox(spacing=1)
1355 currentVBox.add(self.new_label_heading("Current"))
1356 currentVBox.add(self.labelsC["timer"])
1357 currentVBox.add(self.labelsC["count"])
1358 currentVBox.add(self.labelsC["calories"])
1359 currentVBox.add(self.labelsC["dist"])
1360 currentVBox.add(self.labelsC["avgSpeed"])
1361 self.currentBox = currentVBox
1363 totalVBox = gtk.VBox(spacing=1)
1364 totalVBox.add(self.label_second_view)
1365 totalVBox.add(self.labelsT["timer"])
1366 totalVBox.add(self.labelsT["count"])
1367 totalVBox.add(self.labelsT["calories"])
1368 totalVBox.add(self.labelsT["dist"])
1369 totalVBox.add(self.labelsT["avgSpeed"])
1370 self.totalBox = totalVBox
1372 buttonVBox = gtk.VBox(spacing=1)
1373 buttonVBox.add(self.new_label_heading(""))
1374 buttonVBox.add(self.button)
1375 buttonVBox.add(self.new_label_heading(""))
1377 eventBox = CustomEventBox()
1378 eventBox.set_visible_window(False)
1379 eventBox.add(totalVBox)
1380 eventBox.connect("button-press-event", self.eventBox_clicked)
1381 eventBox.connect("button-release-event", self.eventBox_clicked_release)
1383 mainHBox.add(buttonVBox)
1384 mainHBox.add(descVBox)
1385 mainHBox.add(currentVBox)
1386 mainHBox.add(eventBox)
1387 self.mainhbox = mainHBox
1389 graph = GraphWidget()
1390 self.graph_controller.set_graph(graph)
1392 eventBoxGraph = CustomEventBox()
1393 eventBoxGraph.set_visible_window(False)
1394 eventBoxGraph.add(graph)
1396 eventBoxGraph.connect("button-press-event", self.eventBoxGraph_clicked)
1397 eventBoxGraph.connect("button-release-event", self.eventBoxGraph_clicked_release)
1398 self.graphBox = eventBoxGraph
1400 self.mainvbox = gtk.VBox()
1402 self.mainvbox.add(mainHBox)
1403 self.mainvbox.add(eventBoxGraph)
1405 self.mainvbox.show_all()
1406 self.add(self.mainvbox)
1408 self.connect("unrealize", self.close_requested)
1409 self.set_settings(True)
1410 self.connect("show-settings", self.show_settings)
1412 self.config.add_observer(self.update_aspect)
1414 def eventBoxGraph_clicked(self, widget, data=None):
1415 widget.set_state(gtk.STATE_ACTIVE)
1417 def eventBoxGraph_clicked_release(self, widget, data=None):
1418 self.graph_controller.next_view()
1419 widget.set_state(gtk.STATE_NORMAL)
1421 def eventBox_clicked(self, widget, data=None):
1422 widget.set_state(gtk.STATE_ACTIVE)
1424 def eventBox_clicked_release(self, widget, data=None):
1425 widget.set_state(gtk.STATE_NORMAL)
1427 second_view = self.config.get_secondview()
1428 second_view = (second_view + 1) % 3
1429 self.config.set_secondview(second_view)
1431 def new_label_heading(self, title=""):
1432 l = gtk.Label(title)
1433 hildon.hildon_helper_set_logical_font(l, "SmallSystemFont")
1436 def create_labels(self, new_labels):
1437 for label in self.labels:
1439 hildon.hildon_helper_set_logical_font(l, "SmallSystemFont")
1440 hildon.hildon_helper_set_logical_color(l, gtk.RC_FG, gtk.STATE_NORMAL, "ActiveTextColor")
1441 new_labels[label] = l
1443 def update_aspect(self):
1444 aspect = self.config.get_aspect()
1446 self.graphBox.hide_all()
1448 self.graphBox.show_all()
1450 if aspect == 0 or aspect == 1:
1451 self.currentBox.show_all()
1452 self.totalBox.show_all()
1454 self.currentBox.show_all()
1455 self.totalBox.hide_all()
1457 self.currentBox.hide_all()
1458 self.totalBox.show_all()
1460 x,y = self.size_request()
1463 def update_ui_values(self, labels, values):
1464 labels["timer"].set_label(values.get_print_time())
1465 labels["count"].set_label(values.get_print_steps())
1466 labels["dist"].set_label(values.get_print_distance())
1467 labels["avgSpeed"].set_label(values.get_print_avg_speed())
1468 labels["calories"].set_label(values.get_print_calories())
1470 def update_current(self):
1471 self.update_ui_values(self.labelsC, self.controller.get_first())
1473 def update_total(self):
1474 self.update_ui_values(self.labelsT, self.controller.get_second())
1476 def show_alarm_settings(self, main_button):
1477 def choose_file(widget):
1478 file = hildon.FileChooserDialog(self, gtk.FILE_CHOOSER_ACTION_OPEN, hildon.FileSystemModel() )
1480 if ( file.run() == gtk.RESPONSE_OK):
1481 fname = file.get_filename()
1482 widget.set_value(fname)
1483 self.config.set_alarm_fname(fname)
1486 def test_sound(button):
1488 self.alarm_controller.play()
1489 except Exception, e:
1490 logger.error("Could not play alarm sound: %s" % e)
1491 hildon.hildon_banner_show_information(self, "None", "Could not play alarm sound")
1493 def enableButton_changed(button):
1494 value = button.get_active()
1495 self.config.set_alarm_enable(value)
1497 main_button.set_value("Enabled")
1499 main_button.set_value("Disabled")
1501 def selectorType_changed(selector, data, labelEntry2):
1502 type = selector.get_active(0)
1503 self.config.set_alarm_type(type)
1504 labelEntry2.set_label(suffix[type])
1506 dialog = gtk.Dialog()
1507 dialog.set_title("Alarm settings")
1508 dialog.add_button("OK", gtk.RESPONSE_OK)
1510 enableButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1511 enableButton.set_label("Enable alarm")
1512 enableButton.set_active(self.alarm_controller.get_enable())
1513 enableButton.connect("toggled", enableButton_changed)
1515 testButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1516 testButton.set_alignment(0, 0.8, 1, 1)
1517 testButton.set_title("Test sound")
1518 testButton.connect("pressed", test_sound)
1520 fileButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1521 fileButton.set_alignment(0, 0.8, 1, 1)
1522 fileButton.set_title("Alarm sound")
1523 fileButton.set_value(self.alarm_controller.get_alarm_file())
1524 fileButton.connect("pressed", choose_file)
1526 labelEntry = gtk.Label("Notify every:")
1527 suffix = ["mins", "steps", "m/ft", "calories"]
1528 labelEntry2 = gtk.Label(suffix[self.alarm_controller.get_type()])
1529 intervalEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO_WIDTH)
1530 intervalEntry.set_text(str(self.alarm_controller.get_interval()))
1532 selectorType = hildon.TouchSelector(text=True)
1533 selectorType.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1534 selectorType.append_text("Time")
1535 selectorType.append_text("Steps")
1536 selectorType.append_text("Distance")
1537 selectorType.append_text("Calories")
1538 selectorType.connect("changed", selectorType_changed, labelEntry2)
1540 typePicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1541 typePicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1542 typePicker.set_title("Alarm type")
1543 typePicker.set_selector(selectorType)
1544 typePicker.set_active(self.alarm_controller.get_type())
1547 hbox.add(labelEntry)
1548 hbox.add(intervalEntry)
1549 hbox.add(labelEntry2)
1551 dialog.vbox.add(enableButton)
1552 dialog.vbox.add(fileButton)
1553 dialog.vbox.add(testButton)
1554 dialog.vbox.add(typePicker)
1555 dialog.vbox.add(hbox)
1558 response = dialog.run()
1559 if response != gtk.RESPONSE_OK:
1562 value = int(intervalEntry.get_text())
1563 self.config.set_alarrm_interval(value)
1566 hildon.hildon_banner_show_information(self, "None", "Invalid interval")
1570 def show_settings(self, widget):
1571 def reset_total_counter(arg):
1572 note = hildon.hildon_note_new_confirmation(self.dialog, "Are you sure you want to delete all your pedometer history?")
1574 if ret == gtk.RESPONSE_OK:
1575 self.controller.reset_all_values()
1576 hildon.hildon_banner_show_information(self, "None", "All history was deleted")
1579 def alarmButton_pressed(widget):
1580 self.show_alarm_settings(widget)
1582 def selector_changed(selector, data):
1583 mode = selector.get_active(0)
1584 self.config.set_mode(mode)
1586 def selectorUnit_changed(selector, data):
1587 unit = selector.get_active(0)
1588 self.config.set_unit(unit)
1590 update_weight_button()
1591 stepLengthButton_value_update()
1593 def selectorUI_changed(selector, data):
1594 aspect = selectorUI.get_active(0)
1595 self.config.set_aspect(aspect)
1597 def logButton_changed(checkButton):
1598 logging = checkButton.get_active()
1599 self.config.set_logging(logging)
1601 def idleButton_changed(idleButton):
1602 no_idle_time = idleButton.get_active()
1603 self.config.set_noidletime(no_idle_time)
1605 def update_weight_button():
1606 weightButton.set_value(str(self.config.get_weight()) + \
1607 " " + self.controller.get_str_weight_unit(self.config.get_unit()) )
1609 def weight_dialog(button):
1610 dialog = gtk.Dialog("Weight", self.dialog)
1611 dialog.add_button("OK", gtk.RESPONSE_OK)
1613 label = gtk.Label("Weight:")
1615 entry.set_text(str(self.config.get_weight()))
1617 suffixLabel = gtk.Label(self.controller.get_str_weight_unit(self.config.get_unit()))
1622 hbox.add(suffixLabel)
1624 dialog.vbox.add(hbox)
1627 response = dialog.run()
1628 if response != gtk.RESPONSE_OK:
1631 value = int(entry.get_text())
1634 self.config.set_weight(value)
1635 update_weight_button()
1638 hildon.hildon_banner_show_information(self, "None", "Invalid weight")
1641 def sensitivity_dialog(button):
1642 def seekbar_changed(seekbar):
1643 label.set_text(str(seekbar.get_position()) + " %")
1645 dialog = gtk.Dialog("Sensitivity", self.dialog)
1646 dialog.add_button("OK", gtk.RESPONSE_OK)
1647 seekbar = hildon.Seekbar()
1648 seekbar.set_size_request(400, -1)
1649 seekbar.set_total_time(200)
1650 seekbar.set_position(self.config.get_sensitivity())
1651 seekbar.connect("value-changed", seekbar_changed)
1655 label = gtk.Label(str(self.config.get_sensitivity()) + " %")
1656 label.set_size_request(30, -1)
1659 dialog.vbox.add(hbox)
1662 if dialog.run() == gtk.RESPONSE_OK:
1663 value = seekbar.get_position()
1664 self.config.set_sensitivity(value)
1665 button.set_value(str(value) + " %")
1669 def stepLengthButton_value_update():
1670 if self.config.get_height() == 5:
1671 l_unit = ["m", "ft"]
1672 stepLengthButton.set_value("Custom value: %.2f %s" % (self.config.get_step_length(), l_unit[self.config.get_unit()]))
1674 h = [ ["< 1.50 m", "1.50 - 1.65 m", "1.66 - 1.80 m", "1.81 - 1.95 m", " > 1.95 m"],
1675 ["< 5 ft", "5 - 5.5 ft", "5.5 - 6 ft", "6 - 6.5 ft", "> 6.5 ft"]]
1676 str = "Using predefined value for height: %s" % h[self.config.get_unit()][self.config.get_height()]
1677 stepLengthButton.set_value(str)
1679 def stepLength_dialog(button):
1680 def selectorH_changed(selector, data, dialog):
1681 height = selector.get_active(0)
1682 self.config.set_height(height)
1683 stepLengthButton_value_update()
1685 def manualButton_clicked(button, dialog):
1687 dlg.set_title("Custom step length")
1688 dlg.add_button("OK", gtk.RESPONSE_OK)
1690 label = gtk.Label("Length")
1692 entry = hildon.Entry(gtk.HILDON_SIZE_AUTO_WIDTH)
1693 if self.config.get_height() == 5:
1694 entry.set_text(str(self.config.get_step_length()))
1696 labelSuffix = gtk.Label()
1697 if self.config.get_unit() == 0:
1698 labelSuffix.set_label("m")
1700 labelSuffix.set_label("ft")
1704 hbox.add(labelSuffix)
1709 response = dlg.run()
1710 if response != gtk.RESPONSE_OK:
1713 value = float(entry.get_text())
1716 self.config.set_step_length(value)
1717 self.config.set_height(5)
1718 stepLengthButton_value_update()
1721 hildon.hildon_banner_show_information(self, "None", "Invalid length")
1725 def heightButton_clicked(button, dialog):
1728 dialog = gtk.Dialog()
1729 dialog.set_title("Step length")
1731 manualButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1732 manualButton.set_title("Enter custom value")
1733 manualButton.set_alignment(0, 0.8, 1, 1)
1734 manualButton.connect("clicked", manualButton_clicked, dialog)
1736 selectorH = hildon.TouchSelector(text=True)
1737 selectorH.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1738 selectorH.append_text("< 1.50 m")
1739 selectorH.append_text("1.50 - 1.65 m")
1740 selectorH.append_text("1.66 - 1.80 m")
1741 selectorH.append_text("1.81 - 1.95 m")
1742 selectorH.append_text(" > 1.95 m")
1744 selectorH_English = hildon.TouchSelector(text=True)
1745 selectorH_English.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1746 selectorH_English.append_text("< 5 ft")
1747 selectorH_English.append_text("5 - 5.5 ft")
1748 selectorH_English.append_text("5.5 - 6 ft")
1749 selectorH_English.append_text("6 - 6.5 ft")
1750 selectorH_English.append_text("> 6.5 ft")
1752 heightPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1753 heightPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1754 heightPicker.set_title("Use predefined values for height")
1757 unit = self.config.get_unit()
1759 heightPicker.set_selector(selectorH)
1761 heightPicker.set_selector(selectorH_English)
1763 height = self.config.get_height()
1765 heightPicker.set_active(height)
1767 heightPicker.get_selector().connect("changed", selectorH_changed, dialog)
1768 heightPicker.connect("value-changed", heightButton_clicked, dialog)
1770 dialog.vbox.add(heightPicker)
1771 dialog.vbox.add(manualButton)
1774 if dialog.run() == gtk.RESPONSE_DELETE_EVENT:
1777 def donateButton_clicked(button, dialog):
1778 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"
1779 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
1782 dialog = gtk.Dialog()
1783 dialog.set_title("Settings")
1784 dialog.add_button("OK", gtk.RESPONSE_OK)
1785 self.dialog = dialog
1787 stepLengthButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1788 stepLengthButton.set_title("Step length")
1789 stepLengthButton.set_alignment(0, 0.8, 1, 1)
1790 stepLengthButton.connect("clicked", stepLength_dialog)
1791 stepLengthButton_value_update()
1793 resetButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1794 resetButton.set_title("Reset")
1795 resetButton.set_value("All the stored values will be erased")
1796 resetButton.set_alignment(0, 0.8, 1, 1)
1797 resetButton.connect("clicked", reset_total_counter)
1799 alarmButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1800 alarmButton.set_title("Alarm")
1801 if self.config.get_alarm_enable():
1802 alarmButton.set_value("Enabled")
1804 alarmButton.set_value("Disabled")
1805 alarmButton.set_alignment(0, 0.8, 1, 1)
1806 alarmButton.connect("clicked", alarmButton_pressed)
1808 selector = hildon.TouchSelector(text=True)
1809 selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1810 selector.append_text("Walk")
1811 selector.append_text("Run")
1812 selector.connect("changed", selector_changed)
1814 modePicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1815 modePicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1816 modePicker.set_title("Mode")
1817 modePicker.set_selector(selector)
1818 modePicker.set_active(self.config.get_mode())
1820 weightButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1821 weightButton.set_title("Weight")
1822 weightButton.set_alignment(0, 0.8, 1, 1)
1823 update_weight_button()
1824 weightButton.connect("clicked", weight_dialog)
1826 selectorUnit = hildon.TouchSelector(text=True)
1827 selectorUnit.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1828 selectorUnit.append_text("Metric (km)")
1829 selectorUnit.append_text("English (mi)")
1830 selectorUnit.connect("changed", selectorUnit_changed)
1832 unitPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1833 unitPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1834 unitPicker.set_title("Unit")
1835 unitPicker.set_selector(selectorUnit)
1836 unitPicker.set_active(self.config.get_unit())
1838 selectorUI = hildon.TouchSelector(text=True)
1839 selectorUI = hildon.TouchSelector(text=True)
1840 selectorUI.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1841 selectorUI.append_text("Show current + total + graph")
1842 selectorUI.append_text("Show current + total")
1843 selectorUI.append_text("Show only current")
1844 selectorUI.append_text("Show only total")
1845 selectorUI.connect("changed", selectorUI_changed)
1847 UIPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1848 UIPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1849 UIPicker.set_title("Widget aspect")
1850 UIPicker.set_selector(selectorUI)
1851 UIPicker.set_active(self.config.get_aspect())
1853 sensitivityButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1854 sensitivityButton.set_title("Sensitivity")
1855 sensitivityButton.set_alignment(0, 0.8, 1, 1)
1856 sensitivityButton.set_value(str(self.config.get_sensitivity()) + " %")
1857 sensitivityButton.connect("clicked", sensitivity_dialog)
1859 donateButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1860 donateButton.set_title("Donate")
1861 donateButton.set_value("Please support the development of this opensource widget!")
1862 donateButton.set_alignment(0, 0.8, 1, 1)
1863 donateButton.connect("clicked", donateButton_clicked, dialog)
1865 logButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1866 logButton.set_label("Log data")
1867 logButton.set_active(self.config.get_logging())
1868 logButton.connect("toggled", logButton_changed)
1870 idleButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1871 idleButton.set_label("Pause time when not walking")
1872 idleButton.set_active(self.config.get_noidletime())
1873 idleButton.connect("toggled", idleButton_changed)
1875 pan_area = hildon.PannableArea()
1877 vbox.add(alarmButton)
1878 vbox.add(modePicker)
1879 vbox.add(stepLengthButton)
1880 vbox.add(weightButton)
1881 vbox.add(unitPicker)
1882 vbox.add(sensitivityButton)
1884 vbox.add(idleButton)
1885 vbox.add(resetButton)
1886 vbox.add(donateButton)
1887 #vbox.add(logButton)
1889 pan_area.add_with_viewport(vbox)
1890 pan_area.set_size_request(-1, 300)
1892 dialog.vbox.add(pan_area)
1895 response = dialog.run()
1898 def close_requested(self, widget):
1899 if self.controller.is_running:
1900 self.controller.stop_pedometer()
1901 self.controller.stop_midnight_callback()
1903 def update_values(self):
1904 #TODO: do not update if the widget is not on the active desktop
1905 self.label_second_view.set_label(self.second_view_labels[self.config.get_secondview()])
1906 self.update_current()
1909 def button_clicked(self, button):
1910 if self.controller.is_running:
1911 self.controller.stop_pedometer()
1912 self.button.set_icon(ICONSPATH + "play.png")
1914 self.controller.start_pedometer()
1915 self.button.set_icon(ICONSPATH + "stop.png")
1916 hildon.hildon_banner_show_information(self, "None", "Keep the N900 in a pocket close to your hip for best results")
1918 def do_expose_event(self, event):
1919 cr = self.window.cairo_create()
1920 cr.region(event.window.get_clip_region())
1922 #cr.set_source_rgba(0.4, 0.64, 0.564, 0.5)
1923 style = self.rc_get_style()
1924 color = style.lookup_color("DefaultBackgroundColor")
1925 cr.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
1928 width = self.allocation.width
1929 height = self.allocation.height
1931 x = self.allocation.x
1932 y = self.allocation.y
1934 cr.move_to(x + radius, y)
1935 cr.line_to(x + width - radius, y)
1936 cr.curve_to(x + width - radius, y, x + width, y, x + width, y + radius)
1937 cr.line_to(x + width, y + height - radius)
1938 cr.curve_to(x + width, y + height - radius, x + width, y + height, x + width - radius, y + height)
1939 cr.line_to(x + radius, y + height)
1940 cr.curve_to(x + radius, y + height, x, y + height, x, y + height - radius)
1941 cr.line_to(x, y + radius)
1942 cr.curve_to(x, y + radius, x, y, x + radius, y)
1944 cr.set_operator(cairo.OPERATOR_SOURCE)
1947 color = style.lookup_color("ActiveTextColor")
1948 cr.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.5);
1949 cr.set_line_width(1)
1952 hildondesktop.HomePluginItem.do_expose_event(self, event)
1954 def do_realize(self):
1955 screen = self.get_screen()
1956 self.set_colormap(screen.get_rgba_colormap())
1957 self.set_app_paintable(True)
1958 hildondesktop.HomePluginItem.do_realize(self)
1960 hd_plugin_type = PedometerHomePlugin
1965 logger = logging.getLogger("pedometer")
1966 logger.setLevel(logging.INFO)
1968 ch = logging.StreamHandler()
1969 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
1970 ch.setFormatter(formatter)
1971 logger.addHandler(ch)
1973 # The code below is just for testing purposes.
1974 # It allows to run the widget as a standalone process.
1975 if __name__ == "__main__":
1977 gobject.type_register(hd_plugin_type)
1978 obj = gobject.new(hd_plugin_type, plugin_id="plugin_id")