experimental gps support
[pywienerlinien] / gotovienna-qml
1 #!/usr/env/python
2
3 """Public transport information for Vienna"""
4
5 __author__ = 'kelvan <kelvan@logic.at>'
6 __version__ = '0.8.2'
7 __website__ = 'https://github.com/kelvan/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
20 import urllib2
21 import os
22 import sys
23 import threading
24 from datetime import time
25
26 class GotoViennaListModel(QAbstractListModel):
27     def __init__(self, objects=None):
28         QAbstractListModel.__init__(self)
29         if objects is None:
30             objects = []
31         self._objects = objects
32         self.setRoleNames({0: 'modelData'})
33
34     def set_objects(self, objects):
35         self._objects = objects
36
37     def get_objects(self):
38         return self._objects
39
40     def get_object(self, index):
41         return self._objects[index.row()]
42
43     def rowCount(self, parent=QModelIndex()):
44         return len(self._objects)
45
46     def data(self, index, role):
47         if index.isValid():
48             if role == 0:
49                 return self.get_object(index)
50         return None
51
52
53 class Gui(QObject):
54     def __init__(self):
55         QObject.__init__(self)
56         self.itip = ITipParser()
57         self.lines = []
58
59         # Read line names in categorized/sorted order
60         for _, lines in categorize_lines(self.itip.lines):
61             self.lines.extend(lines)
62
63         self.current_line = ''
64         self.current_stations = []
65         self.current_departures = []
66
67     @Slot(int, result=str)
68     def get_direction(self, idx):
69         return self.current_stations[idx][0]
70
71     @Slot(str, str, result='QStringList')
72     def get_stations(self, line, direction):
73         print 'line:', line, 'current line:', self.current_line
74         for dx, stations in self.current_stations:
75             print 'dx:', dx, 'direction:', direction
76             if dx == direction:
77                 return [stationname for stationname, url in stations]
78
79         return ['no stations found']
80
81     directionsLoaded = Signal()
82
83     @Slot(str)
84     def load_directions(self, line):
85         def load_async():
86             stations = sorted(self.itip.get_stations(line).items())
87
88             self.current_line = line
89             self.current_stations = stations
90
91             self.directionsLoaded.emit()
92
93         threading.Thread(target=load_async).start()
94
95     def map_departure(self, dep):
96         dep['lowfloor'] = 1 if dep['lowfloor'] else 0
97         if type(dep['time']) == time:
98             dep['time'] = dep['time'].strftime('%H:%M')
99         return dep
100
101     departuresLoaded = Signal()
102
103     @Slot(str)
104     def load_departures(self, url):
105         def load_async():
106             self.current_departures = map(self.map_departure, self.itip.get_departures(url))
107             print self.current_departures
108             self.departuresLoaded.emit()
109
110         threading.Thread(target=load_async).start()
111         
112     @Slot(str)
113     def load_station_departures(self, station):
114         def load_async():
115             self.current_departures = map(self.map_departure, self.itip.get_departures_by_station(station))
116             print self.current_departures
117             self.departuresLoaded.emit()
118
119         threading.Thread(target=load_async).start()
120         
121     @Slot(float, float)
122     def load_nearby_departures(self, lat, lon):
123         def load_async():
124             self.current_departures = []
125             try:
126                 stations = get_nearby_stations(lat, lon)
127                 print stations
128                 for station in stations:
129                     print station
130                     try:
131                         self.current_departures += map(self.map_departure, self.itip.get_departures_by_station(station))
132                     except Exception as e:
133                         print e.message
134                 print self.current_departures
135             except Exception as e:
136                 print e.message
137                 
138             print 'loaded'
139             self.departuresLoaded.emit()
140
141         threading.Thread(target=load_async).start()
142
143     @Slot(str, str, str, result=str)
144     def get_directions_url(self, line, direction, station):
145         return self.itip.get_url_from_direction(line, direction, station)
146
147     @Slot(result='QStringList')
148     def get_lines(self):
149         return self.lines
150
151     @Slot(result='QVariant')
152     def get_departures(self):
153         return self.current_departures
154
155     @Slot(float, float, result='QStringList')
156     def get_nearby_stations(self, lat, lon):
157         try:
158             return get_nearby_stations(lat, lon)
159         except Exception as e:
160             print e.message
161             return []
162
163     @Slot(str, str)
164     def search(self, line, station):
165         line = line.upper()
166         station = station.decode('utf-8')
167         print line, station
168
169         if line not in self.lines:
170             return "Invalid line"
171
172         try:
173             stations = sorted(self.itip.get_stations(line).items())
174             print stations
175             headers, stations = zip(*stations)
176             print headers
177             print stations
178             details = [(direction, name, url) for direction, stops in stations
179                         for name, url in stops if match_station(station, name)]
180             print details
181         except urllib2.URLError as e:
182             print e.message
183             return e.message
184
185 if __name__ == '__main__':
186     app = QApplication(sys.argv)
187
188     view = QDeclarativeView()
189
190     # instantiate the Python object
191     itip = Gui()
192
193     # expose the object to QML
194     context = view.rootContext()
195     context.setContextProperty('itip', itip)
196
197     if os.path.abspath(__file__).startswith('/usr/bin/'):
198         # Assume system-wide installation, QML from /usr/share/
199         view.setSource('/usr/share/gotovienna/qml/main.qml')
200     else:
201         # Assume test from source directory, use relative path
202         view.setSource(os.path.join(os.path.dirname(__file__), 'qml/main.qml'))
203
204     view.showFullScreen()
205
206     sys.exit(app.exec_())
207