3 """Public transport information for Vienna"""
5 __author__ = 'kelvan <kelvan@logic.at>'
7 __website__ = 'http://tinyurl.com/gotoVienna'
8 __license__ = 'GNU General Public License v3 or later'
10 from datetime import datetime
12 from PySide.QtCore import QAbstractListModel, QModelIndex, QObject, Slot, Signal, Qt
13 from PySide.QtGui import QApplication, QTextItem
14 from PySide.QtDeclarative import QDeclarativeView
16 from gotovienna.utils import *
17 from gotovienna.realtime import *
18 from gotovienna.gps import *
19 from gotovienna.update import *
20 from gotovienna.config import config as conf
26 from datetime import time
28 class Config(QObject):
30 QObject.__init__(self)
33 def getGpsEnabled(self):
34 return conf.getGpsEnabled()
36 @Slot(bool, result=unicode)
37 def setGpsEnabled(self, val):
39 return conf.setGpsEnabled(val)
42 def getLastUpdate(self):
44 return conf.getLastStationsUpdate()
47 def updateStations(self):
51 return datetime.now().strftime('%c')
52 except Exception as e:
57 def checkStationsUpdate(self):
58 # FIXME exception handling foo
60 return check_stations_update()
64 class AboutInfo(QObject):
66 QObject.__init__(self)
70 return u'gotoVienna %s' % __version__
73 def getWebsiteURL(self):
77 def getCopyright(self):
78 return 'Copyright 2011, 2012 %s' % __author__
84 class DepartureModel(QAbstractListModel):
85 LINE_ROLE = Qt.UserRole + 1
86 DIRECTION_ROLE = Qt.UserRole + 2
87 STATION_ROLE = Qt.UserRole + 3
88 TIME_ROLE = Qt.UserRole + 4
89 LOWFLOOR_ROLE = Qt.UserRole + 5
91 def __init__(self, parent=None):
92 super(DepartureModel, self).__init__(parent)
96 self.keys[DepartureModel.LINE_ROLE] = 'line'
97 self.keys[DepartureModel.DIRECTION_ROLE] = 'direction'
98 self.keys[DepartureModel.STATION_ROLE] = 'station'
99 self.keys[DepartureModel.TIME_ROLE] = 'time'
100 self.keys[DepartureModel.LOWFLOOR_ROLE] = 'lowfloor'
101 self.setRoleNames(self.keys)
103 def rowCount(self, index):
104 return len(self._data)
106 def data(self, index, role):
107 if not index.isValid():
110 if index.row() > len(self._data):
113 departure = self._data[index.row()]
115 if self.keys.has_key(role):
116 return departure[self.keys[role]]
120 def setDepartures(self, dep):
121 self.beginResetModel()
126 def __init__(self, depModel):
127 QObject.__init__(self)
128 self.itip = ITipParser()
130 self.departureModel = depModel
132 # Read line names in categorized/sorted order
133 for _, lines in categorize_lines(self.itip.lines):
134 self.lines.extend(lines)
136 self.current_line = ''
137 self.current_stations = []
138 self.current_departures = []
140 @Slot(int, result=str)
141 def get_direction(self, idx):
142 return self.current_stations[idx][0]
144 @Slot(str, str, result='QStringList')
145 def get_stations(self, line, direction):
146 print 'line:', line, 'current line:', self.current_line
147 for dx, stations in self.current_stations:
148 print 'dx:', dx, 'direction:', direction
150 return [stationname for stationname, url in stations]
152 return ['no stations found']
154 directionsLoaded = Signal()
157 def load_directions(self, line):
159 stations = sorted(self.itip.get_stations(line).items())
161 self.current_line = line
162 self.current_stations = stations
164 self.directionsLoaded.emit()
166 threading.Thread(target=load_async).start()
168 #def map_departure(self, dep):
169 # """ prepare departure list for qml gui
171 # dep['lowfloor'] = 1 if dep['lowfloor'] else 0
172 # dep['realtime'] = 1 if dep['realtime'] else 0
173 # dep['time'] = dep['ftime']
176 departuresLoaded = Signal()
179 def load_departures_test(self, **args):
180 """ valid args combinations
185 if args.has_key('station'):
186 if args.has_key('line'):
187 self.current_departures = map(self.map_departure, \
188 self.itip.get_departures(args['line'], args['station']))
189 #print self.current_departures
190 self.departuresLoaded.emit()
192 self.current_departures = map(self.map_departure, \
193 sort_departures(self.itip.get_departures_by_station(station)))
195 raise KeyError('Missing valid argument combination')
197 threading.Thread(target=load_async).start()
200 def load_departures(self, url):
202 self.departureModel.setDepartures(self.itip.get_departures(url))
203 #print self.current_departures
204 self.departuresLoaded.emit()
206 threading.Thread(target=load_async).start()
209 def load_station_departures(self, station):
211 self.departureModel.setDepartures(sort_departures(self.itip.get_departures_by_station(station)))
212 self.departuresLoaded.emit()
214 threading.Thread(target=load_async).start()
217 def load_nearby_departures(self, lat, lon):
219 self.current_departures = []
221 stations = get_nearby_stations(lat, lon)
223 for station in stations:
226 self.current_departures += self.itip.get_departures_by_station(station)
227 except Exception as e:
229 self.current_departures = map(self.map_departure, \
230 sort_departures(self.current_departures))
231 #print self.current_departures
232 except Exception as e:
236 self.departuresLoaded.emit()
238 threading.Thread(target=load_async).start()
240 @Slot(str, str, str, result=str)
241 def get_directions_url(self, line, direction, station):
242 return self.itip.get_url_from_direction(line, direction, station)
244 @Slot(result='QStringList')
248 @Slot(float, float, result='QStringList')
249 def get_nearby_stations(self, lat, lon):
251 return get_nearby_stations(lat, lon)
252 except BaseException as e:
253 # No/wrong stations.db file
257 if __name__ == '__main__':
258 app = QApplication(sys.argv)
260 view = QDeclarativeView()
262 aboutInfo = AboutInfo()
264 departureModel = DepartureModel()
266 # instantiate the Python object
267 itip = Gui(departureModel)
269 # expose the object to QML
270 context = view.rootContext()
271 context.setContextProperty('itip', itip)
272 context.setContextProperty('aboutInfo', aboutInfo)
273 context.setContextProperty('config', config)
274 context.setContextProperty('resultModel', departureModel)
276 if os.path.abspath(__file__).startswith('/usr/bin/'):
277 # Assume system-wide installation, QML from /usr/share/
278 view.setSource('/usr/share/gotovienna/qml/main.qml')
280 # Assume test from source directory, use relative path
281 view.setSource(os.path.join(os.path.dirname(__file__), 'qml/main.qml'))
283 view.showFullScreen()
285 sys.exit(app.exec_())