source: src/mlx/gui/flightlist.py@ 854:3e7dca86c1ed

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

The accepted flights can be queried and viewed (re #307)

File size: 25.7 KB
Line 
1# A widget which is a generic list of flights
2
3#-----------------------------------------------------------------------------
4
5from mlx.gui.common import *
6
7import mlx.const as const
8
9#-----------------------------------------------------------------------------
10
11class ColumnDescriptor(object):
12 """A descriptor for a column in the list."""
13 def __init__(self, attribute, heading, type = str,
14 convertFn = None, renderer = None,
15 extraColumnAttributes = None, sortable = False,
16 defaultSortable = False, defaultDescending = False,
17 cellDataFn = None):
18 """Construct the descriptor."""
19 self._attribute = attribute
20 self._heading = heading
21 self._type = type
22 self._convertFn = convertFn
23 self._renderer = \
24 gtk.CellRendererText() if renderer is None else renderer
25 self._extraColumnAttributes = extraColumnAttributes
26 self._sortable = sortable
27 self._defaultSortable = defaultSortable
28 self._defaultDescending = defaultDescending
29 self._cellDataFn = cellDataFn
30
31 @property
32 def defaultSortable(self):
33 """Determine if this column is the default sortable one."""
34 return self._defaultSortable
35
36 def appendType(self, types):
37 """Append the type of this column to the given list of types."""
38 types.append(self._type)
39
40 def getViewColumn(self, index):
41 """Get a new column object for a tree view.
42
43 @param index is the 0-based index of the column."""
44 column = gtk.TreeViewColumn(self._heading, self._renderer,
45 text = index)
46 column.set_expand(True)
47 if self._sortable:
48 column.set_sort_column_id(index)
49 column.set_sort_indicator(True)
50
51 if self._extraColumnAttributes is not None:
52 for (key, value) in self._extraColumnAttributes.iteritems():
53 if key=="alignment":
54 self._renderer.set_alignment(value, 0.5)
55 else:
56 raise Exception("unhandled extra column attribute '" +
57 key + "'")
58 if self._cellDataFn is not None:
59 column.set_cell_data_func(self._renderer, self._cellDataFn)
60
61 return column
62
63 def getValueFrom(self, flight):
64 """Get the value from the given flight."""
65 attributes = self._attribute.split(".")
66 value = getattr(flight, attributes[0])
67 for attr in attributes[1:]:
68 value = getattr(value, attr)
69 return self._type(value) if self._convertFn is None \
70 else self._convertFn(value, flight)
71
72#-----------------------------------------------------------------------------
73
74class FlightList(gtk.Alignment):
75 """Construct the flight list.
76
77 This is a complete widget with a scroll window. It is alignment centered
78 horizontally and expandable vertically."""
79
80 defaultColumnDescriptors = [
81 ColumnDescriptor("callsign", xstr("flightsel_no")),
82 ColumnDescriptor("departureTime", xstr("flightsel_deptime"),
83 sortable = True, defaultSortable = True),
84 ColumnDescriptor("departureICAO", xstr("flightsel_from"),
85 sortable = True),
86 ColumnDescriptor("arrivalICAO", xstr("flightsel_to"), sortable = True)
87 ]
88
89 def __init__(self, columnDescriptors = defaultColumnDescriptors,
90 popupMenuProducer = None, widthRequest = None,
91 multiSelection = False):
92 """Construct the flight list with the given column descriptors."""
93
94 self._columnDescriptors = columnDescriptors
95 self._popupMenuProducer = popupMenuProducer
96 self._popupMenu = None
97
98 types = [int]
99 defaultSortableIndex = None
100 for columnDescriptor in self._columnDescriptors:
101 if columnDescriptor.defaultSortable:
102 defaultSortableIndex = len(types)
103 columnDescriptor.appendType(types)
104
105 self._model = gtk.ListStore(*types)
106 if defaultSortableIndex is not None:
107 sortOrder = SORT_DESCENDING \
108 if self._columnDescriptors[defaultSortableIndex-1]._defaultDescending \
109 else SORT_ASCENDING
110 self._model.set_sort_column_id(defaultSortableIndex, sortOrder)
111 self._view = gtk.TreeView(self._model)
112
113 flightIndexColumn = gtk.TreeViewColumn()
114 flightIndexColumn.set_visible(False)
115 self._view.append_column(flightIndexColumn)
116
117 index = 1
118 for columnDescriptor in self._columnDescriptors:
119 column = columnDescriptor.getViewColumn(index)
120 self._view.append_column(column)
121 index += 1
122
123 self._view.connect("row-activated", self._rowActivated)
124 self._view.connect("button-press-event", self._buttonPressEvent)
125
126 selection = self._view.get_selection()
127 selection.connect("changed", self._selectionChanged)
128 if multiSelection:
129 selection.set_mode(SELECTION_MULTIPLE)
130
131 scrolledWindow = gtk.ScrolledWindow()
132 scrolledWindow.add(self._view)
133 if widthRequest is not None:
134 scrolledWindow.set_size_request(widthRequest, -1)
135 # FIXME: these should be constants in common.py
136 scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
137 else gtk.POLICY_AUTOMATIC,
138 gtk.PolicyType.AUTOMATIC if pygobject
139 else gtk.POLICY_AUTOMATIC)
140 scrolledWindow.set_shadow_type(gtk.ShadowType.IN if pygobject
141 else gtk.SHADOW_IN)
142
143 super(FlightList, self).__init__(xalign = 0.5, yalign = 0.0,
144 xscale = 0.0, yscale = 1.0)
145 self.add(scrolledWindow)
146
147 @property
148 def selectedIndexes(self):
149 """Get the indexes of the selected entries, if any.
150
151 The indexes are sorted."""
152 selection = self._view.get_selection()
153 (model, rows) = selection.get_selected_rows()
154
155 indexes = [self._getIndexForPath(path) for path in rows]
156 indexes.sort()
157 return indexes
158
159 @property
160 def hasFlights(self):
161 """Determine if there are any flights in the list."""
162 return self._model.get_iter_root() is not None
163
164 def clear(self):
165 """Clear the model."""
166 self._model.clear()
167
168 def addFlight(self, flight):
169 """Add the given booked flight."""
170 values = [self._model.iter_n_children(None)]
171 for columnDescriptor in self._columnDescriptors:
172 values.append(columnDescriptor.getValueFrom(flight))
173 self._model.append(values)
174
175 def removeFlights(self, indexes):
176 """Remove the flights with the given indexes."""
177 model = self._model
178 idx = 0
179 iter = model.get_iter_first()
180 while iter is not None:
181 nextIter = model.iter_next(iter)
182 if model.get_value(iter, 0) in indexes:
183 model.remove(iter)
184 else:
185 model.set_value(iter, 0, idx)
186 idx += 1
187 iter = nextIter
188
189 def _getIndexForPath(self, path):
190 """Get the index for the given path."""
191 iter = self._model.get_iter(path)
192 return self._model.get_value(iter, 0)
193
194 def _rowActivated(self, flightList, path, column):
195 """Called when a row is selected."""
196 self.emit("row-activated", self._getIndexForPath(path))
197
198 def _buttonPressEvent(self, widget, event):
199 """Called when a mouse button is pressed or released."""
200 if event.type!=EVENT_BUTTON_PRESS or event.button!=3 or \
201 self._popupMenuProducer is None:
202 return
203
204 (path, _, _, _) = self._view.get_path_at_pos(int(event.x),
205 int(event.y))
206 selection = self._view.get_selection()
207 selection.unselect_all()
208 selection.select_path(path)
209
210 if self._popupMenu is None:
211 self._popupMenu = self._popupMenuProducer()
212 menu = self._popupMenu
213 if pygobject:
214 menu.popup(None, None, None, None, event.button, event.time)
215 else:
216 menu.popup(None, None, None, event.button, event.time)
217
218 def _selectionChanged(self, selection):
219 """Called when the selection has changed."""
220 self.emit("selection-changed", self.selectedIndexes)
221
222#-------------------------------------------------------------------------------
223
224gobject.signal_new("row-activated", FlightList, gobject.SIGNAL_RUN_FIRST,
225 None, (int,))
226
227gobject.signal_new("selection-changed", FlightList, gobject.SIGNAL_RUN_FIRST,
228 None, (object,))
229
230#-----------------------------------------------------------------------------
231
232class PendingFlightsFrame(gtk.Frame):
233 """A frame for a list of pending (reported or rejected) flights.
234
235 It contains the list and the buttons available."""
236 @staticmethod
237 def getAircraft(tailNumber, bookedFlight):
238 """Get the aircraft from the given booked flight.
239
240 This is the tail number followed by the ICAO code of the aircraft's
241 type."""
242 return tailNumber + \
243 " (" + const.icaoCodes[bookedFlight.aircraftType] + ")"
244
245 def _getAcft(tailNumber, bookedFlight):
246 return PendingFlightsFrame.getAircraft(tailNumber, bookedFlight)
247
248 columnDescriptors = [
249 ColumnDescriptor("callsign", xstr("flightsel_no")),
250 ColumnDescriptor("departureTime", xstr("flightsel_deptime"),
251 sortable = True, defaultSortable = True),
252 ColumnDescriptor("departureICAO", xstr("flightsel_from"),
253 sortable = True),
254 ColumnDescriptor("arrivalICAO", xstr("flightsel_to"),
255 sortable = True),
256 ColumnDescriptor("tailNumber", xstr("pendflt_acft"),
257 convertFn = _getAcft)
258 ]
259
260 def __init__(self, which, wizard, window, pirepEditable = False):
261 """Construct the frame with the given title."""
262 super(PendingFlightsFrame, self).__init__(xstr("pendflt_title_" + which))
263
264 self._which = which
265 self._wizard = wizard
266 self._window = window
267 self._pirepEditable = pirepEditable
268
269 alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
270 alignment.set_padding(padding_top = 2, padding_bottom = 8,
271 padding_left = 4, padding_right = 4)
272
273 hbox = gtk.HBox()
274
275 self._flights = []
276 self._flightList = FlightList(columnDescriptors =
277 PendingFlightsFrame.columnDescriptors,
278 widthRequest = 500, multiSelection = True)
279 self._flightList.connect("selection-changed", self._selectionChanged)
280
281 hbox.pack_start(self._flightList, True, True, 4)
282
283 buttonBox = gtk.VBox()
284
285 self._editButton = gtk.Button(xstr("pendflt_" +
286 ("edit" if pirepEditable else
287 "view") + "_" + which))
288 self._editButton.set_sensitive(False)
289 self._editButton.connect("clicked", self._editClicked)
290 buttonBox.pack_start(self._editButton, False, False, 2)
291
292 self._reflyButton = gtk.Button(xstr("pendflt_refly_" + which))
293 self._reflyButton.set_sensitive(False)
294 self._reflyButton.connect("clicked", self._reflyClicked)
295 buttonBox.pack_start(self._reflyButton, False, False, 2)
296
297 self._deleteButton = gtk.Button(xstr("pendflt_delete_" + which))
298 self._deleteButton.set_sensitive(False)
299 self._deleteButton.connect("clicked", self._deleteClicked)
300 buttonBox.pack_start(self._deleteButton, False, False, 2)
301
302 hbox.pack_start(buttonBox, False, False, 4)
303
304 alignment.add(hbox)
305 self.add(alignment)
306
307 @property
308 def hasFlights(self):
309 """Determine if there are any flights in the list."""
310 return self._flightList.hasFlights
311
312 def clear(self):
313 """Clear the lists."""
314 self._flights = []
315 self._flightList.clear()
316
317 def addFlight(self, flight):
318 """Add a flight to the list."""
319 self._flights.append(flight)
320 self._flightList.addFlight(flight)
321
322 def _selectionChanged(self, flightList, selectedIndexes):
323 """Called when the selection in the list has changed."""
324 self._editButton.set_sensitive(len(selectedIndexes)==1)
325 self._reflyButton.set_sensitive(len(selectedIndexes)>0)
326 self._deleteButton.set_sensitive(len(selectedIndexes)>0)
327
328 def _editClicked(self, button):
329 """Called when the Edit button is clicked."""
330 gui = self._wizard.gui
331 gui.beginBusy(xstr("pendflt_pirep_busy"))
332 self.set_sensitive(False)
333
334 indexes = self._flightList.selectedIndexes
335 assert(len(indexes)==1)
336
337 flightID = self._flights[indexes[0]].id
338 gui.webHandler.getPIREP(self._pirepResultCallback, flightID)
339
340 def _pirepResultCallback(self, returned, result):
341 """Called when the PIREP query result is available."""
342 gobject.idle_add(self._handlePIREPResult, returned, result)
343
344 def _handlePIREPResult(self, returned, result):
345 """Handle the refly result."""
346
347 self.set_sensitive(True)
348 gui = self._wizard.gui
349 gui.endBusy()
350
351 if returned:
352 if self._pirepEditable:
353 gui.editPIREP(result.pirep)
354 else:
355 gui.viewPIREP(result.pirep)
356
357 def _reflyClicked(self, button):
358 """Called when the Refly button is clicked."""
359 if askYesNo(xstr("pendflt_refly_question"), parent = self._window):
360 gui = self._wizard.gui
361 gui.beginBusy(xstr("pendflt_refly_busy"))
362 self.set_sensitive(False)
363
364 flightIDs = [self._flights[i].id
365 for i in self._flightList.selectedIndexes]
366 gui.webHandler.reflyFlights(self._reflyResultCallback, flightIDs)
367
368 def _reflyResultCallback(self, returned, result):
369 """Called when the refly result is available."""
370 gobject.idle_add(self._handleReflyResult, returned, result)
371
372 def _handleReflyResult(self, returned, result):
373 """Handle the refly result."""
374
375 self.set_sensitive(True)
376 gui = self._wizard.gui
377 gui.endBusy()
378
379 print "PendingFlightsFrame._handleReflyResult", returned, result
380
381 if returned:
382 indexes = self._flightList.selectedIndexes
383
384 flights = [self._flights[index] for index in indexes]
385
386 self._flightList.removeFlights(indexes)
387 for index in indexes[::-1]:
388 del self._flights[index]
389
390 for flight in flights:
391 self._wizard.reflyFlight(flight)
392 self._window.checkFlights()
393 else:
394 communicationErrorDialog()
395
396 def _deleteClicked(self, button):
397 """Called when the Delete button is clicked."""
398 if askYesNo(xstr("flight_delete_question"), parent = self._window):
399 gui = self._wizard.gui
400 gui.beginBusy(xstr("pendflt_refly_busy"))
401 self.set_sensitive(False)
402
403 flightIDs = [self._flights[i].id
404 for i in self._flightList.selectedIndexes]
405 gui.webHandler.deleteFlights(self._deleteResultCallback, flightIDs)
406
407 def _deleteResultCallback(self, returned, result):
408 """Called when the deletion result is available."""
409 gobject.idle_add(self._handleDeleteResult, returned, result)
410
411 def _handleDeleteResult(self, returned, result):
412 """Handle the delete result."""
413
414 self.set_sensitive(True)
415 gui = self._wizard.gui
416 gui.endBusy()
417
418 print "PendingFlightsFrame._handleDeleteResult", returned, result
419
420 if returned:
421 indexes = self._flightList.selectedIndexes
422
423 flights = [self._flights[index] for index in indexes]
424
425 self._flightList.removeFlights(indexes)
426 for index in indexes[::-1]:
427 del self._flights[index]
428
429 for flight in flights:
430 self._wizard.deleteFlight(flight)
431 self._window.checkFlights()
432 else:
433 communicationErrorDialog()
434
435#-----------------------------------------------------------------------------
436
437class PendingFlightsWindow(gtk.Window):
438 """The window to display the lists of the pending (reported or rejected)
439 flights."""
440 def __init__(self, wizard):
441 """Construct the window"""
442 super(PendingFlightsWindow, self).__init__()
443
444 gui = wizard.gui
445
446 self.set_title(WINDOW_TITLE_BASE + " - " + xstr("pendflt_title"))
447 self.set_size_request(-1, 450)
448 self.set_transient_for(gui.mainWindow)
449 self.set_modal(True)
450
451 mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
452 xscale = 1.0, yscale = 1.0)
453 mainAlignment.set_padding(padding_top = 0, padding_bottom = 12,
454 padding_left = 8, padding_right = 8)
455
456 vbox = gtk.VBox()
457
458 self._reportedFrame = PendingFlightsFrame("reported", wizard, self,
459 True)
460 vbox.pack_start(self._reportedFrame, True, True, 2)
461
462 self._rejectedFrame = PendingFlightsFrame("rejected", wizard, self)
463 vbox.pack_start(self._rejectedFrame, True, True, 2)
464
465 alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
466 xscale = 0.0, yscale = 0.0)
467 self._closeButton = gtk.Button(xstr("button_ok"))
468 self._closeButton.connect("clicked", self._closeClicked)
469 alignment.add(self._closeButton)
470 vbox.pack_start(alignment, False, False, 2)
471
472 mainAlignment.add(vbox)
473
474 self.add(mainAlignment)
475
476 self.connect("key-press-event", self._keyPressed)
477
478 @property
479 def hasFlights(self):
480 """Determine if the window has any flights."""
481 return self._reportedFrame.hasFlights or self._rejectedFrame.hasFlights
482
483 def clear(self):
484 """Clear the lists."""
485 self._reportedFrame.clear()
486 self._rejectedFrame.clear()
487
488 def addReportedFlight(self, flight):
489 """Add a reported flight."""
490 self._reportedFrame.addFlight(flight)
491
492 def addRejectedFlight(self, flight):
493 """Add a rejected flight."""
494 self._rejectedFrame.addFlight(flight)
495
496 def checkFlights(self):
497 """Check if there are any flights in any of the lists, and close the
498 window if not."""
499 if not self.hasFlights:
500 self.emit("delete-event", None)
501
502 def _closeClicked(self, button):
503 """Called when the Close button is clicked.
504
505 A 'delete-event' is emitted to close the window."""
506 self.emit("delete-event", None)
507
508 def _keyPressed(self, window, event):
509 """Called when a key is pressed in the window.
510
511 If the Escape key is pressed, 'delete-event' is emitted to close the
512 window."""
513 if gdk.keyval_name(event.keyval) == "Escape":
514 self.emit("delete-event", None)
515 return True
516
517#-----------------------------------------------------------------------------
518
519class AcceptedFlightsWindow(gtk.Window):
520 """A window for a list of accepted flights."""
521 def getFlightDuration(flightTimeStart, flight):
522 """Get the flight duration for the given flight."""
523 minutes = int(round((flight.flightTimeEnd - flightTimeStart)/60.0))
524 return "%02d:%02d" % (minutes/60, minutes%60)
525
526 columnDescriptors = [
527 ColumnDescriptor("bookedFlight.callsign", xstr("flightsel_no")),
528 ColumnDescriptor("bookedFlight.departureTime", xstr("flightsel_deptime"),
529 sortable = True, defaultSortable = True,
530 defaultDescending = True),
531 ColumnDescriptor("bookedFlight.departureICAO", xstr("flightsel_from"),
532 sortable = True),
533 ColumnDescriptor("bookedFlight.arrivalICAO", xstr("flightsel_to"),
534 sortable = True),
535 ColumnDescriptor("bookedFlight.tailNumber", xstr("pendflt_acft"),
536 convertFn = lambda value, flight:
537 PendingFlightsFrame.getAircraft(value,
538 flight.bookedFlight)),
539 ColumnDescriptor("flightTimeStart", xstr("acceptedflt_flight_duration"),
540 convertFn = getFlightDuration, sortable = True,
541 extraColumnAttributes =
542 { "alignment": 0.5 } ),
543 ColumnDescriptor("numPassengers", xstr("acceptedflt_num_pax"),
544 type = int, sortable = True,
545 extraColumnAttributes =
546 { "alignment": 1.0 } ),
547 ColumnDescriptor("fuelUsed", xstr("acceptedflt_fuel"),
548 type = int, sortable = True,
549 extraColumnAttributes =
550 { "alignment": 1.0 } ),
551 ColumnDescriptor("rating", xstr("acceptedflt_rating"),
552 type = float, sortable = True,
553 extraColumnAttributes =
554 { "alignment": 1.0 },
555 cellDataFn = lambda col, cell, model, iter:
556 cell.set_property("text",
557 "%.0f" %
558 (model.get(iter, 9)[0],)))
559 ]
560
561 def __init__(self, gui):
562 """Construct the window."""
563 super(AcceptedFlightsWindow, self).__init__()
564
565 self._gui = gui
566
567 self.set_title(WINDOW_TITLE_BASE + " - " + xstr("acceptedflt_title"))
568 self.set_size_request(-1, 700)
569 self.set_transient_for(gui.mainWindow)
570
571 alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
572 alignment.set_padding(padding_top = 2, padding_bottom = 8,
573 padding_left = 4, padding_right = 4)
574
575 vbox = gtk.VBox()
576
577 hbox = gtk.HBox()
578 vbox.pack_start(hbox, True, True, 4)
579
580 self._flights = []
581 self._flightList = FlightList(columnDescriptors =
582 AcceptedFlightsWindow.columnDescriptors,
583 widthRequest = 750,
584 multiSelection = False)
585 self._flightList.connect("selection-changed", self._selectionChanged)
586
587 hbox.pack_start(self._flightList, True, True, 4)
588
589 buttonBox = gtk.VBox()
590
591 self._refreshButton = gtk.Button(xstr("acceptedflt_refresh"))
592 self._refreshButton.set_sensitive(True)
593 self._refreshButton.connect("clicked", self._refreshClicked)
594 buttonBox.pack_start(self._refreshButton, False, False, 2)
595
596 filler = gtk.Alignment(xalign = 0.0, yalign = 0.0,
597 xscale = 1.0, yscale = 1.0)
598 filler.set_size_request(-1, 4)
599 buttonBox.pack_start(filler, False, False, 0)
600
601 self._viewButton = gtk.Button(xstr("acceptedflt_view"))
602 self._viewButton.set_sensitive(False)
603 self._viewButton.connect("clicked", self._viewClicked)
604 buttonBox.pack_start(self._viewButton, False, False, 2)
605
606 hbox.pack_start(buttonBox, False, False, 4)
607
608 buttonAlignment = gtk.Alignment(xscale = 0.0, yscale = 0.0,
609 xalign = 0.5, yalign = 0.5)
610
611 self._closeButton = gtk.Button(xstr("button_ok"))
612 self._closeButton.connect("clicked", self._closeClicked)
613
614 buttonAlignment.add(self._closeButton)
615 vbox.pack_start(buttonAlignment, False, False, 2)
616
617 alignment.add(vbox)
618
619 self.add(alignment)
620
621 self.connect("key-press-event", self._keyPressed)
622
623 @property
624 def hasFlights(self):
625 """Determine if there are any flights that we know of."""
626 return len(self._flights)>0
627
628 def clear(self):
629 """Clear the flight list."""
630 self._flights = []
631 self._flightList.clear()
632
633 def addFlight(self, flight):
634 """Add the given flight."""
635 self._flights.append(flight)
636 self._flightList.addFlight(flight)
637
638 def _selectionChanged(self, flightList, selectedIndexes):
639 """Called when the selection has changed."""
640 self._viewButton.set_sensitive(len(selectedIndexes)==1)
641
642 def _refreshClicked(self, button):
643 """Called when the refresh button has been clicked."""
644 self.clear()
645 self._gui.showFlights(None)
646
647 def _viewClicked(self, button):
648 """Called when the view button has been clicked."""
649 gui = self._gui
650 gui.beginBusy(xstr("pendflt_pirep_busy"))
651 self.set_sensitive(False)
652
653 indexes = self._flightList.selectedIndexes
654 assert(len(indexes)==1)
655
656 flightID = self._flights[indexes[0]].bookedFlight.id
657 gui.webHandler.getPIREP(self._pirepResultCallback, flightID)
658
659 def _pirepResultCallback(self, returned, result):
660 """Called when the PIREP query result is available."""
661 gobject.idle_add(self._handlePIREPResult, returned, result)
662
663 def _handlePIREPResult(self, returned, result):
664 """Handle the refly result."""
665 self.set_sensitive(True)
666 gui = self._gui
667 gui.endBusy()
668
669 if returned:
670 gui.viewPIREP(result.pirep)
671
672 def _closeClicked(self, button):
673 """Called when the Close button is clicked.
674
675 A 'delete-event' is emitted to close the window."""
676 self.emit("delete-event", None)
677
678 def _keyPressed(self, window, event):
679 """Called when a key is pressed in the window.
680
681 If the Escape key is pressed, 'delete-event' is emitted to close the
682 window."""
683 if gdk.keyval_name(event.keyval) == "Escape":
684 self.emit("delete-event", None)
685 return True
686
687#-----------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.