source: src/mlx/gui/timetable.py@ 858:1f655516b7ae

Last change on this file since 858:1f655516b7ae was 858:1f655516b7ae, checked in by István Váradi <ivaradi@…>, 7 years ago

The timetable can be queried, displayed and filtered (re #304)

File size: 15.8 KB
Line 
1# Module for handling the time table and booking flights
2
3#-----------------------------------------------------------------------------
4
5from mlx.gui.common import *
6from flightlist import ColumnDescriptor
7
8import mlx.const as const
9
10import datetime
11
12#-----------------------------------------------------------------------------
13
14class Timetable(gtk.Alignment):
15 """The widget for the time table."""
16 def _getVIPRenderer():
17 """Get the renderer for the VIP column."""
18 renderer = gtk.CellRendererToggle()
19 renderer.set_activatable(True)
20 return renderer
21
22 defaultColumnDescriptors = [
23 ColumnDescriptor("callsign", xstr("timetable_no"),
24 sortable = True, defaultSortable = True),
25 ColumnDescriptor("aircraftType", xstr("timetable_type"),
26 sortable = True,
27 convertFn = lambda aircraftType, flight:
28 aircraftNames[aircraftType]),
29 ColumnDescriptor("departureICAO", xstr("timetable_from"),
30 sortable = True),
31 ColumnDescriptor("arrivalICAO", xstr("timetable_to"), sortable = True),
32 ColumnDescriptor("departureTime", xstr("timetable_dep"),
33 sortable = True),
34 ColumnDescriptor("arrivalTime", xstr("timetable_arr"), sortable = True),
35 ColumnDescriptor("duration", xstr("timetable_duration"),
36 sortable = True,
37 convertFn = lambda duration, flight:
38 "%02d:%02d" % (duration/3600,
39 (duration%3600)/60)),
40 ColumnDescriptor("spec", xstr("timetable_vip"), type = bool,
41 renderer = _getVIPRenderer(),
42 sortable = True,
43 convertFn = lambda spec, flight: spec==1)
44 ]
45
46 @staticmethod
47 def isFlightSelected(flight, regularEnabled, vipEnabled, aircraftTypes):
48 """Determine if the given flight is selected by the given
49 filtering conditions."""
50 return ((regularEnabled and flight.spec==0) or \
51 (vipEnabled and flight.spec==1)) and \
52 flight.aircraftType in aircraftTypes
53
54 def __init__(self, columnDescriptors = defaultColumnDescriptors,
55 popupMenuProducer = None):
56 """Construct the time table."""
57 # FIXME: this is very similar to flightlist.FlightList
58 self._columnDescriptors = columnDescriptors
59 self._popupMenuProducer = popupMenuProducer
60 self._popupMenu = None
61
62 types = [int]
63 defaultSortableIndex = None
64 for columnDescriptor in self._columnDescriptors:
65 if columnDescriptor.defaultSortable:
66 defaultSortableIndex = len(types)
67 columnDescriptor.appendType(types)
68
69 self._model = gtk.ListStore(*types)
70 if defaultSortableIndex is not None:
71 sortOrder = SORT_DESCENDING \
72 if self._columnDescriptors[defaultSortableIndex-1]._defaultDescending \
73 else SORT_ASCENDING
74 self._model.set_sort_column_id(defaultSortableIndex, sortOrder)
75 self._view = gtk.TreeView(self._model)
76
77 flightPairIndexColumn = gtk.TreeViewColumn()
78 flightPairIndexColumn.set_visible(False)
79 self._view.append_column(flightPairIndexColumn)
80
81 index = 1
82 for columnDescriptor in self._columnDescriptors:
83 column = columnDescriptor.getViewColumn(index)
84 self._view.append_column(column)
85 index += 1
86
87 self._view.connect("row-activated", self._rowActivated)
88 self._view.connect("button-press-event", self._buttonPressEvent)
89
90 selection = self._view.get_selection()
91 selection.connect("changed", self._selectionChanged)
92
93 scrolledWindow = gtk.ScrolledWindow()
94 scrolledWindow.add(self._view)
95 scrolledWindow.set_size_request(800, -1)
96
97 # FIXME: these should be constants in common.py
98 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
99 else gtk.POLICY_AUTOMATIC,
100 gtk.PolicyType.AUTOMATIC if pygobject
101 else gtk.POLICY_AUTOMATIC)
102 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
103 else gtk.SHADOW_IN)
104
105 super(Timetable, self).__init__(xalign = 0.5, yalign = 0.0,
106 xscale = 0.0, yscale = 1.0)
107 self.add(scrolledWindow)
108
109 self._flightPairs = []
110
111 @property
112 def hasFlightPairs(self):
113 """Determine if the timetable contains any flights."""
114 return len(self._flightPairs)>0
115
116 def clear(self):
117 """Clear the flight pairs."""
118 self._model.clear()
119 self._flightPairs = []
120
121 def setFlightPairs(self, flightPairs):
122 """Setup the table contents from the given list of
123 rpc.ScheduledFlightPair objects."""
124 self.clear()
125
126 self._flightPairs = flightPairs
127
128 def updateList(self, regularEnabled, vipEnabled, types):
129 """Update the actual list according to the given filter values."""
130 index = 0
131 self._model.clear()
132 for flightPair in self._flightPairs:
133 flight = flightPair.flight0
134 if Timetable.isFlightSelected(flight, regularEnabled, vipEnabled,
135 types):
136 values = [index]
137 for columnDescriptor in self._columnDescriptors:
138 values.append(columnDescriptor.getValueFrom(flight))
139 self._model.append(values)
140 index += 1
141
142 def _rowActivated(self, flightList, path, column):
143 """Called when a row is selected."""
144 print "_rowActivated"
145
146 def _buttonPressEvent(self, widget, event):
147 """Called when a mouse button is pressed or released."""
148 print "_buttonPressEvent", event
149
150 def _selectionChanged(self, selection):
151 """Called when the selection has changed."""
152 print "_selectionChanged"
153
154#-----------------------------------------------------------------------------
155
156class CalendarWindow(gtk.Window):
157 """A window for a calendar."""
158 def __init__(self):
159 """Construct the window."""
160 super(CalendarWindow, self).__init__()
161
162 self.set_decorated(False)
163 self.set_modal(True)
164 self.connect("key-press-event", self._keyPressed)
165
166 mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
167 xscale = 1.0, yscale = 1.0)
168 #mainAlignment.set_padding(padding_top = 0, padding_bottom = 12,
169 # padding_left = 8, padding_right = 8)
170
171 self._calendar = gtk.Calendar()
172 self._calendar.connect("day-selected-double-click", self._daySelectedDoubleClick)
173 mainAlignment.add(self._calendar)
174
175 self.add(mainAlignment)
176
177 def setDate(self, date):
178 """Set the current date to the given one."""
179 self._calendar.select_month(date.month-1, date.year)
180 self._calendar.select_day(date.day)
181
182 def getDate(self):
183 """Get the currently selected date."""
184 (year, monthM1, day) = self._calendar.get_date()
185 return datetime.date(year, monthM1+1, day)
186
187 def _daySelectedDoubleClick(self, calendar):
188 """Called when a date is double clicked."""
189 self.emit("date-selected")
190
191 def _keyPressed(self, window, event):
192 """Called when a key is pressed in the window.
193
194 If the Escape key is pressed, 'delete-event' is emitted to close the
195 window."""
196 keyName = gdk.keyval_name(event.keyval)
197 if keyName =="Escape":
198 self.emit("delete-event", None)
199 return True
200 elif keyName =="Return":
201 self.emit("date-selected")
202 return True
203
204gobject.signal_new("date-selected", CalendarWindow, gobject.SIGNAL_RUN_FIRST,
205 None, ())
206
207#-----------------------------------------------------------------------------
208
209class TimetableWindow(gtk.Window):
210 """The window to display the timetable."""
211 typeFamilies = [
212 const.AIRCRAFT_FAMILY_B737NG,
213 const.AIRCRAFT_FAMILY_DH8D,
214 const.AIRCRAFT_FAMILY_B767,
215
216 const.AIRCRAFT_FAMILY_B737CL,
217 const.AIRCRAFT_FAMILY_CRJ2,
218 const.AIRCRAFT_FAMILY_F70,
219
220 const.AIRCRAFT_FAMILY_T134,
221 const.AIRCRAFT_FAMILY_T154
222 ]
223
224 def __init__(self, gui):
225 super(TimetableWindow, self).__init__()
226
227 self._gui = gui
228 self.set_title(WINDOW_TITLE_BASE + " - " + xstr("timetable_title"))
229 self.set_size_request(-1, 600)
230 self.set_transient_for(gui.mainWindow)
231 self.set_modal(True)
232 self.connect("key-press-event", self._keyPressed)
233
234 mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
235 xscale = 1.0, yscale = 1.0)
236 mainAlignment.set_padding(padding_top = 0, padding_bottom = 12,
237 padding_left = 8, padding_right = 8)
238
239 vbox = gtk.VBox()
240
241 filterAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
242 xscale = 1.0, yscale = 1.0)
243
244 filterFrame = gtk.Frame(xstr("timetable_filter"))
245
246 filterVBox = gtk.VBox()
247
248 topAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
249 xscale = 0.0, yscale = 0.0)
250 topHBox = gtk.HBox()
251
252 label = gtk.Label(xstr("timetable_flightdate"))
253 label.set_use_underline(True)
254 topHBox.pack_start(label, False, False, 4)
255
256 self._flightDate = gtk.Button()
257 self._flightDate.connect("clicked", self._flightDateClicked)
258 self._flightDate.set_tooltip_text(xstr("timetable_flightdate_tooltip"))
259 label.set_mnemonic_widget(self._flightDate)
260 topHBox.pack_start(self._flightDate, False, False, 4)
261
262 filler = gtk.Alignment()
263 filler.set_size_request(48, 2)
264 topHBox.pack_start(filler, False, True, 0)
265
266 self._regularFlights = gtk.CheckButton(xstr("timetable_show_regular"))
267 self._regularFlights.set_use_underline(True)
268 self._regularFlights.set_tooltip_text(xstr("timetable_show_regular_tooltip"))
269 self._regularFlights.set_active(True)
270 self._regularFlights.connect("toggled", self._filterChanged)
271 topHBox.pack_start(self._regularFlights, False, False, 8)
272
273 self._vipFlights = gtk.CheckButton(xstr("timetable_show_vip"))
274 self._vipFlights.set_use_underline(True)
275 self._vipFlights.set_tooltip_text(xstr("timetable_show_vip_tooltip"))
276 self._vipFlights.set_active(True)
277 self._vipFlights.connect("toggled", self._filterChanged)
278 topHBox.pack_start(self._vipFlights, False, False, 8)
279
280 topAlignment.add(topHBox)
281
282 filterVBox.pack_start(topAlignment, False, False, 4)
283
284 separator = gtk.HSeparator()
285 filterVBox.pack_start(separator, False, False, 4)
286
287 typeAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
288 xscale = 0.0, yscale = 0.0)
289
290 numColumns = 4
291 numRows = (len(TimetableWindow.typeFamilies)+numColumns-1)/numColumns
292
293 typeTable = gtk.Table(numRows, numColumns)
294 typeTable.set_col_spacings(8)
295 row = 0
296 column = 0
297 self._typeFamilyButtons = {}
298 for typeFamily in TimetableWindow.typeFamilies:
299 checkButton = gtk.CheckButton(aircraftFamilyNames[typeFamily])
300 checkButton.set_active(True)
301 checkButton.connect("toggled", self._filterChanged)
302 self._typeFamilyButtons[typeFamily] = checkButton
303
304 typeTable.attach(checkButton, column, column + 1, row, row+1)
305
306 column += 1
307 if column>=numColumns:
308 row += 1
309 column = 0
310
311 typeAlignment.add(typeTable)
312 filterVBox.pack_start(typeAlignment, False, False, 4)
313
314 filterFrame.add(filterVBox)
315
316 filterAlignment.add(filterFrame)
317 vbox.pack_start(filterAlignment, False, False, 2)
318
319 self._timetable = Timetable()
320 vbox.pack_start(self._timetable, True, True, 2)
321
322 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
323 xscale = 0.0, yscale = 0.0)
324 self._closeButton = gtk.Button(xstr("button_ok"))
325 self._closeButton.connect("clicked", self._closeClicked)
326 alignment.add(self._closeButton)
327 vbox.pack_start(alignment, False, False, 2)
328
329 mainAlignment.add(vbox)
330
331 self._calendarWindow = calendarWindow = CalendarWindow()
332 calendarWindow.set_transient_for(self)
333 calendarWindow.connect("delete-event", self._calendarWindowDeleted)
334 calendarWindow.connect("date-selected", self._calendarWindowDateSelected)
335
336 self.add(mainAlignment)
337
338 self.setDate(datetime.date.today())
339
340 @property
341 def hasFlightPairs(self):
342 """Determine if there any flight pairs displayed in the window."""
343 return self._timetable.hasFlightPairs
344
345 @property
346 def isRegularEnabled(self):
347 """Determine if regular flights are enabled."""
348 return self._regularFlights.get_active()!=0
349
350 @property
351 def isVIPEnabled(self):
352 """Determine if VIP flights are enabled."""
353 return self._vipFlights.get_active()!=0
354
355 def setTypes(self, aircraftTypes):
356 """Enable/disable the type family checkboxes according to the given
357 list of types."""
358 typeFamilies = set()
359 for aircraftType in aircraftTypes:
360 typeFamilies.add(const.aircraftType2Family(aircraftType))
361
362 for (typeFamily, checkButton) in self._typeFamilyButtons.iteritems():
363 checkButton.set_sensitive(typeFamily in typeFamilies)
364
365 def clear(self):
366 """Clear all flight pairs."""
367 self._timetable.clear()
368
369 def setFlightPairs(self, flightPairs):
370 """Set the flight pairs."""
371 self._timetable.setFlightPairs(flightPairs)
372 self._updateList()
373
374 def setDate(self, date):
375 """Set the date to the given one."""
376 self._flightDate.set_label(date.strftime("%Y-%m-%d"))
377 self._calendarWindow.setDate(date)
378
379 def _closeClicked(self, button):
380 """Called when the Close button is clicked.
381
382 A 'delete-event' is emitted to close the window."""
383 self.emit("delete-event", None)
384
385 def _flightDateClicked(self, button):
386 """Called when the flight date button is clicked."""
387 self._calendarWindow.set_position(gtk.WIN_POS_MOUSE)
388 self.set_focus(self._calendarWindow)
389 self._calendarWindow.show_all()
390
391 def _calendarWindowDeleted(self, window, event):
392 """Called when the flight date window is deleted."""
393 self._calendarWindow.hide()
394
395 def _calendarWindowDateSelected(self, window):
396 """Called when the flight date window is deleted."""
397 self._calendarWindow.hide()
398 date = window.getDate()
399 self._flightDate.set_label(date.strftime("%Y-%m-%d"))
400 self._gui.updateTimeTable(date)
401
402 def _filterChanged(self, checkButton):
403 """Called when the filter conditions have changed."""
404 self._updateList()
405
406 def _keyPressed(self, window, event):
407 """Called when a key is pressed in the window.
408
409 If the Escape key is pressed, 'delete-event' is emitted to close the
410 window."""
411 if gdk.keyval_name(event.keyval) == "Escape":
412 self.emit("delete-event", None)
413 return True
414
415 def _updateList(self):
416 """Update the timetable list."""
417 aircraftTypes = []
418 for (aircraftFamily, button) in self._typeFamilyButtons.iteritems():
419 if button.get_active():
420 aircraftTypes += const.aircraftFamily2Types[aircraftFamily]
421
422 self._timetable.updateList(self.isRegularEnabled,
423 self.isVIPEnabled,
424 aircraftTypes)
Note: See TracBrowser for help on using the repository browser.