Settings page
[pywienerlinien] / gotovienna-qml
1 #!/usr/bin/env python
2
3 """Public transport information for Vienna"""
4
5 __author__ = 'kelvan <kelvan@logic.at>'
6 __version__ = '0.9.0'
7 __website__ = 'http://tinyurl.com/gotoVienna'
8 __license__ = 'GNU General Public License v3 or later'
9
10 from datetime import datetime
11
12 from PySide.QtCore import QAbstractListModel, QModelIndex, QObject, Slot, Signal
13 from PySide.QtGui import QApplication
14 from PySide.QtDeclarative import QDeclarativeView
15
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
21
22 import urllib2
23 import os
24 import sys
25 import threading
26 from datetime import time
27
28 class Config(QObject):
29     def __init__(self):
30         QObject.__init__(self)
31
32     @Slot(result=bool)
33     def getGpsEnabled(self):
34         return conf.getGpsEnabled()
35
36     @Slot(bool, result=unicode)
37     def setGpsEnabled(self, val):
38         # TODO
39         return conf.setGpsEnabled(val)
40
41     @Slot(result=unicode)
42     def getLastUpdate(self):
43         # TODO
44         return conf.getLastStationsUpdate()
45
46     @Slot(result=unicode)
47     def updateStations(self):
48         # TODO
49         try:
50             update_stations()
51             return datetime.now().strftime('%c')
52         except Exception as e:
53             print e
54             return ''
55
56     @Slot(result=bool)
57     def checkStationsUpdate(self):
58         # FIXME exception handling foo
59         try:
60             return check_stations_update()
61         except:
62             return False
63
64 class AboutInfo(QObject):
65     def __init__(self):
66         QObject.__init__(self)
67
68     @Slot(result=unicode)
69     def getAppName(self):
70         return u'gotoVienna %s' % __version__
71
72     @Slot(result=unicode)
73     def getWebsiteURL(self):
74         return __website__
75
76     @Slot(result=unicode)
77     def getCopyright(self):
78         return 'Copyright 2011, 2012 %s' % __author__
79
80     @Slot(result=unicode)
81     def getLicense(self):
82         return __license__
83
84 class GotoViennaListModel(QAbstractListModel):
85     def __init__(self, objects=None):
86         QAbstractListModel.__init__(self)
87         if objects is None:
88             objects = []
89         self._objects = objects
90         self.setRoleNames({0: 'modelData'})
91
92     def set_objects(self, objects):
93         self._objects = objects
94
95     def get_objects(self):
96         return self._objects
97
98     def get_object(self, index):
99         return self._objects[index.row()]
100
101     def rowCount(self, parent=QModelIndex()):
102         return len(self._objects)
103
104     def data(self, index, role):
105         if index.isValid():
106             if role == 0:
107                 return self.get_object(index)
108         return None
109
110
111 class Gui(QObject):
112     def __init__(self):
113         QObject.__init__(self)
114         self.itip = ITipParser()
115         self.lines = []
116
117         # Read line names in categorized/sorted order
118         for _, lines in categorize_lines(self.itip.lines):
119             self.lines.extend(lines)
120
121         self.current_line = ''
122         self.current_stations = []
123         self.current_departures = []
124
125     @Slot(int, result=str)
126     def get_direction(self, idx):
127         return self.current_stations[idx][0]
128
129     @Slot(str, str, result='QStringList')
130     def get_stations(self, line, direction):
131         print 'line:', line, 'current line:', self.current_line
132         for dx, stations in self.current_stations:
133             print 'dx:', dx, 'direction:', direction
134             if dx == direction:
135                 return [stationname for stationname, url in stations]
136
137         return ['no stations found']
138
139     directionsLoaded = Signal()
140
141     @Slot(str)
142     def load_directions(self, line):
143         def load_async():
144             stations = sorted(self.itip.get_stations(line).items())
145
146             self.current_line = line
147             self.current_stations = stations
148
149             self.directionsLoaded.emit()
150
151         threading.Thread(target=load_async).start()
152
153     def map_departure(self, dep):
154         dep['lowfloor'] = 1 if dep['lowfloor'] else 0
155         if type(dep['time']) == time:
156             dep['time'] = dep['time'].strftime('%H:%M')
157         return dep
158
159     departuresLoaded = Signal()
160
161     @Slot(str)
162     def load_departures(self, url):
163         def load_async():
164             self.current_departures = map(self.map_departure, \
165                                           self.itip.get_departures(url))
166             #print self.current_departures
167             self.departuresLoaded.emit()
168
169         threading.Thread(target=load_async).start()
170
171     @Slot(str)
172     def load_station_departures(self, station):
173         def load_async():
174             self.current_departures = map(self.map_departure, \
175                                           sort_departures(self.itip.get_departures_by_station(station)))
176             #print self.current_departures
177             self.departuresLoaded.emit()
178
179         threading.Thread(target=load_async).start()
180
181     @Slot(float, float)
182     def load_nearby_departures(self, lat, lon):
183         def load_async():
184             self.current_departures = []
185             try:
186                 stations = get_nearby_stations(lat, lon)
187                 print stations
188                 for station in stations:
189                     print station
190                     try:
191                         self.current_departures += self.itip.get_departures_by_station(station)
192                     except Exception as e:
193                         print e.message
194                 self.current_departures = map(self.map_departure, \
195                                               sort_departures(self.current_departures))
196                 #print self.current_departures
197             except Exception as e:
198                 print e.message
199
200             print 'loaded'
201             self.departuresLoaded.emit()
202
203         threading.Thread(target=load_async).start()
204
205     @Slot(str, str, str, result=str)
206     def get_directions_url(self, line, direction, station):
207         return self.itip.get_url_from_direction(line, direction, station)
208
209     @Slot(result='QStringList')
210     def get_lines(self):
211         return self.lines
212
213     @Slot(result='QVariant')
214     def get_departures(self):
215         return self.current_departures
216
217     @Slot(float, float, result='QStringList')
218     def get_nearby_stations(self, lat, lon):
219         try:
220             return get_nearby_stations(lat, lon)
221         except BaseException as e:
222             # No/wrong stations.db file
223             return []
224
225     @Slot(str, str)
226     def search(self, line, station):
227         line = line.upper()
228         station = station.decode('utf-8')
229         print line, station
230
231         if line not in self.lines:
232             return "Invalid line"
233
234         try:
235             stations = sorted(self.itip.get_stations(line).items())
236             print stations
237             headers, stations = zip(*stations)
238             print headers
239             print stations
240             details = [(direction, name, url) for direction, stops in stations
241                         for name, url in stops if match_station(station, name)]
242             print details
243         except urllib2.URLError as e:
244             print e.message
245             return e.message
246
247 if __name__ == '__main__':
248     app = QApplication(sys.argv)
249
250     view = QDeclarativeView()
251
252     aboutInfo = AboutInfo()
253     config = Config()
254
255     # instantiate the Python object
256     itip = Gui()
257
258     # expose the object to QML
259     context = view.rootContext()
260     context.setContextProperty('itip', itip)
261     context.setContextProperty('aboutInfo', aboutInfo)
262     context.setContextProperty('config', config)
263
264     if os.path.abspath(__file__).startswith('/usr/bin/'):
265         # Assume system-wide installation, QML from /usr/share/
266         view.setSource('/usr/share/gotovienna/qml/main.qml')
267     else:
268         # Assume test from source directory, use relative path
269         view.setSource(os.path.join(os.path.dirname(__file__), 'qml/main.qml'))
270
271     view.showFullScreen()
272
273     sys.exit(app.exec_())
274