source: subversion/applications/mobile/rana/rana.py @ 34949

Last change on this file since 34949 was 10271, checked in by ojw, 11 years ago

Add explicit background colour

  • Property svn:executable set to *
File size: 7.3 KB
Line 
1#!/usr/bin/python
2#----------------------------------------------------------------------------
3# Rana main GUI.  Displays maps, for use on a mobile device
4#
5# Controls:
6#   * click on the overlay text to change fields displayed
7#----------------------------------------------------------------------------
8# Copyright 2007-2008, Oliver White
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation, either version 3 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program.  If not, see <http://www.gnu.org/licenses/>.
22#----------------------------------------------------------------------------
23import pygtk
24pygtk.require('2.0')
25import gobject
26import gtk
27import sys
28import cairo
29import os
30from math import sqrt
31from time import clock
32from gtk import gdk
33
34
35def update1(mapWidget):
36  mapWidget.update()
37  return(True)
38
39def update2(mapWidget):
40  mapWidget.checkForRedraw()
41  return(True)
42
43class MapWidget(gtk.Widget):
44  __gsignals__ = { \
45    'realize': 'override',
46    'expose-event' : 'override',
47    'size-allocate': 'override',
48    'size-request': 'override'
49    }
50  def __init__(self):
51    gtk.Widget.__init__(self)
52    self.draw_gc = None
53    self.timer1 = gobject.timeout_add(100, update1, self)
54    self.timer2 = gobject.timeout_add(10, update2, self)
55    self.d = {} # List of data
56    self.m = {} # List of modules
57    self.loadModules('modules')
58   
59  def loadModules(self, module_path):
60    """Load all modules from the specified directory"""
61    sys.path.append(module_path)
62    print "importing modules:"
63    for f in os.listdir(module_path):
64      if(f[0:4] == 'mod_' and f[-3:] == '.py'):
65        name = f[4:-3]
66        filename = "%s\\%s" % ( module_path, f[0:-3]) # with directory name, but without .py extension
67        a = __import__(f[0:-3])
68        self.m[name] = a.getModule(self.m,self.d)
69        self.m[name].moduleName = name
70        print " * %s: %s" % (name, self.m[name].__doc__)
71
72    print "Loaded all modules, initialising"
73    for m in self.m.values():
74      m.firstTime()
75     
76  def beforeDie(self):
77    print "Shutting-down modules"
78    for m in self.m.values():
79      m.shutdown()
80 
81  def update(self):
82    for m in self.m.values():
83      m.update()
84    self.checkForRedraw()
85 
86  def checkForRedraw(self):
87    if(self.d.get("needRedraw", False)):
88      self.forceRedraw()
89
90  def mousedown(self,x,y):
91    pass
92   
93  def click(self, x, y):
94    m = self.m.get("clickHandler",None)
95    if(m != None):
96      m.handleClick(x,y)
97      self.update()
98     
99  def handleDrag(self,x,y,dx,dy,startX,startY):
100    m = self.m.get("clickHandler",None)
101    if(m != None):
102      m.handleDrag(startX,startY,dx,dy,x,y)
103      self.update()
104       
105  def forceRedraw(self):
106    """Make the window trigger a draw event. 
107    TODO: consider replacing this if porting pyroute to another platform"""
108    self.d['needRedraw'] = False
109    try:
110      self.window.invalidate_rect((0,0,self.rect.width,self.rect.height),False)
111    except AttributeError:
112      pass
113   
114  def draw(self, cr):
115    start = clock()
116    for m in self.m.values():
117      m.beforeDraw()
118
119    menuName = self.d.get('menu', None)
120    if(menuName != None):
121      for m in self.m.values():
122        m.drawMenu(cr, menuName)
123    else:
124      # map background
125      cr.set_source_rgb(0.2,0.2,0.2)
126      cr.rectangle(0,0,self.rect.width,self.rect.height)
127      cr.fill()
128     
129      # Draw the base map, the map overlays, and the screen overlays
130      for m in self.m.values():
131        m.drawMap(cr)
132      for m in self.m.values():
133        m.drawMapOverlay(cr)
134      for m in self.m.values():
135        m.drawScreenOverlay(cr)
136
137    #print "Redraw took %1.2f ms" % (1000 * (clock() - start))
138   
139  def do_realize(self):
140    self.set_flags(self.flags() | gtk.REALIZED)
141    self.window = gdk.Window( \
142      self.get_parent_window(),
143      width = self.allocation.width,
144      height = self.allocation.height,
145      window_type = gdk.WINDOW_CHILD,
146      wclass = gdk.INPUT_OUTPUT,
147      event_mask = self.get_events() | gdk.EXPOSURE_MASK)
148    self.window.set_user_data(self)
149    self.style.attach(self.window)
150    self.style.set_background(self.window, gtk.STATE_NORMAL)
151    self.window.move_resize(*self.allocation)
152  def do_size_request(self, allocation):
153    pass
154  def do_size_allocate(self, allocation):
155    self.allocation = allocation
156    if self.flags() & gtk.REALIZED:
157      self.window.move_resize(*allocation)
158  def _expose_cairo(self, event, cr):
159    self.rect = self.allocation
160    self.d['viewport'] = (self.rect.x, self.rect.y, self.rect.width, self.rect.height)
161    #self.modules['projection'].setView( \
162    #  self.rect.x,
163    #  self.rect.y,
164    #  self.rect.width,
165    #  self.rect.height)
166   
167    if(1): # optional set clipping in cairo
168      cr.rectangle(
169        self.rect.x,
170        self.rect.y,
171        self.rect.width,
172        self.rect.height)
173      cr.clip()
174   
175    self.draw(cr)
176  def do_expose_event(self, event):
177    self.chain(event)
178    cr = self.window.cairo_create()
179    return self._expose_cairo(event, cr)
180
181class GuiBase:
182  """Wrapper class for a GUI interface"""
183  def __init__(self, device):
184    # Create the window
185    win = gtk.Window()
186    win.set_title('Rana')
187    win.connect('delete-event', gtk.main_quit)
188
189    if(device == 'eee'): # test for use with asus eee
190      win.resize(800,600)
191      win.move(gtk.gdk.screen_width() - 900, 50)
192    elif(device == 'n95'): # test for use with nokia 95
193      win.resize(480,640)
194      win.move(gtk.gdk.screen_width() - 500, 50)
195    else: # test for use with neo freerunner
196      win.resize(480,640)
197      win.move(gtk.gdk.screen_width() - 500, 50)
198   
199    # Events
200    event_box = gtk.EventBox()
201    event_box.connect("button_press_event", lambda w,e: self.pressed(e))
202    event_box.connect("button_release_event", lambda w,e: self.released(e))
203    event_box.connect("motion_notify_event", lambda w,e: self.moved(e))
204    win.add(event_box)
205   
206    # Create the map
207    self.mapWidget = MapWidget()
208    event_box.add(self.mapWidget)
209   
210    # Finalise the window
211    win.show_all()
212    gtk.main()
213    self.mapWidget.beforeDie()
214   
215  def pressed(self, event):
216    self.dragstartx = event.x
217    self.dragstarty = event.y
218    #print dir(event)
219    #print "Pressed button %d at %1.0f, %1.0f" % (event.button, event.x, event.y)
220   
221    self.dragx = event.x
222    self.dragy = event.y
223    self.mapWidget.mousedown(event.x,event.y)
224   
225  def moved(self, event):
226    """Drag-handler"""
227
228    self.mapWidget.handleDrag( \
229      event.x,
230      event.y,
231      event.x - self.dragx, 
232      event.y - self.dragy,
233      self.dragstartx,
234      self.dragstarty)
235
236    self.dragx = event.x
237    self.dragy = event.y
238  def released(self, event):
239    dx = event.x - self.dragstartx
240    dy = event.y - self.dragstarty
241    distSq = dx * dx + dy * dy
242    # Adjust this to the length^2 of a gerfingerpoken on your touchscreen (1024 is for Freerunner, since it's very high resolution)
243    if distSq < 1024:
244      self.mapWidget.click(event.x, event.y)
245
246
247if __name__ == "__main__":
248  program = GuiBase('neo')
249
250 
Note: See TracBrowser for help on using the repository browser.