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
37 APP_NAME = "pedometer"
39 PATH = "/apps/pedometerhomewidget"
41 HEIGHT = PATH + "/height"
42 STEP_LENGTH = PATH + "/step_length"
43 WEIGHT = PATH + "/weight"
45 SENSITIVITY = PATH + "/sensitivity"
46 ASPECT = PATH + "/aspect"
47 SECONDVIEW = PATH + "/secondview"
48 GRAPHVIEW = PATH + "/graphview"
49 NOIDLETIME = PATH + "/noidletime"
50 LOGGING = PATH + "/logging"
52 ALARM_PATH = PATH + "/alarm"
53 ALARM_ENABLE = ALARM_PATH + "/enable"
54 ALARM_FNAME = ALARM_PATH + "/fname"
55 ALARM_TYPE = ALARM_PATH + "/type"
56 ALARM_INTERVAL = ALARM_PATH + "/interval"
58 ICONSPATH = "/opt/pedometerhomewidget/"
62 class Singleton(object):
65 def __new__(cls, *args, **kwargs):
68 cls._instance = super(Singleton, cls).__new__(
72 class Translate(Singleton):
75 if self._references > 1:
77 #Get the local directory since we are not installing anything
78 #self.local_path = os.path.realpath(os.path.dirname(sys.argv[0]))
79 self.local_path = os.path.join(os.path.expanduser("~"), "pedometer-widget-0.1", "locale")
80 # Init the list of languages to support
82 #Check the default locale
83 lc, encoding = locale.getdefaultlocale()
85 #If we have a default, it's the first in the list
87 # Now lets get all of the supported languages on the system
88 language = os.environ.get('LANGUAGE', None)
90 """langage comes back something like en_CA:en_US:en_GB:en
91 on linuxy systems, on Win32 it's nothing, so we need to
92 split it up into a list"""
93 langs += language.split(":")
94 """Now add on to the back of the list the translations that we
95 know that we have, our defaults"""
96 langs += ["en_CA", "en_US", "ro_RO"]
98 """Now langs is a list of all of the languages that we are going
99 to try to use. First we check the default, then what the system
100 told us, and finally the 'known' list"""
102 gettext.bindtextdomain(APP_NAME, self.local_path)
103 print self.local_path
105 gettext.textdomain(APP_NAME)
106 # Get the language to use
107 self.lang = gettext.translation(APP_NAME, self.local_path
108 , languages=langs, fallback = True)
109 """Install the language, map _() (which we marked our
110 strings to translate with) to self.lang.gettext() which will
113 _ = Translate().lang.gettext
115 class PedoIntervalCounter(Singleton):
125 #TODO: check if last detected step is at the end of the interval
127 def set_vals(self, coords, tval):
133 def set_mode(self, mode):
134 #runnig, higher threshold to prevent fake steps
137 self.MIN_THRESHOLD = 650.0 * (200 - self.sensitivity) / 100
138 self.MIN_TIME_STEPS = 0.35
141 self.MIN_THRESHOLD = 500.0 * (200 - self.sensitivity) / 100
142 self.MIN_TIME_STEPS = 0.5
144 def set_sensitivity(self, value):
145 self.sensitivity = value
146 self.set_mode(self.mode)
148 def calc_mean(self, vals):
153 return sum / len(vals)
156 def calc_stdev(self, vals):
158 mean = self.calc_mean(vals)
160 rez += pow(abs(mean - i), 2)
161 return math.sqrt(rez / len(vals))
163 def calc_threshold(self, vals):
166 mean = self.calc_mean(vals)
167 threshold = max (abs(mean - vmax), abs(mean - vmin))
170 def count_steps(self, vals, t):
171 threshold = self.MIN_THRESHOLD
172 mean = self.calc_mean(vals)
176 if abs(vals[i] - mean) > threshold:
178 ntime = t[i] + self.MIN_TIME_STEPS
179 while i < len(vals) and t[i] < ntime:
184 def get_best_values(self, x, y, z):
185 dev1 = self.calc_stdev(x)
186 dev2 = self.calc_stdev(y)
187 dev3 = self.calc_stdev(z)
188 dev_max = max(dev1, dev2, dev3)
190 if (abs(dev1 - dev_max) < 0.001):
191 logger.info("X chosen as best axis, stdev %f" % dev1)
193 elif (abs(dev2 - dev_max) < 0.001):
194 logger.info("Y chosen as best axis, stdev %f" % dev2)
197 logger.info("Z chosen as best axis, stdev %f" % dev3)
200 def number_steps(self):
201 vals = self.get_best_values(self.x, self.y, self.z)
202 return self.count_steps(vals, self.t)
205 def __init__(self, time=0, steps=0, dist=0, calories=0):
208 self.calories = calories
211 def __add__(self, other):
212 return PedoValues(self.time + other.time,
213 self.steps + other.steps,
214 self.dist + other.dist,
215 self.calories + other.calories)
217 def __sub__(self, other):
218 return PedoValues(self.time - other.time,
219 self.steps - other.steps,
220 self.dist - other.dist,
221 self.calories - other.calories)
223 def get_print_time(self):
225 hours = int(tdelta / 3600)
226 tdelta -= 3600 * hours
227 mins = int(tdelta / 60)
230 strtime = "%.2d:%.2d:%.2d" % (hours, mins, secs)
233 def get_print_distance(self):
237 return "%.2f km" % (self.dist / 1000)
239 return "%.2f mi" % (self.dist / 1609.344)
242 return "%d m" % self.dist
244 return "%d ft" % int(self.dist * 3.2808)
246 def get_avg_speed(self):
256 speed = 1.0 * self.dist / self.time
259 def get_print_avg_speed(self):
271 return "N/A " + suffix
272 speed = 1.0 * self.dist / self.time
273 #convert from meters per second to km/h or mi/h
275 return "%.2f %s" % (speed, suffix)
277 def get_print_steps(self):
278 return str(self.steps)
280 def get_print_calories(self):
281 return "%.2f" % self.calories
283 class PedoRepository(Singleton):
287 raise NotImplementedError("Must be implemented by subclass")
290 raise NotImplementedError("Must be implemented by subclass")
292 def reset_values(self):
296 def get_history_count(self):
297 """return the number of days in the log"""
300 def get_values(self):
303 def add_values(self, values, when=None):
306 """add PedoValues values to repository """
308 self.values[when] = self.values[when] + values
310 self.values[when] = values
312 def get_last_7_days(self):
317 ret.append(self.values[day])
319 ret.append(PedoValues())
320 day = day - timedelta(days=1)
323 def get_last_weeks(self):
324 delta = timedelta(days=1)
326 week = int(date.today().strftime("%W"))
331 val += self.values[day]
334 w = int(day.strftime("%W"))
344 def get_alltime_values(self):
346 for k, v in self.values.iteritems():
350 def get_today_values(self):
352 return self.values[date.today()]
356 def get_this_week_values(self):
361 ret += self.values[day]
364 if day.weekday() == 0:
366 day = day - timedelta(days=1)
370 class PedoRepositoryXML(PedoRepository):
371 DIR = os.path.join(os.path.expanduser("~"), ".pedometer")
372 FILE = os.path.join(DIR, "data.xml")
373 FILE2 = os.path.join(DIR, "pickle.log")
375 if not os.path.exists(self.DIR):
376 os.makedirs(self.DIR)
377 PedoRepository.__init__(self)
381 f = open(self.FILE, "r")
382 dom = parseString(f.read())
383 values = dom.getElementsByTagName("pedometer")[0]
384 for v in values.getElementsByTagName("date"):
385 d = int(v.getAttribute("ordinal_day"))
386 steps = int(v.getAttribute("steps"))
387 calories = float(v.getAttribute("calories"))
388 dist = float(v.getAttribute("dist"))
389 time = float(v.getAttribute("time"))
390 day = date.fromordinal(d)
391 self.values[day] = PedoValues(time, steps, dist, calories)
395 logger.error("Error while loading data from xml file: %s" % e)
399 f = open(self.FILE, "w")
401 impl = getDOMImplementation()
403 newdoc = impl.createDocument(None, "pedometer", None)
404 top_element = newdoc.documentElement
405 for k, v in self.values.iteritems():
406 d = newdoc.createElement('date')
407 d.setAttribute("day", str(k.isoformat()))
408 d.setAttribute("ordinal_day", str(k.toordinal()))
409 d.setAttribute("steps", str(v.steps))
410 d.setAttribute("time", str(v.time))
411 d.setAttribute("dist", str(v.dist))
412 d.setAttribute("calories", str(v.calories))
413 top_element.appendChild(d)
415 newdoc.appendChild(top_element)
417 #f.write(newdoc.toprettyxml())
420 logger.error("Error while saving data to xml file: %s" % e)
422 class PedoRepositoryPickle(PedoRepository):
423 DIR = os.path.join(os.path.expanduser("~"), ".pedometer")
424 FILE = os.path.join(DIR, "pickle.log")
427 if not os.path.exists(self.DIR):
428 os.makedirs(self.DIR)
429 PedoRepository.__init__(self)
433 f = open(self.FILE, "rb")
434 self.values = pickle.load(f)
437 logger.error("Error while loading pickle file: %s" % e)
441 f = open(self.FILE, "wb")
442 pickle.dump(self.values, f)
445 logger.error("Error while saving data to pickle: %s" % e)
447 class PedoController(Singleton):
453 #what to display in second view - 0 - alltime, 1 - today, 2 - week
455 callback_update_ui = None
460 #The interval(number of steps) between two file updates
461 BUFFER_STEPS_INTERVAL = 100
462 #values for the two views in the widget ( current and day/week/alltime)
463 #third value to count the steps that were not yet written to file
464 v = [PedoValues(), PedoValues(), PedoValues()]
472 midnight_source_id = None
473 midnight_before_source_id = None
477 self.pedometer = PedoCounter(self.steps_detected)
478 self.pedometerInterval = PedoIntervalCounter()
479 self.pedometerInterval.set_mode(self.mode)
480 self.repository = PedoRepositoryXML()
481 self.repository.load()
485 if not self.midnight_set:
486 self.update_at_midnight()
487 self.midnight_set = True
489 self.config = Config()
490 self.config.add_observer(self.load_config)
492 def update_at_midnight(self):
493 next_day = date.today() + timedelta(days=1)
494 diff = int(time.mktime(next_day.timetuple()) - time.time())
495 diff_before = diff - 5
496 diff_after = diff + 5
497 self.midnight_source_id = gobject.timeout_add_seconds(diff_after, self.midnight_callback, True)
498 self.midnight_before_source_id = gobject.timeout_add_seconds(diff_before, self.midnight_before_callback, True)
500 def stop_midnight_callback(self):
501 if self.midnight_source_id is not None:
502 gobject.source_remove(self.midnight_source_id)
503 if self.midnight_before_source_id is not None:
504 gobject.source_remove(self.midnight_before_source_id)
506 def midnight_before_callback(self, first=False):
507 logger.info("Before midnight callback")
509 self.stop_pedometer()
510 self.start_pedometer()
512 self.midnight_before_source_id = gobject.timeout_add_seconds(24*3600, self.midnight_before_callback)
517 def midnight_callback(self, first=False):
518 logger.info("Midnight callback")
522 self.midnight_source_id = gobject.timeout_add_seconds(24*3600, self.midnight_callback)
527 def load_config(self):
528 self.set_height(self.config.get_height(), self.config.get_step_length())
529 self.set_mode(self.config.get_mode())
530 self.set_unit(self.config.get_unit())
531 self.set_weight(self.config.get_weight())
532 self.set_second_view(self.config.get_secondview())
533 self.set_no_idle_time(self.config.get_noidletime())
534 self.set_sensitivity(self.config.get_sensitivity())
536 def load_values(self):
537 if self.second_view == 0:
538 self.v[1] = self.repository.get_alltime_values()
539 elif self.second_view == 1:
540 self.v[1] = self.repository.get_today_values()
542 self.v[1] = self.repository.get_this_week_values()
544 def save_values(self):
545 logger.info("Saving values to file")
546 self.repository.add_values(self.v[2])
547 self.repository.save()
550 def start_pedometer(self):
551 self.v[0] = PedoValues()
552 self.v[2] = PedoValues()
553 self.last_time = time.time()
554 self.is_running = True
555 self.pedometer.start()
558 def reset_all_values(self):
559 self.repository.reset_values()
560 self.v[0] = PedoValues()
561 self.v[1] = PedoValues()
562 self.v[2] = PedoValues()
565 def stop_pedometer(self):
566 self.is_running = False
567 self.pedometer.request_stop()
572 def get_second(self):
574 return self.v[2] + self.v[1]
578 def update_current(self):
580 Update distance and calories for current values based on new height, mode values
582 self.v[0].dist = self.get_distance(self.v[0].steps)
583 self.v[0].calories = self.get_calories(self.v[0].steps)
585 def steps_detected(self, cnt, last_steps=False):
586 if not last_steps and cnt == 0 and self.no_idle_time:
587 logger.info("No steps detected, timer is paused")
589 self.v[0].steps += cnt
590 self.v[0].dist += self.get_distance(cnt)
591 self.v[0].calories += self.get_calories(self.get_distance(cnt))
592 self.v[0].time += time.time() - self.last_time
594 self.v[2].steps += cnt
595 self.v[2].dist += self.get_distance(cnt)
596 self.v[2].calories += self.get_calories(self.get_distance(cnt))
597 self.v[2].time += time.time() - self.last_time
599 if not last_steps and self.v[2].steps > self.BUFFER_STEPS_INTERVAL:
602 self.v[2] = PedoValues()
609 self.last_time = time.time()
611 def get_calories(self, distance):
612 """calculate lost calories for the distance and weight given as parameters
614 #different coefficient for running and walking
620 #convert distance from meters to miles
621 distance *= 0.000621371192
624 #convert weight from kg to pounds
627 return weight * distance * coef
629 def set_mode(self, mode):
631 self.set_height(self.height_interval)
632 self.pedometerInterval.set_mode(self.mode)
635 def set_unit(self, new_unit):
641 def get_str_weight_unit(self, unit=None):
649 def set_weight(self, value):
653 def get_weight(self):
656 def set_sensitivity(self, value):
657 self.sensitivity = value
658 self.pedometerInterval.set_sensitivity(value)
660 def get_sensitivity(self):
661 return self.sensitivity
663 def set_second_view(self, second_view):
664 self.second_view = second_view
668 def set_callback_ui(self, func):
669 self.callback_update_ui = func
671 def set_height(self, height_interval, step_length=None):
672 self.height_interval = height_interval
674 if step_length is None:
675 step_length = self.STEP_LENGTH
676 #set height, will affect the distance
677 if height_interval == 0:
678 self.STEP_LENGTH = 0.59
679 elif height_interval == 1:
680 self.STEP_LENGTH = 0.64
681 elif height_interval == 2:
682 self.STEP_LENGTH = 0.71
683 elif height_interval == 3:
684 self.STEP_LENGTH = 0.77
685 elif height_interval == 4:
686 self.STEP_LENGTH = 0.83
687 elif height_interval == 5:
688 self.STEP_LENGTH = step_length
689 #increase step length if RUNNING
691 self.STEP_LENGTH *= 1.45
694 def set_no_idle_time(self, value):
695 self.no_idle_time = value
697 def get_distance(self, steps=None):
700 return self.STEP_LENGTH * steps;
702 def add_observer(self, func):
704 self.observers.index(func)
706 self.observers.append(func)
708 def remove_observer(self, func):
709 self.observers.remove(func)
711 def notify(self, optional=False):
712 if self.callback_update_ui is not None:
713 self.callback_update_ui()
715 for func in self.observers:
718 class AlarmController(Singleton):
720 fname = "/home/user/MyDocs/.sounds/Ringtones/Bicycle.aac"
726 pedo_controller = None
729 self.client = gconf.client_get_default()
730 self.config = Config()
731 self.config.add_observer(self.load_config)
733 self.pedo_controller = PedoController()
736 self.pedo_controller.add_observer(self.update)
737 self.start_value = self.pedo_controller.get_first()
739 def init_player(self):
740 self.player = gst.element_factory_make("playbin2", "player")
741 fakesink = gst.element_factory_make("fakesink", "fakesink")
742 self.player.set_property("video-sink", fakesink)
744 bus = self.player.get_bus()
745 bus.add_signal_watch()
746 bus.connect("message", self.on_message)
748 def on_message(self, bus, message):
750 if t == gst.MESSAGE_EOS:
751 self.player.set_state(gst.STATE_NULL)
752 self.is_playing = False
753 elif t == gst.MESSAGE_ERROR:
754 self.player.set_state(gst.STATE_NULL)
755 self.is_playing = False
756 err, debug = message.parse_error()
757 logger.error("ERROR: %s, %s" % (err, debug) )
759 def update(self, optional):
760 diff = self.pedo_controller.get_first() - self.start_value
761 if self.type == 0 and diff.time >= self.interval * 60 or \
762 self.type == 1 and diff.steps >= self.interval or \
763 self.type == 2 and diff.dist >= self.interval or \
764 self.type == 3 and diff.calories >= self.interval:
766 #get new instance of current values
767 self.start_value = PedoValues() + self.pedo_controller.get_first()
768 logger.info("Alarm!")
771 if self.player is None:
774 self.player.set_state(gst.STATE_NULL)
775 self.is_playing = False
777 self.player.set_property("uri", "file://" + self.fname)
778 self.player.set_state(gst.STATE_PLAYING)
779 self.is_playing = True
782 if self.player is not None:
783 self.player.set_state(gst.STATE_NULL)
785 def load_config(self):
786 self.enable = self.config.get_alarm_enable()
787 self.set_alarm_file(self.config.get_alarm_fname())
788 self.set_interval(self.config.get_alarm_interval())
789 self.set_type(self.config.get_alarm_type())
791 def set_enable(self, value):
795 self.pedo_controller.add_observer(self.update)
796 self.start_value = self.pedo_controller.get_first()
800 self.pedo_controller.remove_observer(self.update)
802 def get_enable(self):
805 def set_alarm_file(self, fname):
808 def get_alarm_file(self):
809 if self.fname == None:
813 def set_interval(self, interval):
814 self.interval = interval
816 def get_interval(self):
819 def set_type(self, type):
825 class PedoCounter(Singleton):
826 COORD_FNAME = "/sys/class/i2c-adapter/i2c-3/3-001d/coord"
827 COORD_FNAME_SDK = "/home/andrei/pedometer-widget-0.1/date.txt"
828 LOGFILE = "/home/user/log_pedometer"
829 #time in ms between two accelerometer data reads
830 COORD_GET_INTERVAL = 25
834 interval_counter = None
835 stop_requested = False
836 update_function = None
840 def __init__(self, update_function=None):
841 if not os.path.exists(self.COORD_FNAME):
842 self.COORD_FNAME = self.COORD_FNAME_SDK
844 self.interval_counter = PedoIntervalCounter()
845 self.update_function = update_function
847 def set_logging(self, value):
850 def get_rotation(self):
851 f = open(self.COORD_FNAME, 'r')
852 coords = [int(w) for w in f.readline().split()]
857 logger.info("Counter started")
858 self.isRunning = True
859 self.stop_requested = False
861 fname = "%d_%d_%d_%d_%d_%d" % time.localtime()[0:6]
862 self.file = open(self.LOGFILE + fname + ".txt", "w")
863 gobject.idle_add(self.run)
866 self.coords = [[], [], []]
867 self.stime = time.time()
869 gobject.timeout_add(self.COORD_GET_INTERVAL, self.read_coords)
872 def read_coords(self):
873 x, y, z = self.get_rotation()
874 self.coords[0].append(int(x))
875 self.coords[1].append(int(y))
876 self.coords[2].append(int(z))
877 now = time.time() - self.stime
879 self.file.write("%d %d %d %f\n" % (self.coords[0][-1], self.coords[1][-1], self.coords[2][-1], now))
884 if self.t[-1] > self.COUNT_INTERVAL or self.stop_requested:
886 gobject.idle_add(self.stop_interval)
889 def stop_interval(self):
890 self.interval_counter.set_vals(self.coords, self.t)
891 cnt = self.interval_counter.number_steps()
893 logger.info("Number of steps detected for last interval %d, number of coords: %d" % (cnt, len(self.t)))
895 gobject.idle_add(self.update_function, cnt, self.stop_requested)
897 if self.stop_requested:
898 gobject.idle_add(self.stop)
900 gobject.idle_add(self.run)
906 logger.info("Counter has finished")
908 def request_stop(self):
909 self.stop_requested = True
910 self.isRunning = False
912 class CustomButton(hildon.Button):
913 def __init__(self, icon):
914 hildon.Button.__init__(self, gtk.HILDON_SIZE_AUTO_WIDTH, hildon.BUTTON_ARRANGEMENT_VERTICAL)
916 self.set_size_request(int(32 * 1.4), int(30 * 1.0))
917 self.retval = self.connect("expose_event", self.expose)
919 def set_icon(self, icon):
922 def expose(self, widget, event):
923 self.context = widget.window.cairo_create()
924 self.context.rectangle(event.area.x, event.area.y,
925 event.area.width, event.area.height)
928 rect = self.get_allocation()
929 self.context.rectangle(rect.x, rect.y, rect.width, rect.height)
930 self.context.set_source_rgba(1, 1, 1, 0)
932 style = self.rc_get_style()
933 color = style.lookup_color("DefaultBackgroundColor")
934 if self.state == gtk.STATE_ACTIVE:
935 style = self.rc_get_style()
936 color = style.lookup_color("SelectionColor")
937 self.context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
940 #img = cairo.ImageSurface.create_from_png(self.icon)
942 #self.context.set_source_surface(img)
943 #self.context.set_source_surface(img, rect.width/2 - img.get_width() /2, 0)
945 img.set_from_file(self.icon)
946 buf = img.get_pixbuf()
947 buf = buf.scale_simple(int(32 * 1.5), int(30 * 1.5), gtk.gdk.INTERP_BILINEAR)
949 self.context.set_source_pixbuf(buf, rect.x + (event.area.width / 2 - 15) - 8, rect.y + 1)
950 self.context.scale(200, 200)
955 class CustomEventBox(gtk.EventBox):
958 gtk.EventBox.__init__(self)
960 def do_expose_event(self, event):
961 self.context = self.window.cairo_create()
962 self.context.rectangle(event.area.x, event.area.y,
963 event.area.width, event.area.height)
966 rect = self.get_allocation()
967 self.context.rectangle(rect.x, rect.y, rect.width, rect.height)
969 if self.state == gtk.STATE_ACTIVE:
970 style = self.rc_get_style()
971 color = style.lookup_color("SelectionColor")
972 self.context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
974 self.context.set_source_rgba(1, 1, 1, 0)
977 gtk.EventBox.do_expose_event(self, event)
979 class GraphController(Singleton):
980 ytitles = [_("Steps"), _("Average Speed"), _("Distance"), _("Calories")]
981 xtitles = [_("Day"), _("Week")] # "Today"]
987 self.repository = PedoRepositoryXML()
989 PedoController().add_observer(self.update_ui)
990 self.config = Config()
991 self.config.add_observer(self.load_config)
993 def load_config(self):
994 self.set_current_view(self.config.get_graphview())
996 def set_graph(self, widget):
1000 def set_current_view(self, view):
1002 current_view % len(ytitles) - gives the ytitle
1003 current_view / len(ytitles) - gives the xtitle
1005 self.x_id = view / len(self.ytitles)
1006 self.y_id = view % len(self.ytitles)
1009 def next_view(self):
1010 current_view = self.config.get_graphview() + 1
1011 if current_view == len(self.ytitles) * len(self.xtitles):
1013 self.config.set_graphview(current_view)
1015 def last_weeks_labels(self):
1017 delta = timedelta(days=7)
1020 ret.append(_("Week") + d.strftime("%W"))
1024 def compute_values(self):
1027 values = self.repository.get_last_7_days()
1029 delta = timedelta(days=1)
1031 labels.append(d.ctime().split()[0])
1034 elif self.x_id == 1:
1035 values = self.repository.get_last_weeks()
1038 labels.append(_("Week") + " " + d.strftime("%W"))
1039 d = d - timedelta(days=7)
1041 values = self.repository.get_today()
1045 yvalues = [line.steps for line in values]
1046 elif self.y_id == 1:
1047 yvalues = [line.get_avg_speed() for line in values]
1048 elif self.y_id == 2:
1049 yvalues = [line.dist for line in values]
1051 yvalues = [line.calories for line in values]
1053 #determine values for y lines in graph
1054 diff = self.get_best_interval_value(max(yvalues))
1057 ytext.append(str(int(i*diff)))
1059 if self.widget is not None:
1062 self.widget.values = yvalues
1063 self.widget.ytext = ytext
1064 self.widget.xtext = labels
1065 self.widget.max_value = diff * 5
1066 self.widget.text = self.xtitles[self.x_id] + " / " + self.ytitles[self.y_id]
1067 self.widget.queue_draw()
1069 logger.error("Widget not set in GraphController")
1071 def get_best_interval_value(self, max_value):
1072 diff = 1.0 * max_value / 5
1073 l = len(str(int(diff)))
1074 d = math.pow(10, l/2)
1075 val = int(math.ceil(1.0 * diff / d)) * d
1080 def update_ui(self, optional=False):
1081 """update graph values every x seconds"""
1082 if optional and self.last_update - time.time() < 600:
1084 if self.widget is None:
1087 self.compute_values()
1088 self.last_update = time.time()
1090 class GraphWidget(gtk.DrawingArea):
1093 gtk.DrawingArea.__init__(self)
1094 self.set_size_request(-1, 150)
1098 self.ytext = [" 0", "1000", "2000", "3000", "4000", "5000"]
1099 self.xtext = [_("Monday"), _("Tuesday"), _("Wednesday"), _("Thursday"), _("Friday"), _("Saturday"), _("Sunday")]
1100 self.values = [1500, 3400, 4000, 3600, 3200, 0, 4500]
1101 self.max_value = 5000
1102 self.text = _("All time steps")
1104 def do_expose_event(self, event):
1105 context = self.window.cairo_create()
1107 # set a clip region for the expose event
1108 context.rectangle(event.area.x, event.area.y,
1109 event.area.width, event.area.height)
1114 context.set_operator(cairo.OPERATOR_SOURCE)
1115 style = self.rc_get_style()
1117 if self.state == gtk.STATE_ACTIVE:
1118 color = style.lookup_color("SelectionColor")
1120 color = style.lookup_color("DefaultBackgroundColor")
1121 context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75)
1133 rect = self.get_allocation()
1137 cr.select_font_face("Purisa", cairo.FONT_SLANT_NORMAL,
1138 cairo.FONT_WEIGHT_NORMAL)
1139 cr.set_font_size(13)
1141 #check space needed to display ylabels
1142 te = cr.text_extents(self.ytext[-1])
1143 border_left = te[2] + 7
1145 cr.set_source_rgb(1, 1, 1)
1146 cr.move_to(border_left, space_above)
1147 cr.line_to(border_left, y-space_below)
1148 cr.set_line_width(2)
1151 cr.move_to(border_left, y-space_below)
1152 cr.line_to(x-border_right, y-space_below)
1153 cr.set_line_width(2)
1156 ydiff = (y-space_above-space_below) / self.yvalues
1157 for i in range(self.yvalues):
1158 yy = y-space_below-ydiff*(i+1)
1159 cr.move_to(border_left, yy)
1160 cr.line_to(x-border_right, yy)
1161 cr.set_line_width(0.8)
1166 yy = y - space_below - ydiff*i + 5
1167 te = cr.text_extents(self.ytext[i])
1169 cr.move_to(border_left-te[2]-2, yy)
1170 cr.show_text(self.ytext[i])
1172 cr.set_font_size(15)
1173 te = cr.text_extents(self.text)
1174 cr.move_to((x-te[2])/2, y-5)
1175 cr.show_text(self.text)
1177 graph_x_space = x - border_left - border_right
1178 graph_y_space = y - space_below - space_above
1179 bar_width = graph_x_space*0.75 / len(self.values)
1180 bar_distance = graph_x_space*0.25 / (1+len(self.values))
1182 #set dummy max value to avoid exceptions
1183 if self.max_value == 0:
1184 self.max_value = 100
1185 for i in range(len(self.values)):
1186 xx = border_left + (i+1)*bar_distance + i * bar_width
1188 height = graph_y_space * (1.0 * self.values[i] / self.max_value)
1189 cr.set_source_rgba(1, 1, 1, 0.75)
1190 cr.rectangle(int(xx), int(yy-height), int(bar_width), int(height))
1193 cr.set_source_rgba(1, 1, 1, 1)
1194 cr.select_font_face("Purisa", cairo.FONT_SLANT_NORMAL,
1195 cairo.FONT_WEIGHT_NORMAL)
1196 cr.set_font_size(13)
1198 cr.rotate(2*math.pi * (-45) / 180)
1199 for i in range(len(self.values)):
1200 xx = y - space_below - 10
1201 yy = border_left + (i+1)*bar_distance + i * bar_width
1202 cr.move_to(-xx, yy + bar_width*1.25 / 2)
1203 cr.show_text(self.xtext[i])
1205 class Config(Singleton):
1216 no_idle_time = False
1219 alarm_enable = False
1220 alarm_fname = "/home/user/MyDocs/.sounds/Ringtones/Bicycle.aac"
1227 if self._references > 1:
1229 self.client = gconf.client_get_default()
1230 self.client.add_dir('/apps/pedometerhomewidget', gconf.CLIENT_PRELOAD_RECURSIVE)
1231 self.notify_id = self.client.notify_add('/apps/pedometerhomewidget', self.gconf_changed)
1233 def add_observer(self, func):
1235 self.observers.index(func)
1237 self.observers.append(func)
1240 def remove_observer(self, func):
1241 self.observers.remove(func)
1243 def gconf_changed(self, client, *args, **kargs):
1248 for func in self.observers:
1251 logger.info("Update took: %f seconds" % (t2-t1))
1254 return self.client.get_int(MODE)
1256 def set_mode(self, value):
1257 self.client.set_int(MODE, value)
1259 def get_height(self):
1260 return self.client.get_int(HEIGHT)
1262 def set_height(self, value):
1263 self.client.set_int(HEIGHT, value)
1265 def get_step_length(self):
1266 return self.client.get_float(STEP_LENGTH)
1268 def set_step_length(self, value):
1269 self.client.set_float(STEP_LENGTH, value)
1271 def get_weight(self):
1272 return self.client.get_int(WEIGHT)
1274 def set_weight(self, value):
1275 self.client.set_int(WEIGHT, value)
1277 def get_sensitivity(self):
1278 return self.client.get_int(SENSITIVITY)
1280 def set_sensitivity(self, value):
1281 self.client.set_int(SENSITIVITY, value)
1284 return self.client.get_int(UNIT)
1286 def set_unit(self, value):
1287 self.client.set_int(UNIT, value)
1289 def get_aspect(self):
1290 return self.client.get_int(ASPECT)
1292 def set_aspect(self, value):
1293 self.client.set_int(ASPECT, value)
1295 def get_secondview(self):
1296 value = self.client.get_int(SECONDVIEW)
1297 if value < 0 or value > 2:
1299 logger.error("Invalid secondview value read from Gconf. Using default value")
1303 def set_secondview(self, value):
1304 self.client.set_int(SECONDVIEW, value)
1306 def get_graphview(self):
1307 return self.client.get_int(GRAPHVIEW)
1309 def set_graphview(self, value):
1310 self.client.set_int(GRAPHVIEW, value)
1312 def get_noidletime(self):
1313 return self.client.get_bool(NOIDLETIME)
1315 def set_noidletime(self, value):
1316 self.client.set_bool(NOIDLETIME, value)
1318 def get_logging(self):
1319 return self.client.get_bool(LOGGING)
1321 def set_logging(self, value):
1322 self.client.set_bool(LOGGING, value)
1324 def get_alarm_enable(self):
1325 return self.client.get_bool(ALARM_ENABLE)
1327 def set_alarm_enable(self, value):
1328 self.client.set_bool(ALARM_ENABLE, value)
1330 def get_alarm_fname(self):
1331 return self.client.get_string(ALARM_FNAME)
1333 def set_alarm_fname(self, value):
1334 self.client.set_string(ALARM_FNAME, value)
1336 def get_alarm_interval(self):
1337 return self.client.get_int(ALARM_INTERVAL)
1339 def set_alarrm_interval(self, value):
1340 self.client.set_int(ALARM_INTERVAL, value)
1342 def get_alarm_type(self):
1343 return self.client.get_int(ALARM_TYPE)
1345 def set_alarm_type(self, value):
1346 self.client.set_int(ALARM_TYPE, value)
1349 class PedometerHomePlugin(hildondesktop.HomePluginItem):
1353 labels = ["timer", "count", "dist", "avgSpeed", "calories"]
1358 #second view ( day / week/ alltime)
1361 second_view_labels = [_("All-time"), _("Today"), _("This week")]
1364 graph_controller = None
1369 hildondesktop.HomePluginItem.__init__(self)
1371 print "!!!!!!!!!!!!!!!Pedometer init"
1372 gobject.type_register(CustomEventBox)
1373 gobject.type_register(GraphWidget)
1378 self.config = Config()
1380 self.button = CustomButton(ICONSPATH + "play.png")
1381 self.button.connect("clicked", self.button_clicked)
1383 self.create_labels(self.labelsC)
1384 self.create_labels(self.labelsT)
1385 self.label_second_view = self.new_label_heading(self.second_view_labels[self.config.get_secondview()])
1387 self.controller = PedoController()
1388 self.controller.set_callback_ui(self.update_values)
1390 self.graph_controller = GraphController()
1391 self.alarm_controller = AlarmController()
1393 self.update_current()
1396 mainHBox = gtk.HBox(spacing=1)
1398 descVBox = gtk.VBox(spacing=1)
1399 descVBox.add(self.new_label_heading())
1400 descVBox.add(self.new_label_heading(_("Time") + ":"))
1401 descVBox.add(self.new_label_heading(_("Steps") + ":"))
1402 descVBox.add(self.new_label_heading(_("Calories") + ":"))
1403 descVBox.add(self.new_label_heading(_("Distance") + ":"))
1404 descVBox.add(self.new_label_heading(_("Avg Speed") + ":"))
1406 currentVBox = gtk.VBox(spacing=1)
1407 currentVBox.add(self.new_label_heading(_("Current")))
1408 currentVBox.add(self.labelsC["timer"])
1409 currentVBox.add(self.labelsC["count"])
1410 currentVBox.add(self.labelsC["calories"])
1411 currentVBox.add(self.labelsC["dist"])
1412 currentVBox.add(self.labelsC["avgSpeed"])
1413 self.currentBox = currentVBox
1415 totalVBox = gtk.VBox(spacing=1)
1416 totalVBox.add(self.label_second_view)
1417 totalVBox.add(self.labelsT["timer"])
1418 totalVBox.add(self.labelsT["count"])
1419 totalVBox.add(self.labelsT["calories"])
1420 totalVBox.add(self.labelsT["dist"])
1421 totalVBox.add(self.labelsT["avgSpeed"])
1422 self.totalBox = totalVBox
1424 buttonVBox = gtk.VBox(spacing=1)
1425 buttonVBox.add(self.new_label_heading(""))
1426 buttonVBox.add(self.button)
1427 buttonVBox.add(self.new_label_heading(""))
1429 eventBox = CustomEventBox()
1430 eventBox.set_visible_window(False)
1431 eventBox.add(totalVBox)
1432 eventBox.connect("button-press-event", self.eventBox_clicked)
1433 eventBox.connect("button-release-event", self.eventBox_clicked_release)
1435 mainHBox.add(buttonVBox)
1436 mainHBox.add(descVBox)
1437 mainHBox.add(currentVBox)
1438 mainHBox.add(eventBox)
1439 self.mainhbox = mainHBox
1441 graph = GraphWidget()
1442 self.graph_controller.set_graph(graph)
1444 eventBoxGraph = CustomEventBox()
1445 eventBoxGraph.set_visible_window(False)
1446 eventBoxGraph.add(graph)
1448 eventBoxGraph.connect("button-press-event", self.eventBoxGraph_clicked)
1449 eventBoxGraph.connect("button-release-event", self.eventBoxGraph_clicked_release)
1450 self.graphBox = eventBoxGraph
1452 self.mainvbox = gtk.VBox()
1454 self.mainvbox.add(mainHBox)
1455 self.mainvbox.add(eventBoxGraph)
1457 self.mainvbox.show_all()
1458 self.add(self.mainvbox)
1460 self.connect("unrealize", self.close_requested)
1461 self.set_settings(True)
1462 self.connect("show-settings", self.show_settings)
1464 self.config.add_observer(self.update_aspect)
1466 def eventBoxGraph_clicked(self, widget, data=None):
1467 widget.set_state(gtk.STATE_ACTIVE)
1469 def eventBoxGraph_clicked_release(self, widget, data=None):
1470 self.graph_controller.next_view()
1471 widget.set_state(gtk.STATE_NORMAL)
1473 def eventBox_clicked(self, widget, data=None):
1474 widget.set_state(gtk.STATE_ACTIVE)
1476 def eventBox_clicked_release(self, widget, data=None):
1477 widget.set_state(gtk.STATE_NORMAL)
1479 second_view = self.config.get_secondview()
1480 second_view = (second_view + 1) % 3
1481 self.config.set_secondview(second_view)
1483 def new_label_heading(self, title=""):
1484 l = gtk.Label(title)
1485 hildon.hildon_helper_set_logical_font(l, "SmallSystemFont")
1488 def create_labels(self, new_labels):
1489 for label in self.labels:
1491 hildon.hildon_helper_set_logical_font(l, "SmallSystemFont")
1492 hildon.hildon_helper_set_logical_color(l, gtk.RC_FG, gtk.STATE_NORMAL, "ActiveTextColor")
1493 new_labels[label] = l
1495 def update_aspect(self):
1496 aspect = self.config.get_aspect()
1498 self.graphBox.hide_all()
1500 self.graphBox.show_all()
1502 if aspect == 0 or aspect == 1:
1503 self.currentBox.show_all()
1504 self.totalBox.show_all()
1506 self.currentBox.show_all()
1507 self.totalBox.hide_all()
1509 self.currentBox.hide_all()
1510 self.totalBox.show_all()
1512 x,y = self.size_request()
1515 def update_ui_values(self, labels, values):
1516 labels["timer"].set_label(values.get_print_time())
1517 labels["count"].set_label(values.get_print_steps())
1518 labels["dist"].set_label(values.get_print_distance())
1519 labels["avgSpeed"].set_label(values.get_print_avg_speed())
1520 labels["calories"].set_label(values.get_print_calories())
1522 def update_current(self):
1523 self.update_ui_values(self.labelsC, self.controller.get_first())
1525 def update_total(self):
1526 self.update_ui_values(self.labelsT, self.controller.get_second())
1528 def show_alarm_settings(self, main_button):
1529 def choose_file(widget):
1530 file = hildon.FileChooserDialog(self, gtk.FILE_CHOOSER_ACTION_OPEN, hildon.FileSystemModel() )
1532 if ( file.run() == gtk.RESPONSE_OK):
1533 fname = file.get_filename()
1534 widget.set_value(fname)
1535 self.config.set_alarm_fname(fname)
1538 def test_sound(button):
1540 self.alarm_controller.play()
1541 except Exception, e:
1542 logger.error("Could not play alarm sound: %s" % e)
1543 hildon.hildon_banner_show_information(self, "None", "Could not play alarm sound")
1545 def enableButton_changed(button):
1546 value = button.get_active()
1547 self.config.set_alarm_enable(value)
1549 main_button.set_value("Enabled")
1551 main_button.set_value("Disabled")
1553 def selectorType_changed(selector, data, labelEntry2):
1554 type = selector.get_active(0)
1555 self.config.set_alarm_type(type)
1556 labelEntry2.set_label(suffix[type])
1558 dialog = gtk.Dialog()
1559 dialog.set_title(_("Alarm settings"))
1560 dialog.add_button(_("OK"), gtk.RESPONSE_OK)
1562 enableButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1563 enableButton.set_label(_("Enable alarm"))
1564 enableButton.set_active(self.alarm_controller.get_enable())
1565 enableButton.connect("toggled", enableButton_changed)
1567 testButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1568 testButton.set_alignment(0, 0.8, 1, 1)
1569 testButton.set_title(_("Test sound"))
1570 testButton.connect("pressed", test_sound)
1572 fileButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1573 fileButton.set_alignment(0, 0.8, 1, 1)
1574 fileButton.set_title(_("Alarm sound"))
1575 fileButton.set_value(self.alarm_controller.get_alarm_file())
1576 fileButton.connect("pressed", choose_file)
1578 labelEntry = gtk.Label(_("Notify every") + ":")
1579 suffix = [_("mins"), _("steps"), _("m/ft"), _("calories")]
1580 labelEntry2 = gtk.Label(suffix[self.alarm_controller.get_type()])
1581 intervalEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO_WIDTH)
1582 intervalEntry.set_text(str(self.alarm_controller.get_interval()))
1584 selectorType = hildon.TouchSelector(text=True)
1585 selectorType.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1586 selectorType.append_text(_("Time"))
1587 selectorType.append_text(_("Steps"))
1588 selectorType.append_text(_("Distance"))
1589 selectorType.append_text(_("Calories"))
1590 selectorType.connect("changed", selectorType_changed, labelEntry2)
1592 typePicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1593 typePicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1594 typePicker.set_title(_("Alarm type"))
1595 typePicker.set_selector(selectorType)
1596 typePicker.set_active(self.alarm_controller.get_type())
1599 hbox.add(labelEntry)
1600 hbox.add(intervalEntry)
1601 hbox.add(labelEntry2)
1603 dialog.vbox.add(enableButton)
1604 dialog.vbox.add(fileButton)
1605 dialog.vbox.add(testButton)
1606 dialog.vbox.add(typePicker)
1607 dialog.vbox.add(hbox)
1610 response = dialog.run()
1611 if response != gtk.RESPONSE_OK:
1614 value = int(intervalEntry.get_text())
1615 self.config.set_alarrm_interval(value)
1618 hildon.hildon_banner_show_information(self, "None", _("Invalid interval"))
1622 def show_settings(self, widget):
1623 def reset_total_counter(arg):
1624 note = hildon.hildon_note_new_confirmation(self.dialog, _("Are you sure you want to delete all your pedometer history?"))
1626 if ret == gtk.RESPONSE_OK:
1627 self.controller.reset_all_values()
1628 hildon.hildon_banner_show_information(self, "None", _("All history was deleted"))
1631 def alarmButton_pressed(widget):
1632 self.show_alarm_settings(widget)
1634 def selector_changed(selector, data):
1635 mode = selector.get_active(0)
1636 self.config.set_mode(mode)
1638 def selectorUnit_changed(selector, data):
1639 unit = selector.get_active(0)
1640 self.config.set_unit(unit)
1642 update_weight_button()
1643 stepLengthButton_value_update()
1645 def selectorUI_changed(selector, data):
1646 aspect = selectorUI.get_active(0)
1647 self.config.set_aspect(aspect)
1649 def logButton_changed(checkButton):
1650 logging = checkButton.get_active()
1651 self.config.set_logging(logging)
1653 def idleButton_changed(idleButton):
1654 no_idle_time = idleButton.get_active()
1655 self.config.set_noidletime(no_idle_time)
1657 def update_weight_button():
1658 weightButton.set_value(str(self.config.get_weight()) + \
1659 " " + self.controller.get_str_weight_unit(self.config.get_unit()) )
1661 def weight_dialog(button):
1662 dialog = gtk.Dialog(_("Weight"), self.dialog)
1663 dialog.add_button(_("OK"), gtk.RESPONSE_OK)
1665 label = gtk.Label(_("Weight") + ":")
1667 entry.set_text(str(self.config.get_weight()))
1669 suffixLabel = gtk.Label(self.controller.get_str_weight_unit(self.config.get_unit()))
1674 hbox.add(suffixLabel)
1676 dialog.vbox.add(hbox)
1679 response = dialog.run()
1680 if response != gtk.RESPONSE_OK:
1683 value = int(entry.get_text())
1686 self.config.set_weight(value)
1687 update_weight_button()
1690 hildon.hildon_banner_show_information(self, "None", _("Invalid weight"))
1693 def sensitivity_dialog(button):
1694 def seekbar_changed(seekbar):
1695 label.set_text(str(seekbar.get_position()) + " %")
1697 dialog = gtk.Dialog(_("Sensitivity"), self.dialog)
1698 dialog.add_button(_("OK"), gtk.RESPONSE_OK)
1699 seekbar = hildon.Seekbar()
1700 seekbar.set_size_request(400, -1)
1701 seekbar.set_total_time(200)
1702 seekbar.set_position(self.config.get_sensitivity())
1703 seekbar.connect("value-changed", seekbar_changed)
1707 label = gtk.Label(str(self.config.get_sensitivity()) + " %")
1708 label.set_size_request(30, -1)
1711 dialog.vbox.add(hbox)
1714 if dialog.run() == gtk.RESPONSE_OK:
1715 value = seekbar.get_position()
1716 self.config.set_sensitivity(value)
1717 button.set_value(str(value) + " %")
1721 def stepLengthButton_value_update():
1722 if self.config.get_height() == 5:
1723 l_unit = ["m", "ft"]
1724 stepLengthButton.set_value(_("Custom value") + ": %.2f %s" % (self.config.get_step_length(), l_unit[self.config.get_unit()]))
1726 h = [ ["< 1.50 m", "1.50 - 1.65 m", "1.66 - 1.80 m", "1.81 - 1.95 m", " > 1.95 m"],
1727 ["< 5 ft", "5 - 5.5 ft", "5.5 - 6 ft", "6 - 6.5 ft", "> 6.5 ft"]]
1728 str = _("Using predefined value for height") + ": %s" % h[self.config.get_unit()][self.config.get_height()]
1729 stepLengthButton.set_value(str)
1731 def stepLength_dialog(button):
1732 def selectorH_changed(selector, data, dialog):
1733 height = selector.get_active(0)
1734 self.config.set_height(height)
1735 stepLengthButton_value_update()
1737 def manualButton_clicked(button, dialog):
1739 dlg.set_title(_("Custom step length"))
1740 dlg.add_button(_("OK"), gtk.RESPONSE_OK)
1742 label = gtk.Label(_("Length"))
1744 entry = hildon.Entry(gtk.HILDON_SIZE_AUTO_WIDTH)
1745 if self.config.get_height() == 5:
1746 entry.set_text(str(self.config.get_step_length()))
1748 labelSuffix = gtk.Label()
1749 if self.config.get_unit() == 0:
1750 labelSuffix.set_label("m")
1752 labelSuffix.set_label("ft")
1756 hbox.add(labelSuffix)
1761 response = dlg.run()
1762 if response != gtk.RESPONSE_OK:
1765 value = float(entry.get_text())
1768 self.config.set_step_length(value)
1769 self.config.set_height(5)
1770 stepLengthButton_value_update()
1773 hildon.hildon_banner_show_information(self, "None", _("Invalid length"))
1777 def heightButton_clicked(button, dialog):
1780 dialog = gtk.Dialog()
1781 dialog.set_title(_("Step length"))
1783 manualButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1784 manualButton.set_title(_("Enter custom value"))
1785 manualButton.set_alignment(0, 0.8, 1, 1)
1786 manualButton.connect("clicked", manualButton_clicked, dialog)
1788 selectorH = hildon.TouchSelector(text=True)
1789 selectorH.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1790 selectorH.append_text("< 1.50 m")
1791 selectorH.append_text("1.50 - 1.65 m")
1792 selectorH.append_text("1.66 - 1.80 m")
1793 selectorH.append_text("1.81 - 1.95 m")
1794 selectorH.append_text(" > 1.95 m")
1796 selectorH_English = hildon.TouchSelector(text=True)
1797 selectorH_English.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1798 selectorH_English.append_text("< 5 ft")
1799 selectorH_English.append_text("5 - 5.5 ft")
1800 selectorH_English.append_text("5.5 - 6 ft")
1801 selectorH_English.append_text("6 - 6.5 ft")
1802 selectorH_English.append_text("> 6.5 ft")
1804 heightPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1805 heightPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1806 heightPicker.set_title(_("Use predefined values for height"))
1809 unit = self.config.get_unit()
1811 heightPicker.set_selector(selectorH)
1813 heightPicker.set_selector(selectorH_English)
1815 height = self.config.get_height()
1817 heightPicker.set_active(height)
1819 heightPicker.get_selector().connect("changed", selectorH_changed, dialog)
1820 heightPicker.connect("value-changed", heightButton_clicked, dialog)
1822 dialog.vbox.add(heightPicker)
1823 dialog.vbox.add(manualButton)
1826 if dialog.run() == gtk.RESPONSE_DELETE_EVENT:
1829 def donateButton_clicked(button, dialog):
1830 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"
1831 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
1834 dialog = gtk.Dialog()
1835 dialog.set_title(_("Settings"))
1836 dialog.add_button(_("OK"), gtk.RESPONSE_OK)
1837 self.dialog = dialog
1839 stepLengthButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1840 stepLengthButton.set_title(_("Step length"))
1841 stepLengthButton.set_alignment(0, 0.8, 1, 1)
1842 stepLengthButton.connect("clicked", stepLength_dialog)
1843 stepLengthButton_value_update()
1845 resetButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1846 resetButton.set_title(_("Reset"))
1847 resetButton.set_value(_("All the stored values will be erased"))
1848 resetButton.set_alignment(0, 0.8, 1, 1)
1849 resetButton.connect("clicked", reset_total_counter)
1851 alarmButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1852 alarmButton.set_title(_("Alarm"))
1853 if self.config.get_alarm_enable():
1854 alarmButton.set_value(_("Enabled"))
1856 alarmButton.set_value(_("Disabled"))
1857 alarmButton.set_alignment(0, 0.8, 1, 1)
1858 alarmButton.connect("clicked", alarmButton_pressed)
1860 selector = hildon.TouchSelector(text=True)
1861 selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1862 selector.append_text(_("Walk"))
1863 selector.append_text(_("Run"))
1864 selector.connect("changed", selector_changed)
1866 modePicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1867 modePicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1868 modePicker.set_title(_("Mode"))
1869 modePicker.set_selector(selector)
1870 modePicker.set_active(self.config.get_mode())
1872 weightButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1873 weightButton.set_title(_("Weight"))
1874 weightButton.set_alignment(0, 0.8, 1, 1)
1875 update_weight_button()
1876 weightButton.connect("clicked", weight_dialog)
1878 selectorUnit = hildon.TouchSelector(text=True)
1879 selectorUnit.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1880 selectorUnit.append_text(_("Metric (km)"))
1881 selectorUnit.append_text(_("English (mi)"))
1882 selectorUnit.connect("changed", selectorUnit_changed)
1884 unitPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1885 unitPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1886 unitPicker.set_title(_("Unit"))
1887 unitPicker.set_selector(selectorUnit)
1888 unitPicker.set_active(self.config.get_unit())
1890 selectorUI = hildon.TouchSelector(text=True)
1891 selectorUI = hildon.TouchSelector(text=True)
1892 selectorUI.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1893 selectorUI.append_text(_("Show current + total + graph"))
1894 selectorUI.append_text(_("Show current + total"))
1895 selectorUI.append_text(_("Show only current"))
1896 selectorUI.append_text(_("Show only total"))
1897 selectorUI.connect("changed", selectorUI_changed)
1899 UIPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1900 UIPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1901 UIPicker.set_title(_("Widget aspect"))
1902 UIPicker.set_selector(selectorUI)
1903 UIPicker.set_active(self.config.get_aspect())
1905 sensitivityButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1906 sensitivityButton.set_title(_("Sensitivity"))
1907 sensitivityButton.set_alignment(0, 0.8, 1, 1)
1908 sensitivityButton.set_value(str(self.config.get_sensitivity()) + " %")
1909 sensitivityButton.connect("clicked", sensitivity_dialog)
1911 donateButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1912 donateButton.set_title(_("Donate"))
1913 donateButton.set_value(_("Please support the development of this opensource widget!"))
1914 donateButton.set_alignment(0, 0.8, 1, 1)
1915 donateButton.connect("clicked", donateButton_clicked, dialog)
1917 logButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1918 logButton.set_label(_("Log data"))
1919 logButton.set_active(self.config.get_logging())
1920 logButton.connect("toggled", logButton_changed)
1922 idleButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1923 idleButton.set_label(_("Pause time when not walking"))
1924 idleButton.set_active(self.config.get_noidletime())
1925 idleButton.connect("toggled", idleButton_changed)
1927 pan_area = hildon.PannableArea()
1929 vbox.add(alarmButton)
1930 vbox.add(modePicker)
1931 vbox.add(stepLengthButton)
1932 vbox.add(weightButton)
1933 vbox.add(unitPicker)
1934 vbox.add(sensitivityButton)
1936 vbox.add(idleButton)
1937 vbox.add(resetButton)
1938 vbox.add(donateButton)
1939 #vbox.add(logButton)
1941 pan_area.add_with_viewport(vbox)
1942 pan_area.set_size_request(-1, 300)
1944 dialog.vbox.add(pan_area)
1947 response = dialog.run()
1950 def close_requested(self, widget):
1951 if self.controller.is_running:
1952 self.controller.stop_pedometer()
1953 self.controller.stop_midnight_callback()
1955 def update_values(self):
1956 #TODO: do not update if the widget is not on the active desktop
1957 self.label_second_view.set_label(self.second_view_labels[self.config.get_secondview()])
1958 self.update_current()
1961 def button_clicked(self, button):
1962 if self.controller.is_running:
1963 self.controller.stop_pedometer()
1964 self.button.set_icon(ICONSPATH + "play.png")
1966 self.controller.start_pedometer()
1967 self.button.set_icon(ICONSPATH + "stop.png")
1968 hildon.hildon_banner_show_information(self, "None", _("Keep the N900 in a pocket close to your hip for best results"))
1970 def do_expose_event(self, event):
1971 cr = self.window.cairo_create()
1972 cr.region(event.window.get_clip_region())
1974 #cr.set_source_rgba(0.4, 0.64, 0.564, 0.5)
1975 style = self.rc_get_style()
1976 color = style.lookup_color("DefaultBackgroundColor")
1977 cr.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
1980 width = self.allocation.width
1981 height = self.allocation.height
1983 x = self.allocation.x
1984 y = self.allocation.y
1986 cr.move_to(x + radius, y)
1987 cr.line_to(x + width - radius, y)
1988 cr.curve_to(x + width - radius, y, x + width, y, x + width, y + radius)
1989 cr.line_to(x + width, y + height - radius)
1990 cr.curve_to(x + width, y + height - radius, x + width, y + height, x + width - radius, y + height)
1991 cr.line_to(x + radius, y + height)
1992 cr.curve_to(x + radius, y + height, x, y + height, x, y + height - radius)
1993 cr.line_to(x, y + radius)
1994 cr.curve_to(x, y + radius, x, y, x + radius, y)
1996 cr.set_operator(cairo.OPERATOR_SOURCE)
1999 color = style.lookup_color("ActiveTextColor")
2000 cr.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.5);
2001 cr.set_line_width(1)
2004 hildondesktop.HomePluginItem.do_expose_event(self, event)
2006 def do_realize(self):
2007 screen = self.get_screen()
2008 self.set_colormap(screen.get_rgba_colormap())
2009 self.set_app_paintable(True)
2010 hildondesktop.HomePluginItem.do_realize(self)
2012 hd_plugin_type = PedometerHomePlugin
2017 logger = logging.getLogger("pedometer")
2018 logger.setLevel(logging.INFO)
2020 ch = logging.StreamHandler()
2021 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
2022 ch.setFormatter(formatter)
2023 logger.addHandler(ch)
2025 # The code below is just for testing purposes.
2026 # It allows to run the widget as a standalone process.
2027 if __name__ == "__main__":
2029 gobject.type_register(hd_plugin_type)
2030 obj = gobject.new(hd_plugin_type, plugin_id="plugin_id")