Implement LinePad for easy line selection
[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__ = '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, \
107                                           self.itip.get_departures(url))
108             #print self.current_departures
109             self.departuresLoaded.emit()
110
111         threading.Thread(target=load_async).start()
112
113     @Slot(str)
114     def load_station_departures(self, station):
115         def load_async():
116             self.current_departures = map(self.map_departure, \
117                                           sort_departures(self.itip.get_departures_by_station(station)))
118             #print self.current_departures
119             self.departuresLoaded.emit()
120
121         threading.Thread(target=load_async).start()
122
123     @Slot(float, float)
124     def load_nearby_departures(self, lat, lon):
125         def load_async():
126             self.current_departures = []
127             try:
128                 stations = get_nearby_stations(lat, lon)
129                 print stations
130                 for station in stations:
131                     print station
132                     try:
133                         self.current_departures += self.itip.get_departures_by_station(station)
134                     except Exception as e:
135                         print e.message
136                 self.current_departures = map(self.map_departure, \
137                                               sort_departures(self.current_departures))
138                 #print self.current_departures
139             except Exception as e:
140                 print e.message
141
142             print 'loaded'
143             self.departuresLoaded.emit()
144
145         threading.Thread(target=load_async).start()
146
147     @Slot(str, str, str, result=str)
148     def get_directions_url(self, line, direction, station):
149         return self.itip.get_url_from_direction(line, direction, station)
150
151     @Slot(result='QStringList')
152     def get_lines(self):
153         return self.lines
154
155     @Slot(result='QVariant')
156     def get_departures(self):
157         return self.current_departures
158
159     @Slot(float, float, result='QStringList')
160     def get_nearby_stations(self, lat, lon):
161         try:
162             return get_nearby_stations(lat, lon)
163         except Exception as e:
164             print e.message
165             return []
166
167     @Slot(str, str)
168     def search(self, line, station):
169         line = line.upper()
170         station = station.decode('utf-8')
171         print line, station
172
173         if line not in self.lines:
174             return "Invalid line"
175
176         try:
177             stations = sorted(self.itip.get_stations(line).items())
178             print stations
179             headers, stations = zip(*stations)
180             print headers
181             print stations
182             details = [(direction, name, url) for direction, stops in stations
183                         for name, url in stops if match_station(station, name)]
184             print details
185         except urllib2.URLError as e:
186             print e.message
187             return e.message
188
189 if __name__ == '__main__':
190     app = QApplication(sys.argv)
191
192     view = QDeclarativeView()
193
194     # instantiate the Python object
195     itip = Gui()
196
197     # expose the object to QML
198     context = view.rootContext()
199     context.setContextProperty('itip', itip)
200
201     if os.path.abspath(__file__).startswith('/usr/bin/'):
202         # Assume system-wide installation, QML from /usr/share/
203         view.setSource('/usr/share/gotovienna/qml/main.qml')
204     else:
205         # Assume test from source directory, use relative path
206         view.setSource(os.path.join(os.path.dirname(__file__), 'qml/main.qml'))
207
208     view.showFullScreen()
209
210     sys.exit(app.exec_())
211