source: src/mlx/gui/pirep.py@ 1033:330058d37574

python3
Last change on this file since 1033:330058d37574 was 1033:330058d37574, checked in by István Váradi <ivaradi@…>, 2 years ago

Updates for the new crew and passenger handling (re #357)

File size: 62.3 KB
RevLine 
[220]1
[919]2from .common import *
3from .dcdata import getTable
4from .info import FlightInfo
5from .flight import comboModel
[220]6
[236]7from mlx.pirep import PIREP
[846]8from mlx.flight import Flight
[220]9from mlx.const import *
10
11import time
[845]12import re
[220]13
14#------------------------------------------------------------------------------
15
[300]16## @package mlx.gui.pirep
17#
[845]18# The detailed PIREP viewer and editor windows.
[300]19#
20# The \ref PIREPViewer class is a dialog displaying all information found in a
21# PIREP. It consists of three tabs. The Data tab displays the simple,
22# itemizable data. The Comments & defects tab contains the flight comments and
23# defects, while the Log tab contains the flight log collected by the
24# \ref mlx.logger.Logger "logger".
25
26#------------------------------------------------------------------------------
27
[996]28class MessageFrame(Gtk.Frame):
[855]29 """A frame containing the information about a PIREP message.
30
31 It consists of a text view with the heading information (author, time) and
32 another text view with the actual message."""
33 def __init__(self, message, senderPID, senderName):
34 """Construct the frame."""
[996]35 Gtk.Frame.__init__(self)
[855]36
[996]37 vbox = Gtk.VBox()
[855]38
[996]39 self._heading = heading = Gtk.TextView()
[855]40 heading.set_editable(False)
41 heading.set_can_focus(False)
[999]42 heading.set_wrap_mode(Gtk.WrapMode.WORD)
[985]43 heading.set_size_request(-1, 16)
[855]44
45 buffer = heading.get_buffer()
[999]46 self._headingTag = buffer.create_tag("heading", weight=Pango.Weight.BOLD)
[855]47 buffer.set_text("%s - %s" % (senderPID, senderName))
48 buffer.apply_tag(self._headingTag,
49 buffer.get_start_iter(), buffer.get_end_iter())
50
[996]51 headingAlignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
[855]52 xscale = 1.0, yscale = 0.0)
53 headingAlignment.set_padding(padding_top = 0, padding_bottom = 0,
54 padding_left = 2, padding_right = 2)
55 headingAlignment.add(heading)
56 vbox.pack_start(headingAlignment, True, True, 4)
57
[996]58 self._messageView = messageView = Gtk.TextView()
[999]59 messageView.set_wrap_mode(Gtk.WrapMode.WORD)
[986]60 messageView.set_editable(False)
61 messageView.set_can_focus(False)
[855]62 messageView.set_accepts_tab(False)
63 messageView.set_size_request(-1, 60)
64
65 buffer = messageView.get_buffer()
66 buffer.set_text(message)
67
68 vbox.pack_start(messageView, True, True, 4)
69
70 self.add(vbox)
71 self.show_all()
72
[994]73 styleContext = self.get_style_context()
[996]74 color = styleContext.get_background_color(Gtk.StateFlags.NORMAL)
[994]75 heading.override_background_color(0, color)
[855]76
77
78#-------------------------------------------------------------------------------
79
[996]80class MessagesWidget(Gtk.Frame):
[855]81 """The widget for the messages."""
82 @staticmethod
83 def getFaultFrame(alignment):
84 """Get the fault frame from the given alignment."""
85 return alignment.get_children()[0]
86
87 def __init__(self, gui):
[996]88 Gtk.Frame.__init__(self)
[855]89
90 self._gui = gui
91 self.set_label(xstr("pirep_messages"))
92 label = self.get_label_widget()
93 label.set_use_underline(True)
94
[996]95 alignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
[855]96 xscale = 1.0, yscale = 1.0)
97 alignment.set_padding(padding_top = 4, padding_bottom = 4,
98 padding_left = 4, padding_right = 4)
99
[996]100 self._outerBox = outerBox = Gtk.EventBox()
[855]101 outerBox.add(alignment)
102
[996]103 self._innerBox = innerBox = Gtk.EventBox()
[855]104 alignment.add(self._innerBox)
105
[996]106 alignment = Gtk.Alignment(xalign = 0.5, yalign = 0.5,
[855]107 xscale = 1.0, yscale = 1.0)
108 alignment.set_padding(padding_top = 0, padding_bottom = 0,
109 padding_left = 0, padding_right = 0)
110
111 innerBox.add(alignment)
112
[996]113 scroller = Gtk.ScrolledWindow()
[999]114 scroller.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
115 scroller.set_shadow_type(Gtk.ShadowType.NONE)
[855]116
[996]117 self._messages = Gtk.VBox()
[855]118 self._messages.set_homogeneous(False)
119 scroller.add_with_viewport(self._messages)
120
121 alignment.add(scroller)
122
123 self._messageWidgets = []
124
125 self.add(outerBox)
126 self.show_all()
127
128 def addMessage(self, message):
129 """Add a message from the given sender."""
130
[996]131 alignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
[855]132 xscale = 1.0, yscale = 0.0)
133 alignment.set_padding(padding_top = 2, padding_bottom = 2,
134 padding_left = 4, padding_right = 4)
135
136 messageFrame = MessageFrame(message.message,
137 message.senderPID,
138 message.senderName)
139
140 alignment.add(messageFrame)
141 self._messages.pack_start(alignment, False, False, 4)
142 self._messages.show_all()
143
144 self._messageWidgets.append((alignment, messageFrame))
145
146 def reset(self):
147 """Reset the widget by removing all messages."""
148 for (alignment, messageFrame) in self._messageWidgets:
149 self._messages.remove(alignment)
150 self._messages.show_all()
151
152 self._messageWidgets = []
153
154#------------------------------------------------------------------------------
155
[996]156class PIREPViewer(Gtk.Dialog):
[220]157 """The dialog for PIREP viewing."""
158 @staticmethod
159 def createFrame(label):
160 """Create a frame with the given label.
161
162 The frame will contain an alignment to properly distance the
163 insides. The alignment will contain a VBox to contain the real
[831]164 contents.
[220]165
166 The function returns a tuple with the following items:
167 - the frame,
168 - the inner VBox."""
[996]169 frame = Gtk.Frame(label = label)
[220]170
[996]171 alignment = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
[220]172 xscale = 1.0, yscale = 1.0)
173 frame.add(alignment)
174 alignment.set_padding(padding_top = 4, padding_bottom = 4,
175 padding_left = 4, padding_right = 4)
[996]176 box = Gtk.VBox()
[220]177 alignment.add(box)
[831]178
[220]179 return (frame, box)
180
181 @staticmethod
[845]182 def getLabel(text, extraText = ""):
[220]183 """Get a bold label with the given text."""
[996]184 label = Gtk.Label("<b>" + text + "</b>" + extraText)
[220]185 label.set_use_markup(True)
186 label.set_alignment(0.0, 0.5)
187 return label
188
189 @staticmethod
190 def getDataLabel(width = None, xAlignment = 0.0):
191 """Get a bold label with the given text."""
[996]192 label = Gtk.Label()
[220]193 if width is not None:
194 label.set_width_chars(width)
195 label.set_alignment(xAlignment, 0.5)
196 return label
197
198 @staticmethod
[845]199 def getTextWindow(heightRequest = 40, editable = False):
[220]200 """Get a scrollable text window.
[831]201
[220]202 Returns a tuple of the following items:
203 - the window,
204 - the text view."""
[996]205 scrolledWindow = Gtk.ScrolledWindow()
[999]206 scrolledWindow.set_shadow_type(Gtk.ShadowType.IN)
207 scrolledWindow.set_policy(Gtk.PolicyType.AUTOMATIC,
208 Gtk.PolicyType.AUTOMATIC)
[220]209
[996]210 textView = Gtk.TextView()
[999]211 textView.set_wrap_mode(Gtk.WrapMode.WORD)
[845]212 textView.set_editable(editable)
213 textView.set_cursor_visible(editable)
[220]214 textView.set_size_request(-1, heightRequest)
215 scrolledWindow.add(textView)
216
217 return (scrolledWindow, textView)
218
219 @staticmethod
220 def tableAttach(table, column, row, labelText, width = None,
221 dataLabelXAlignment = 0.0):
222 """Attach a labeled data to the given column and row of the
[831]223 table.
224
[220]225 If width is given, that will be the width of the data
226 label.
227
228 Returns the data label attached."""
[996]229 dataBox = Gtk.HBox()
[220]230 table.attach(dataBox, column, column+1, row, row+1)
[831]231
[220]232 dataLabel = PIREPViewer.addLabeledData(dataBox, labelText,
233 width = width)
234 dataLabel.set_alignment(dataLabelXAlignment, 0.5)
235
236 return dataLabel
237
238 @staticmethod
239 def addLabeledData(hBox, labelText, width = None, dataPadding = 8):
240 """Add a label and a data label to the given HBox.
[831]241
[845]242 Returns the data label."""
[220]243 label = PIREPViewer.getLabel(labelText)
244 hBox.pack_start(label, False, False, 0)
[831]245
[220]246 dataLabel = PIREPViewer.getDataLabel(width = width)
247 hBox.pack_start(dataLabel, False, False, dataPadding)
248
249 return dataLabel
250
251 @staticmethod
[845]252 def addHFiller(hBox, width = 8):
253 """Add a filler to the given horizontal box."""
[996]254 filler = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
[845]255 xscale = 1.0, yscale = 1.0)
256 filler.set_size_request(width, -1)
257 hBox.pack_start(filler, False, False, 0)
258
259 @staticmethod
[220]260 def addVFiller(vBox, height = 4):
[238]261 """Add a filler to the given vertical box."""
[996]262 filler = Gtk.Alignment(xalign = 0.0, yalign = 0.0,
[238]263 xscale = 1.0, yscale = 1.0)
[220]264 filler.set_size_request(-1, height)
265 vBox.pack_start(filler, False, False, 0)
[831]266
[220]267 @staticmethod
268 def timestamp2text(label, timestamp):
269 """Convert the given timestamp into a text containing the hour
270 and minute in UTC and put that text into the given label."""
271 tm = time.gmtime(timestamp)
272 label.set_text("%02d:%02d" % (tm.tm_hour, tm.tm_min))
273
[855]274 def __init__(self, gui, showMessages = False):
[220]275 """Construct the PIREP viewer."""
276 super(PIREPViewer, self).__init__(title = WINDOW_TITLE_BASE +
277 " - " +
278 xstr("pirepView_title"),
279 parent = gui.mainWindow)
[831]280
[220]281 self.set_resizable(False)
282
283 self._gui = gui
[831]284
[220]285 contentArea = self.get_content_area()
[221]286
[996]287 self._notebook = Gtk.Notebook()
[221]288 contentArea.pack_start(self._notebook, False, False, 4)
[831]289
[220]290 dataTab = self._buildDataTab()
[996]291 label = Gtk.Label(xstr("pirepView_tab_data"))
[221]292 label.set_use_underline(True)
293 label.set_tooltip_text(xstr("pirepView_tab_data_tooltip"))
294 self._notebook.append_page(dataTab, label)
[831]295
[221]296 commentsTab = self._buildCommentsTab()
[996]297 label = Gtk.Label(xstr("pirepView_tab_comments"))
[221]298 label.set_use_underline(True)
299 label.set_tooltip_text(xstr("pirepView_tab_comments_tooltip"))
300 self._notebook.append_page(commentsTab, label)
[831]301
[221]302 logTab = self._buildLogTab()
[996]303 label = Gtk.Label(xstr("pirepView_tab_log"))
[221]304 label.set_use_underline(True)
305 label.set_tooltip_text(xstr("pirepView_tab_log_tooltip"))
306 self._notebook.append_page(logTab, label)
[224]307
[855]308 self._showMessages = showMessages
309 if showMessages:
310 messagesTab = self._buildMessagesTab()
[996]311 label = Gtk.Label(xstr("pirepView_tab_messages"))
[855]312 label.set_use_underline(True)
313 label.set_tooltip_text(xstr("pirepView_tab_messages_tooltip"))
314 self._notebook.append_page(messagesTab, label)
315
[999]316 self._okButton = self.add_button(xstr("button_ok"), Gtk.ResponseType.OK)
[224]317 self._okButton.set_can_default(True)
[831]318
[220]319 def setPIREP(self, pirep):
320 """Setup the data in the dialog from the given PIREP."""
321 bookedFlight = pirep.bookedFlight
322
323 self._callsign.set_text(bookedFlight.callsign)
324 self._tailNumber.set_text(bookedFlight.tailNumber)
325 aircraftType = xstr("aircraft_" + icaoCodes[bookedFlight.aircraftType].lower())
326 self._aircraftType.set_text(aircraftType)
[831]327
[220]328 self._departureICAO.set_text(bookedFlight.departureICAO)
329 self._departureTime.set_text("%02d:%02d" % \
330 (bookedFlight.departureTime.hour,
331 bookedFlight.departureTime.minute))
332
333 self._arrivalICAO.set_text(bookedFlight.arrivalICAO)
334 self._arrivalTime.set_text("%02d:%02d" % \
335 (bookedFlight.arrivalTime.hour,
336 bookedFlight.arrivalTime.minute))
337
[1033]338 self._numPassengers.set_text(str(bookedFlight.numPassengers) + " + " +
339 str(bookedFlight.numChildren) + " + " +
340 str(bookedFlight.numInfants))
341 self._numCrew.set_text(str(bookedFlight.numCockpitCrew) + " + " +
342 str(bookedFlight.numCabinCrew))
[220]343 self._bagWeight.set_text(str(bookedFlight.bagWeight))
344 self._cargoWeight.set_text(str(bookedFlight.cargoWeight))
345 self._mailWeight.set_text(str(bookedFlight.mailWeight))
[831]346
[220]347 self._route.get_buffer().set_text(bookedFlight.route)
348
349 self._filedCruiseLevel.set_text("FL" + str(pirep.filedCruiseAltitude/100))
350
351 if pirep.cruiseAltitude != pirep.filedCruiseAltitude:
352 self._modifiedCruiseLevel.set_text("FL" + str(pirep.cruiseAltitude/100))
353 else:
354 self._modifiedCruiseLevel.set_text("-")
355
356 self._userRoute.get_buffer().set_text(pirep.route)
357
358 self._departureMETAR.get_buffer().set_text(pirep.departureMETAR)
359
360 self._arrivalMETAR.get_buffer().set_text(pirep.arrivalMETAR)
361 self._departureRunway.set_text(pirep.departureRunway)
362 self._sid.set_text(pirep.sid)
363
364 self._star.set_text("-" if pirep.star is None else pirep.star)
365 self._transition.set_text("-" if pirep.transition is None else pirep.transition)
366 self._approachType.set_text(pirep.approachType)
367 self._arrivalRunway.set_text(pirep.arrivalRunway)
368
369 PIREPViewer.timestamp2text(self._blockTimeStart, pirep.blockTimeStart)
370 PIREPViewer.timestamp2text(self._blockTimeEnd, pirep.blockTimeEnd)
371 PIREPViewer.timestamp2text(self._flightTimeStart, pirep.flightTimeStart)
372 PIREPViewer.timestamp2text(self._flightTimeEnd, pirep.flightTimeEnd)
[831]373
[220]374 self._flownDistance.set_text("%.1f" % (pirep.flownDistance,))
375 self._fuelUsed.set_text("%.0f" % (pirep.fuelUsed,))
376
377 rating = pirep.rating
378 if rating<0:
379 self._rating.set_markup('<b><span foreground="red">NO GO</span></b>')
380 else:
381 self._rating.set_text("%.1f %%" % (rating,))
382
[1033]383 self._flownNumCabinCrew.set_text("%d" % (pirep.numCabinCrew,))
[303]384 self._flownNumPassengers.set_text("%d" % (pirep.numPassengers,))
385 self._flownBagWeight.set_text("%.0f" % (pirep.bagWeight,))
[223]386 self._flownCargoWeight.set_text("%.0f" % (pirep.cargoWeight,))
[303]387 self._flownMailWeight.set_text("%.0f" % (pirep.mailWeight,))
[831]388 self._flightType.set_text(xstr("flighttype_" +
[220]389 flightType2string(pirep.flightType)))
390 self._online.set_text(xstr("pirepView_" +
391 ("yes" if pirep.online else "no")))
392
393 delayCodes = ""
394 for code in pirep.delayCodes:
395 if delayCodes: delayCodes += ", "
[437]396 delayCodes += code
[831]397
398 self._delayCodes.get_buffer().set_text(delayCodes)
[221]399
400 self._comments.get_buffer().set_text(pirep.comments)
401 self._flightDefects.get_buffer().set_text(pirep.flightDefects)
402
403 logBuffer = self._log.get_buffer()
404 logBuffer.set_text("")
[225]405 lineIndex = 0
[221]406 for (timeStr, line) in pirep.logLines:
[225]407 isFault = lineIndex in pirep.faultLineIndexes
[226]408 appendTextBuffer(logBuffer,
409 formatFlightLogLine(timeStr, line),
410 isFault = isFault)
[225]411 lineIndex += 1
[221]412
[855]413 if self._showMessages:
414 self._messages.reset()
415 for message in pirep.messages:
416 self._messages.addMessage(message)
417
[221]418 self._notebook.set_current_page(0)
[224]419 self._okButton.grab_default()
[220]420
421 def _buildDataTab(self):
422 """Build the data tab of the viewer."""
[996]423 table = Gtk.Table(1, 2)
[220]424 table.set_row_spacings(4)
425 table.set_col_spacings(16)
426 table.set_homogeneous(True)
427
[996]428 box1 = Gtk.VBox()
[220]429 table.attach(box1, 0, 1, 0, 1)
[831]430
[996]431 box2 = Gtk.VBox()
[220]432 table.attach(box2, 1, 2, 0, 1)
[831]433
[220]434 flightFrame = self._buildFlightFrame()
435 box1.pack_start(flightFrame, False, False, 4)
436
437 routeFrame = self._buildRouteFrame()
438 box1.pack_start(routeFrame, False, False, 4)
439
440 departureFrame = self._buildDepartureFrame()
441 box2.pack_start(departureFrame, True, True, 4)
442
443 arrivalFrame = self._buildArrivalFrame()
444 box2.pack_start(arrivalFrame, True, True, 4)
445
446 statisticsFrame = self._buildStatisticsFrame()
447 box2.pack_start(statisticsFrame, False, False, 4)
448
449 miscellaneousFrame = self._buildMiscellaneousFrame()
450 box1.pack_start(miscellaneousFrame, False, False, 4)
451
452 return table
453
454 def _buildFlightFrame(self):
455 """Build the frame for the flight data."""
[831]456
[220]457 (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_flight"))
[831]458
[996]459 dataBox = Gtk.HBox()
[220]460 mainBox.pack_start(dataBox, False, False, 0)
[831]461
[220]462 self._callsign = \
463 PIREPViewer.addLabeledData(dataBox,
464 xstr("pirepView_callsign"),
465 width = 8)
466
467 self._tailNumber = \
468 PIREPViewer.addLabeledData(dataBox,
469 xstr("pirepView_tailNumber"),
470 width = 7)
471
472 PIREPViewer.addVFiller(mainBox)
473
[996]474 dataBox = Gtk.HBox()
[220]475 mainBox.pack_start(dataBox, False, False, 0)
476
477 self._aircraftType = \
478 PIREPViewer.addLabeledData(dataBox,
479 xstr("pirepView_aircraftType"),
480 width = 25)
481
482 PIREPViewer.addVFiller(mainBox)
483
[996]484 table = Gtk.Table(3, 2)
[220]485 mainBox.pack_start(table, False, False, 0)
486 table.set_row_spacings(4)
[831]487 table.set_col_spacings(8)
[220]488
489 self._departureICAO = \
490 PIREPViewer.tableAttach(table, 0, 0,
491 xstr("pirepView_departure"),
492 width = 5)
493
494 self._departureTime = \
495 PIREPViewer.tableAttach(table, 1, 0,
496 xstr("pirepView_departure_time"),
497 width = 6)
498
499 self._arrivalICAO = \
500 PIREPViewer.tableAttach(table, 0, 1,
501 xstr("pirepView_arrival"),
502 width = 5)
503
504 self._arrivalTime = \
505 PIREPViewer.tableAttach(table, 1, 1,
506 xstr("pirepView_arrival_time"),
507 width = 6)
508
[996]509 table = Gtk.Table(3, 2)
[220]510 mainBox.pack_start(table, False, False, 0)
511 table.set_row_spacings(4)
[831]512 table.set_col_spacings(8)
[220]513
514 self._numPassengers = \
515 PIREPViewer.tableAttach(table, 0, 0,
516 xstr("pirepView_numPassengers"),
517 width = 4)
518
519 self._numCrew = \
520 PIREPViewer.tableAttach(table, 1, 0,
521 xstr("pirepView_numCrew"),
522 width = 3)
523
524 self._bagWeight = \
[831]525 PIREPViewer.tableAttach(table, 0, 1,
[220]526 xstr("pirepView_bagWeight"),
527 width = 5)
528
529 self._cargoWeight = \
[831]530 PIREPViewer.tableAttach(table, 1, 1,
[220]531 xstr("pirepView_cargoWeight"),
532 width = 5)
533
534 self._mailWeight = \
[831]535 PIREPViewer.tableAttach(table, 2, 1,
[220]536 xstr("pirepView_mailWeight"),
537 width = 5)
[831]538
[220]539 PIREPViewer.addVFiller(mainBox)
540
541 mainBox.pack_start(PIREPViewer.getLabel(xstr("pirepView_route")),
542 False, False, 0)
[831]543
[220]544 (routeWindow, self._route) = PIREPViewer.getTextWindow()
545 mainBox.pack_start(routeWindow, False, False, 0)
546
547 return frame
548
549 def _buildRouteFrame(self):
550 """Build the frame for the user-specified route and flight
551 level."""
[831]552
[220]553 (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_route"))
554
[996]555 levelBox = Gtk.HBox()
[220]556 mainBox.pack_start(levelBox, False, False, 0)
[831]557
[220]558 self._filedCruiseLevel = \
[831]559 PIREPViewer.addLabeledData(levelBox,
[220]560 xstr("pirepView_filedCruiseLevel"),
561 width = 6)
562
563 self._modifiedCruiseLevel = \
564 PIREPViewer.addLabeledData(levelBox,
565 xstr("pirepView_modifiedCruiseLevel"),
566 width = 6)
567
568 PIREPViewer.addVFiller(mainBox)
569
570 (routeWindow, self._userRoute) = PIREPViewer.getTextWindow()
571 mainBox.pack_start(routeWindow, False, False, 0)
572
573 return frame
574
575 def _buildDepartureFrame(self):
[831]576 """Build the frame for the departure data."""
[220]577 (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_departure"))
578
579 mainBox.pack_start(PIREPViewer.getLabel("METAR:"),
580 False, False, 0)
581 (metarWindow, self._departureMETAR) = \
582 PIREPViewer.getTextWindow(heightRequest = -1)
583 mainBox.pack_start(metarWindow, True, True, 0)
584
585 PIREPViewer.addVFiller(mainBox)
586
[996]587 dataBox = Gtk.HBox()
[220]588 mainBox.pack_start(dataBox, False, False, 0)
[831]589
[220]590 self._departureRunway = \
591 PIREPViewer.addLabeledData(dataBox,
592 xstr("pirepView_runway"),
593 width = 5)
594
595 self._sid = \
596 PIREPViewer.addLabeledData(dataBox,
597 xstr("pirepView_sid"),
598 width = 12)
599
600 return frame
[831]601
[220]602 def _buildArrivalFrame(self):
[831]603 """Build the frame for the arrival data."""
[220]604 (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_arrival"))
605
606 mainBox.pack_start(PIREPViewer.getLabel("METAR:"),
607 False, False, 0)
608 (metarWindow, self._arrivalMETAR) = \
609 PIREPViewer.getTextWindow(heightRequest = -1)
610 mainBox.pack_start(metarWindow, True, True, 0)
611
612 PIREPViewer.addVFiller(mainBox)
613
[996]614 table = Gtk.Table(2, 2)
[220]615 mainBox.pack_start(table, False, False, 0)
616 table.set_row_spacings(4)
[831]617 table.set_col_spacings(8)
[220]618
619 self._star = \
620 PIREPViewer.tableAttach(table, 0, 0,
621 xstr("pirepView_star"),
622 width = 12)
623
624 self._transition = \
625 PIREPViewer.tableAttach(table, 1, 0,
626 xstr("pirepView_transition"),
627 width = 12)
[831]628
[220]629 self._approachType = \
630 PIREPViewer.tableAttach(table, 0, 1,
631 xstr("pirepView_approachType"),
632 width = 7)
[831]633
[220]634 self._arrivalRunway = \
[831]635 PIREPViewer.tableAttach(table, 1, 1,
[220]636 xstr("pirepView_runway"),
637 width = 5)
[831]638
[220]639 return frame
640
641 def _buildStatisticsFrame(self):
[831]642 """Build the frame for the statistics data."""
[220]643 (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_statistics"))
644
[996]645 table = Gtk.Table(4, 2)
[220]646 mainBox.pack_start(table, False, False, 0)
647 table.set_row_spacings(4)
648 table.set_col_spacings(8)
[221]649 table.set_homogeneous(False)
[831]650
[220]651 self._blockTimeStart = \
652 PIREPViewer.tableAttach(table, 0, 0,
653 xstr("pirepView_blockTimeStart"),
654 width = 6)
[831]655
[220]656 self._blockTimeEnd = \
657 PIREPViewer.tableAttach(table, 1, 0,
658 xstr("pirepView_blockTimeEnd"),
659 width = 8)
660
661 self._flightTimeStart = \
662 PIREPViewer.tableAttach(table, 0, 1,
663 xstr("pirepView_flightTimeStart"),
664 width = 6)
[831]665
[220]666 self._flightTimeEnd = \
[831]667 PIREPViewer.tableAttach(table, 1, 1,
[220]668 xstr("pirepView_flightTimeEnd"),
669 width = 6)
670
671 self._flownDistance = \
672 PIREPViewer.tableAttach(table, 0, 2,
673 xstr("pirepView_flownDistance"),
674 width = 8)
[831]675
[220]676 self._fuelUsed = \
677 PIREPViewer.tableAttach(table, 1, 2,
678 xstr("pirepView_fuelUsed"),
679 width = 6)
[831]680
[220]681 self._rating = \
682 PIREPViewer.tableAttach(table, 0, 3,
683 xstr("pirepView_rating"),
684 width = 7)
685 return frame
686
687 def _buildMiscellaneousFrame(self):
[831]688 """Build the frame for the miscellaneous data."""
[220]689 (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_miscellaneous"))
[303]690
[996]691 table = Gtk.Table(3, 2)
[303]692 mainBox.pack_start(table, False, False, 0)
693 table.set_row_spacings(4)
[831]694 table.set_col_spacings(8)
695
[303]696 self._flownNumPassengers = \
697 PIREPViewer.tableAttach(table, 0, 0,
698 xstr("pirepView_numPassengers"),
699 width = 4)
700
[1033]701 self._flownNumCabinCrew = \
[303]702 PIREPViewer.tableAttach(table, 1, 0,
703 xstr("pirepView_numCrew"),
704 width = 3)
705
706 self._flownBagWeight = \
707 PIREPViewer.tableAttach(table, 0, 1,
708 xstr("pirepView_bagWeight"),
709 width = 5)
710
[223]711 self._flownCargoWeight = \
[303]712 PIREPViewer.tableAttach(table, 1, 1,
713 xstr("pirepView_cargoWeight"),
714 width = 6)
715
716 self._flownMailWeight = \
717 PIREPViewer.tableAttach(table, 2, 1,
718 xstr("pirepView_mailWeight"),
719 width = 5)
[223]720
[220]721 self._flightType = \
[303]722 PIREPViewer.tableAttach(table, 0, 2,
723 xstr("pirepView_flightType"),
724 width = 15)
[831]725
[220]726 self._online = \
[303]727 PIREPViewer.tableAttach(table, 1, 2,
728 xstr("pirepView_online"),
729 width = 5)
[220]730
731 PIREPViewer.addVFiller(mainBox)
732
733 mainBox.pack_start(PIREPViewer.getLabel(xstr("pirepView_delayCodes")),
734 False, False, 0)
[831]735
[220]736 (textWindow, self._delayCodes) = PIREPViewer.getTextWindow()
737 mainBox.pack_start(textWindow, False, False, 0)
738
[831]739 return frame
[220]740
[221]741 def _buildCommentsTab(self):
742 """Build the tab with the comments and flight defects."""
[996]743 table = Gtk.Table(2, 1)
[221]744 table.set_col_spacings(16)
745
746 (frame, commentsBox) = \
747 PIREPViewer.createFrame(xstr("pirepView_comments"))
748 table.attach(frame, 0, 1, 0, 1)
749
750 (commentsWindow, self._comments) = \
751 PIREPViewer.getTextWindow(heightRequest = -1)
752 commentsBox.pack_start(commentsWindow, True, True, 0)
753
754 (frame, flightDefectsBox) = \
755 PIREPViewer.createFrame(xstr("pirepView_flightDefects"))
756 table.attach(frame, 1, 2, 0, 1)
757
758 (flightDefectsWindow, self._flightDefects) = \
759 PIREPViewer.getTextWindow(heightRequest = -1)
760 flightDefectsBox.pack_start(flightDefectsWindow, True, True, 0)
[831]761
[221]762 return table
763
764 def _buildLogTab(self):
765 """Build the log tab."""
[996]766 mainBox = Gtk.VBox()
[221]767
768 (logWindow, self._log) = PIREPViewer.getTextWindow(heightRequest = -1)
[226]769 addFaultTag(self._log.get_buffer())
[221]770 mainBox.pack_start(logWindow, True, True, 0)
[831]771
[221]772 return mainBox
[831]773
[855]774 def _buildMessagesTab(self):
775 """Build the messages tab."""
[996]776 mainBox = Gtk.VBox()
[855]777
778 self._messages = MessagesWidget(self._gui)
779 mainBox.pack_start(self._messages, True, True, 0)
780
781 return mainBox
782
[220]783#------------------------------------------------------------------------------
[845]784
[996]785class PIREPEditor(Gtk.Dialog):
[845]786 """A PIREP editor dialog."""
787 _delayCodeRE = re.compile("([0-9]{2,3})( \([^\)]*\))")
788
789 @staticmethod
790 def tableAttachWidget(table, column, row, labelText, widget):
791 """Attach the given widget with the given label to the given table.
792
793 The label will got to cell (column, row), the widget to cell
794 (column+1, row)."""
[996]795 label = Gtk.Label("<b>" + labelText + "</b>")
[845]796 label.set_use_markup(True)
[996]797 alignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
[845]798 xscale = 0.0, yscale = 0.0)
799 alignment.add(label)
800 table.attach(alignment, column, column + 1, row, row + 1)
801
802 table.attach(widget, column + 1, column + 2, row, row + 1)
803
804 @staticmethod
805 def tableAttachSpinButton(table, column, row, labelText, maxValue,
806 minValue = 0, stepIncrement = 1,
807 pageIncrement = 10, numeric = True,
808 width = 3):
809 """Attach a spin button with the given label to the given table.
810
811 The label will got to cell (column, row), the spin button to cell
812 (column+1, row)."""
[996]813 button = Gtk.SpinButton()
[845]814 button.set_range(min = minValue, max = maxValue)
815 button.set_increments(step = stepIncrement, page = pageIncrement)
816 button.set_numeric(True)
817 button.set_width_chars(width)
818 button.set_alignment(1.0)
819
820 PIREPEditor.tableAttachWidget(table, column, row, labelText, button)
821
822 return button
823
824 @staticmethod
825 def tableAttachTimeEntry(table, column, row, labelText):
826 """Attach a time entry widget with the given label to the given table.
827
828 The label will got to cell (column, row), the spin button to cell
829 (column+1, row)."""
830 entry = TimeEntry()
831 entry.set_width_chars(5)
832 entry.set_alignment(1.0)
833
834 PIREPEditor.tableAttachWidget(table, column, row, labelText, entry)
835
836 return entry
837
838 def __init__(self, gui):
839 """Construct the PIREP viewer."""
840 super(PIREPEditor, self).__init__(title = WINDOW_TITLE_BASE +
841 " - " +
842 xstr("pirepEdit_title"),
843 parent = gui.mainWindow)
844
845 self.set_resizable(False)
846
847 self._gui = gui
848
[846]849 self._pirep = None
850
[845]851 contentArea = self.get_content_area()
852
[996]853 self._notebook = Gtk.Notebook()
[845]854 contentArea.pack_start(self._notebook, False, False, 4)
855
856 dataTab = self._buildDataTab()
[996]857 label = Gtk.Label(xstr("pirepView_tab_data"))
[845]858 label.set_use_underline(True)
859 label.set_tooltip_text(xstr("pirepView_tab_data_tooltip"))
860 self._notebook.append_page(dataTab, label)
861
862 self._flightInfo = self._buildCommentsTab()
[996]863 label = Gtk.Label(xstr("pirepView_tab_comments"))
[845]864 label.set_use_underline(True)
865 label.set_tooltip_text(xstr("pirepView_tab_comments_tooltip"))
866 self._notebook.append_page(self._flightInfo, label)
867
868 logTab = self._buildLogTab()
[996]869 label = Gtk.Label(xstr("pirepView_tab_log"))
[845]870 label.set_use_underline(True)
871 label.set_tooltip_text(xstr("pirepView_tab_log_tooltip"))
872 self._notebook.append_page(logTab, label)
873
[999]874 self.add_button(xstr("button_cancel"), Gtk.ResponseType.CANCEL)
[846]875
[999]876 self._okButton = self.add_button(xstr("button_save"), Gtk.ResponseType.NONE)
[853]877 self._okButton.connect("clicked", self._okClicked)
[845]878 self._okButton.set_can_default(True)
[849]879 self._modified = False
[853]880 self._toSave = False
[845]881
882 def setPIREP(self, pirep):
883 """Setup the data in the dialog from the given PIREP."""
[846]884 self._pirep = pirep
885
[845]886 bookedFlight = pirep.bookedFlight
887
888 self._callsign.set_text(bookedFlight.callsign)
889 self._tailNumber.set_text(bookedFlight.tailNumber)
890 aircraftType = xstr("aircraft_" + icaoCodes[bookedFlight.aircraftType].lower())
891 self._aircraftType.set_text(aircraftType)
892
893 self._departureICAO.set_text(bookedFlight.departureICAO)
894 self._departureTime.set_text("%02d:%02d" % \
895 (bookedFlight.departureTime.hour,
896 bookedFlight.departureTime.minute))
897
898 self._arrivalICAO.set_text(bookedFlight.arrivalICAO)
899 self._arrivalTime.set_text("%02d:%02d" % \
900 (bookedFlight.arrivalTime.hour,
901 bookedFlight.arrivalTime.minute))
902
[1033]903 self._numPassengers.set_text(str(bookedFlight.numPassengers) + " + " +
904 str(bookedFlight.numChildren) + " + " +
905 str(bookedFlight.numInfants))
906 self._numCrew.set_text(str(bookedFlight.numCockpitCrew) + " + " +
907 str(bookedFlight.numCabinCrew))
[845]908 self._bagWeight.set_text(str(bookedFlight.bagWeight))
909 self._cargoWeight.set_text(str(bookedFlight.cargoWeight))
910 self._mailWeight.set_text(str(bookedFlight.mailWeight))
911
912 self._route.get_buffer().set_text(bookedFlight.route)
913
914 self._filedCruiseLevel.set_value(pirep.filedCruiseAltitude/100)
915 self._modifiedCruiseLevel.set_value(pirep.cruiseAltitude/100)
916
917 self._userRoute.get_buffer().set_text(pirep.route)
918
919 self._departureMETAR.get_buffer().set_text(pirep.departureMETAR)
920
921 self._arrivalMETAR.get_buffer().set_text(pirep.arrivalMETAR)
922 self._departureRunway.set_text(pirep.departureRunway)
923 self._sid.get_child().set_text(pirep.sid)
924
925 if not pirep.star:
926 self._star.set_active(0)
927 else:
928 self._star.get_child().set_text(pirep.star)
929
930 if not pirep.transition:
931 self._transition.set_active(0)
932 else:
933 self._transition.get_child().set_text(pirep.transition)
934 self._approachType.set_text(pirep.approachType)
935 self._arrivalRunway.set_text(pirep.arrivalRunway)
936
937 self._blockTimeStart.setTimestamp(pirep.blockTimeStart)
938 self._blockTimeEnd.setTimestamp(pirep.blockTimeEnd)
939 self._flightTimeStart.setTimestamp(pirep.flightTimeStart)
940 self._flightTimeEnd.setTimestamp(pirep.flightTimeEnd)
941
942 self._flownDistance.set_text("%.1f" % (pirep.flownDistance,))
943 self._fuelUsed.set_value(int(pirep.fuelUsed))
944
945 rating = pirep.rating
946 if rating<0:
947 self._rating.set_markup('<b><span foreground="red">NO GO</span></b>')
948 else:
949 self._rating.set_text("%.1f %%" % (rating,))
950
[1033]951 self._flownNumCabinCrew.set_value(pirep.numCabinCrew)
[845]952 self._flownNumPassengers.set_value(pirep.numPassengers)
[1033]953 self._flownNumChildren.set_value(pirep.numChildren)
954 self._flownNumInfants.set_value(pirep.numInfants)
[845]955 self._flownBagWeight.set_value(pirep.bagWeight)
956 self._flownCargoWeight.set_value(pirep.cargoWeight)
957 self._flownMailWeight.set_value(pirep.mailWeight)
958 self._flightType.set_active(flightType2index(pirep.flightType))
959 self._online.set_active(pirep.online)
960
961 self._flightInfo.reset()
962 self._flightInfo.enable(bookedFlight.aircraftType)
963
964 delayCodes = ""
965 for code in pirep.delayCodes:
966 if delayCodes: delayCodes += ", "
967 delayCodes += code
968 m = PIREPEditor._delayCodeRE.match(code)
969 if m:
970 self._flightInfo.activateDelayCode(m.group(1))
971
972 self._delayCodes.get_buffer().set_text(delayCodes)
973
974 self._flightInfo.comments = pirep.comments
975 if pirep.flightDefects.find("<br/></b>")!=-1:
976 flightDefects = pirep.flightDefects.split("<br/></b>")
977 caption = flightDefects[0]
978 index = 0
979 for defect in flightDefects[1:]:
980 if defect.find("<b>")!=-1:
981 (explanation, nextCaption) = defect.split("<b>")
982 else:
983 explanation = defect
984 nextCaption = None
985 self._flightInfo.addFault(index, caption)
986 self._flightInfo.setExplanation(index, explanation)
987 index += 1
988 caption = nextCaption
989
990 # self._comments.get_buffer().set_text(pirep.comments)
991 # self._flightDefects.get_buffer().set_text(pirep.flightDefects)
992
993 logBuffer = self._log.get_buffer()
994 logBuffer.set_text("")
995 lineIndex = 0
996 for (timeStr, line) in pirep.logLines:
997 isFault = lineIndex in pirep.faultLineIndexes
998 appendTextBuffer(logBuffer,
999 formatFlightLogLine(timeStr, line),
1000 isFault = isFault)
1001 lineIndex += 1
1002
1003 self._notebook.set_current_page(0)
1004 self._okButton.grab_default()
1005
[849]1006 self._modified = False
[846]1007 self._updateButtons()
[849]1008 self._modified = True
[853]1009 self._toSave = False
[846]1010
1011 def delayCodesChanged(self):
1012 """Called when the delay codes have changed."""
1013 self._updateButtons()
1014
1015 def commentsChanged(self):
1016 """Called when the comments have changed."""
1017 self._updateButtons()
1018
1019 def faultExplanationsChanged(self):
1020 """Called when the fault explanations have changed."""
1021 self._updateButtons()
1022
[845]1023 def _buildDataTab(self):
1024 """Build the data tab of the viewer."""
[996]1025 table = Gtk.Table(1, 2)
[845]1026 table.set_row_spacings(4)
1027 table.set_col_spacings(16)
1028 table.set_homogeneous(True)
1029
[996]1030 box1 = Gtk.VBox()
[845]1031 table.attach(box1, 0, 1, 0, 1)
1032
[996]1033 box2 = Gtk.VBox()
[845]1034 table.attach(box2, 1, 2, 0, 1)
1035
1036 flightFrame = self._buildFlightFrame()
1037 box1.pack_start(flightFrame, False, False, 4)
1038
1039 routeFrame = self._buildRouteFrame()
1040 box1.pack_start(routeFrame, False, False, 4)
1041
1042 departureFrame = self._buildDepartureFrame()
1043 box2.pack_start(departureFrame, True, True, 4)
1044
1045 arrivalFrame = self._buildArrivalFrame()
1046 box2.pack_start(arrivalFrame, True, True, 4)
1047
1048 statisticsFrame = self._buildStatisticsFrame()
1049 box2.pack_start(statisticsFrame, False, False, 4)
1050
1051 miscellaneousFrame = self._buildMiscellaneousFrame()
1052 box1.pack_start(miscellaneousFrame, False, False, 4)
1053
1054 return table
1055
1056 def _buildFlightFrame(self):
1057 """Build the frame for the flight data."""
1058
1059 (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_flight"))
1060
[996]1061 dataBox = Gtk.HBox()
[845]1062 mainBox.pack_start(dataBox, False, False, 0)
1063
1064 self._callsign = \
1065 PIREPViewer.addLabeledData(dataBox,
1066 xstr("pirepView_callsign"),
1067 width = 8)
1068
1069 self._tailNumber = \
1070 PIREPViewer.addLabeledData(dataBox,
1071 xstr("pirepView_tailNumber"),
1072 width = 7)
1073
1074 PIREPViewer.addVFiller(mainBox)
1075
[996]1076 dataBox = Gtk.HBox()
[845]1077 mainBox.pack_start(dataBox, False, False, 0)
1078
1079 self._aircraftType = \
1080 PIREPViewer.addLabeledData(dataBox,
1081 xstr("pirepView_aircraftType"),
1082 width = 25)
1083
1084 PIREPViewer.addVFiller(mainBox)
1085
[996]1086 table = Gtk.Table(3, 2)
[845]1087 mainBox.pack_start(table, False, False, 0)
1088 table.set_row_spacings(4)
1089 table.set_col_spacings(8)
1090
1091 self._departureICAO = \
1092 PIREPViewer.tableAttach(table, 0, 0,
1093 xstr("pirepView_departure"),
1094 width = 5)
1095
1096 self._departureTime = \
1097 PIREPViewer.tableAttach(table, 1, 0,
1098 xstr("pirepView_departure_time"),
1099 width = 6)
1100
1101 self._arrivalICAO = \
1102 PIREPViewer.tableAttach(table, 0, 1,
1103 xstr("pirepView_arrival"),
1104 width = 5)
1105
1106 self._arrivalTime = \
1107 PIREPViewer.tableAttach(table, 1, 1,
1108 xstr("pirepView_arrival_time"),
1109 width = 6)
1110
[996]1111 table = Gtk.Table(3, 2)
[845]1112 mainBox.pack_start(table, False, False, 0)
1113 table.set_row_spacings(4)
1114 table.set_col_spacings(8)
1115
1116 self._numPassengers = \
1117 PIREPViewer.tableAttach(table, 0, 0,
1118 xstr("pirepView_numPassengers"),
1119 width = 4)
1120 self._numCrew = \
1121 PIREPViewer.tableAttach(table, 1, 0,
1122 xstr("pirepView_numCrew"),
1123 width = 3)
1124
1125 self._bagWeight = \
1126 PIREPViewer.tableAttach(table, 0, 1,
1127 xstr("pirepView_bagWeight"),
1128 width = 5)
1129
1130 self._cargoWeight = \
1131 PIREPViewer.tableAttach(table, 1, 1,
1132 xstr("pirepView_cargoWeight"),
1133 width = 5)
1134
1135 self._mailWeight = \
1136 PIREPViewer.tableAttach(table, 2, 1,
1137 xstr("pirepView_mailWeight"),
1138 width = 5)
1139
1140 PIREPViewer.addVFiller(mainBox)
1141
1142 mainBox.pack_start(PIREPViewer.getLabel(xstr("pirepView_route")),
1143 False, False, 0)
1144
1145 (routeWindow, self._route) = PIREPViewer.getTextWindow()
1146 mainBox.pack_start(routeWindow, False, False, 0)
1147
1148 return frame
1149
1150 def _buildRouteFrame(self):
1151 """Build the frame for the user-specified route and flight
1152 level."""
1153
1154 (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_route"))
1155
[996]1156 levelBox = Gtk.HBox()
[845]1157 mainBox.pack_start(levelBox, False, False, 0)
1158
1159 label = PIREPViewer.getLabel(xstr("pirepView_filedCruiseLevel"),
1160 xstr("pirepEdit_FL"))
1161 levelBox.pack_start(label, False, False, 0)
1162
[996]1163 self._filedCruiseLevel = Gtk.SpinButton()
[845]1164 self._filedCruiseLevel.set_increments(step = 10, page = 100)
1165 self._filedCruiseLevel.set_range(min = 0, max = 500)
[847]1166 self._filedCruiseLevel.set_tooltip_text(xstr("route_level_tooltip"))
[845]1167 self._filedCruiseLevel.set_numeric(True)
[846]1168 self._filedCruiseLevel.connect("value-changed", self._updateButtons)
[845]1169
1170 levelBox.pack_start(self._filedCruiseLevel, False, False, 0)
1171
1172 PIREPViewer.addHFiller(levelBox)
1173
1174 label = PIREPViewer.getLabel(xstr("pirepView_modifiedCruiseLevel"),
1175 xstr("pirepEdit_FL"))
1176 levelBox.pack_start(label, False, False, 0)
1177
[996]1178 self._modifiedCruiseLevel = Gtk.SpinButton()
[845]1179 self._modifiedCruiseLevel.set_increments(step = 10, page = 100)
1180 self._modifiedCruiseLevel.set_range(min = 0, max = 500)
[847]1181 self._modifiedCruiseLevel.set_tooltip_text(xstr("pirepEdit_modified_route_level_tooltip"))
[845]1182 self._modifiedCruiseLevel.set_numeric(True)
[846]1183 self._modifiedCruiseLevel.connect("value-changed", self._updateButtons)
[845]1184
1185 levelBox.pack_start(self._modifiedCruiseLevel, False, False, 0)
1186
1187 PIREPViewer.addVFiller(mainBox)
1188
1189 (routeWindow, self._userRoute) = \
1190 PIREPViewer.getTextWindow(editable = True)
1191 mainBox.pack_start(routeWindow, False, False, 0)
[846]1192 self._userRoute.get_buffer().connect("changed", self._updateButtons)
[847]1193 self._userRoute.set_tooltip_text(xstr("route_route_tooltip"))
[845]1194
1195 return frame
1196
1197 def _buildDepartureFrame(self):
1198 """Build the frame for the departure data."""
1199 (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_departure"))
1200
1201 mainBox.pack_start(PIREPViewer.getLabel("METAR:"),
1202 False, False, 0)
1203 (metarWindow, self._departureMETAR) = \
1204 PIREPViewer.getTextWindow(heightRequest = -1,
1205 editable = True)
[846]1206 self._departureMETAR.get_buffer().connect("changed", self._updateButtons)
[847]1207 self._departureMETAR.set_tooltip_text(xstr("takeoff_metar_tooltip"))
[845]1208 mainBox.pack_start(metarWindow, True, True, 0)
1209
1210 PIREPViewer.addVFiller(mainBox)
1211
[996]1212 dataBox = Gtk.HBox()
[845]1213 mainBox.pack_start(dataBox, False, False, 0)
1214
[996]1215 label = Gtk.Label("<b>" + xstr("pirepView_runway") + "</b>")
[845]1216 label.set_use_markup(True)
1217 dataBox.pack_start(label, False, False, 0)
1218
1219 # FIXME: quite the same as the runway entry boxes in the wizard
[996]1220 self._departureRunway = Gtk.Entry()
[845]1221 self._departureRunway.set_width_chars(5)
1222 self._departureRunway.set_tooltip_text(xstr("takeoff_runway_tooltip"))
1223 self._departureRunway.connect("changed", self._upperChanged)
1224 dataBox.pack_start(self._departureRunway, False, False, 8)
1225
[996]1226 label = Gtk.Label("<b>" + xstr("pirepView_sid") + "</b>")
[845]1227 label.set_use_markup(True)
1228 dataBox.pack_start(label, False, False, 0)
1229
1230 # FIXME: quite the same as the SID combo box in
1231 # the flight wizard
[996]1232 self._sid = Gtk.ComboBox.new_with_model_and_entry(comboModel)
[845]1233
1234 self._sid.set_entry_text_column(0)
1235 self._sid.get_child().set_width_chars(10)
1236 self._sid.set_tooltip_text(xstr("takeoff_sid_tooltip"))
1237 self._sid.connect("changed", self._upperChangedComboBox)
1238
1239 dataBox.pack_start(self._sid, False, False, 8)
1240
1241 return frame
1242
1243 def _buildArrivalFrame(self):
1244 """Build the frame for the arrival data."""
1245 (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_arrival"))
1246
1247 mainBox.pack_start(PIREPViewer.getLabel("METAR:"),
1248 False, False, 0)
1249 (metarWindow, self._arrivalMETAR) = \
1250 PIREPViewer.getTextWindow(heightRequest = -1,
1251 editable = True)
[846]1252 self._arrivalMETAR.get_buffer().connect("changed", self._updateButtons)
[847]1253 self._arrivalMETAR.set_tooltip_text(xstr("landing_metar_tooltip"))
[845]1254 mainBox.pack_start(metarWindow, True, True, 0)
1255
1256 PIREPViewer.addVFiller(mainBox)
1257
[996]1258 table = Gtk.Table(2, 4)
[845]1259 mainBox.pack_start(table, False, False, 0)
1260 table.set_row_spacings(4)
1261 table.set_col_spacings(8)
1262
1263 # FIXME: quite the same as in the wizard
[996]1264 self._star = Gtk.ComboBox.new_with_model_and_entry(comboModel)
[845]1265
1266 self._star.set_entry_text_column(0)
1267 self._star.get_child().set_width_chars(10)
1268 self._star.set_tooltip_text(xstr("landing_star_tooltip"))
1269 self._star.connect("changed", self._upperChangedComboBox)
1270
1271 PIREPEditor.tableAttachWidget(table, 0, 0,
1272 xstr("pirepView_star"),
1273 self._star)
1274
1275 # FIXME: quite the same as in the wizard
[996]1276 self._transition = Gtk.ComboBox.new_with_model_and_entry(comboModel)
[845]1277
1278 self._transition.set_entry_text_column(0)
1279 self._transition.get_child().set_width_chars(10)
1280 self._transition.set_tooltip_text(xstr("landing_transition_tooltip"))
1281 self._transition.connect("changed", self._upperChangedComboBox)
1282
1283 PIREPEditor.tableAttachWidget(table, 2, 0,
1284 xstr("pirepView_transition"),
1285 self._transition)
1286
1287
1288 # FIXME: quite the same as in the wizard
[996]1289 self._approachType = Gtk.Entry()
[845]1290 self._approachType.set_width_chars(10)
1291 self._approachType.set_tooltip_text(xstr("landing_approach_tooltip"))
1292 self._approachType.connect("changed", self._upperChanged)
1293
1294 PIREPEditor.tableAttachWidget(table, 0, 1,
1295 xstr("pirepView_approachType"),
1296 self._approachType)
1297
1298 # FIXME: quite the same as in the wizard
[996]1299 self._arrivalRunway = Gtk.Entry()
[845]1300 self._arrivalRunway.set_width_chars(10)
1301 self._arrivalRunway.set_tooltip_text(xstr("landing_runway_tooltip"))
1302 self._arrivalRunway.connect("changed", self._upperChanged)
1303
1304 PIREPEditor.tableAttachWidget(table, 2, 1,
1305 xstr("pirepView_runway"),
1306 self._arrivalRunway)
1307
1308 return frame
1309
1310 def _buildStatisticsFrame(self):
1311 """Build the frame for the statistics data."""
1312 (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_statistics"))
1313
[996]1314 table = Gtk.Table(4, 4)
[845]1315 mainBox.pack_start(table, False, False, 0)
1316 table.set_row_spacings(4)
1317 table.set_col_spacings(8)
1318 table.set_homogeneous(False)
1319
1320 self._blockTimeStart = \
1321 PIREPEditor.tableAttachTimeEntry(table, 0, 0,
1322 xstr("pirepView_blockTimeStart"))
[846]1323 self._blockTimeStart.connect("changed", self._updateButtons)
[847]1324 self._blockTimeStart.set_tooltip_text(xstr("pirepEdit_block_time_start_tooltip"))
[845]1325
1326 self._blockTimeEnd = \
1327 PIREPEditor.tableAttachTimeEntry(table, 2, 0,
1328 xstr("pirepView_blockTimeEnd"))
[846]1329 self._blockTimeEnd.connect("changed", self._updateButtons)
[847]1330 self._blockTimeEnd.set_tooltip_text(xstr("pirepEdit_block_time_end_tooltip"))
[845]1331
1332 self._flightTimeStart = \
1333 PIREPEditor.tableAttachTimeEntry(table, 0, 1,
1334 xstr("pirepView_flightTimeStart"))
[846]1335 self._flightTimeStart.connect("changed", self._updateButtons)
[847]1336 self._flightTimeStart.set_tooltip_text(xstr("pirepEdit_flight_time_start_tooltip"))
[845]1337
1338 self._flightTimeEnd = \
1339 PIREPEditor.tableAttachTimeEntry(table, 2, 1,
1340 xstr("pirepView_flightTimeEnd"))
[846]1341 self._flightTimeEnd.connect("changed", self._updateButtons)
[847]1342 self._flightTimeEnd.set_tooltip_text(xstr("pirepEdit_flight_time_end_tooltip"))
[845]1343
1344 self._flownDistance = PIREPViewer.getDataLabel(width = 3)
1345 PIREPEditor.tableAttachWidget(table, 0, 2,
1346 xstr("pirepView_flownDistance"),
1347 self._flownDistance)
1348
1349 self._fuelUsed = \
1350 PIREPEditor.tableAttachSpinButton(table, 2, 2,
1351 xstr("pirepView_fuelUsed"),
1352 1000000)
[846]1353 self._fuelUsed.connect("value-changed", self._updateButtons)
[847]1354 self._fuelUsed.set_tooltip_text(xstr("pirepEdit_fuel_used_tooltip"))
[845]1355
1356 self._rating = PIREPViewer.getDataLabel(width = 3)
1357 PIREPEditor.tableAttachWidget(table, 0, 3,
1358 xstr("pirepView_rating"),
1359 self._rating)
1360 return frame
1361
1362 def _buildMiscellaneousFrame(self):
1363 """Build the frame for the miscellaneous data."""
1364 (frame, mainBox) = PIREPViewer.createFrame(xstr("pirepView_frame_miscellaneous"))
1365
[996]1366 table = Gtk.Table(6, 2)
[845]1367 mainBox.pack_start(table, False, False, 0)
1368 table.set_row_spacings(4)
1369 table.set_col_spacings(8)
1370 table.set_homogeneous(False)
1371
[1033]1372 label = Gtk.Label("<b>" + xstr("pirepView_numPassengers") + "</b>")
1373 label.set_use_markup(True)
1374 alignment = Gtk.Alignment(xalign = 0.0, yalign = 0.5,
1375 xscale = 0.0, yscale = 0.0)
1376 alignment.add(label)
1377 table.attach(alignment, 0, 1, 0, 1)
1378
1379
1380
1381 self._flownNumPassengers = button = Gtk.SpinButton()
1382 button.set_range(min = 0, max = 300)
1383 button.set_increments(step = 1, page = 10)
1384 button.set_numeric(True)
1385 button.set_width_chars(2)
1386 button.set_alignment(1.0)
1387 button.connect("value-changed", self._updateButtons)
1388 button.set_tooltip_text(xstr("payload_pax_tooltip"))
[845]1389
[1033]1390 self._flownNumChildren = button = Gtk.SpinButton()
1391 button.set_range(min = 0, max = 300)
1392 button.set_increments(step = 1, page = 10)
1393 button.set_numeric(True)
1394 button.set_width_chars(2)
1395 button.set_alignment(1.0)
1396 button.connect("value-changed", self._updateButtons)
1397 button.set_tooltip_text(xstr("payload_pax_children_tooltip"))
1398
1399 self._flownNumInfants = button = Gtk.SpinButton()
1400 button.set_range(min = 0, max = 300)
1401 button.set_increments(step = 1, page = 10)
1402 button.set_numeric(True)
1403 button.set_width_chars(2)
1404 button.set_alignment(1.0)
1405 button.connect("value-changed", self._updateButtons)
1406 button.set_tooltip_text(xstr("payload_pax_infants_tooltip"))
1407
1408 paxBox = Gtk.HBox()
1409 paxBox.pack_start(self._flownNumPassengers, False, False, 0)
1410 paxBox.pack_start(Gtk.Label("+"), False, False, 4)
1411 paxBox.pack_start(self._flownNumChildren, False, False, 0)
1412 paxBox.pack_start(Gtk.Label("+"), False, False, 4)
1413 paxBox.pack_start(self._flownNumInfants, False, False, 0)
1414 paxBox.set_halign(Gtk.Align.END)
1415
1416 table.attach(paxBox, 1, 4, 0, 1)
1417
1418 self._flownNumCabinCrew = \
1419 PIREPEditor.tableAttachSpinButton(table, 4, 0,
[845]1420 xstr("pirepView_numCrew"),
1421 10)
[1033]1422 self._flownNumCabinCrew.connect("value-changed", self._updateButtons)
1423 self._flownNumCabinCrew.set_tooltip_text(xstr("payload_crew_tooltip"))
[845]1424
1425 self._flownBagWeight = \
1426 PIREPEditor.tableAttachSpinButton(table, 0, 1,
1427 xstr("pirepView_bagWeight"),
1428 100000, width = 6)
[849]1429 self._flownBagWeight.connect("value-changed", self._updateButtons)
[847]1430 self._flownBagWeight.set_tooltip_text(xstr("payload_bag_tooltip"))
[845]1431
1432 self._flownCargoWeight = \
1433 PIREPEditor.tableAttachSpinButton(table, 2, 1,
1434 xstr("pirepView_cargoWeight"),
1435 100000, width = 6)
[849]1436 self._flownCargoWeight.connect("value-changed", self._updateButtons)
[847]1437 self._flownCargoWeight.set_tooltip_text(xstr("payload_cargo_tooltip"))
[845]1438
1439 self._flownMailWeight = \
1440 PIREPEditor.tableAttachSpinButton(table, 4, 1,
1441 xstr("pirepView_mailWeight"),
1442 100000, width = 6)
[849]1443 self._flownMailWeight.connect("value-changed", self._updateButtons)
[847]1444 self._flownMailWeight.set_tooltip_text(xstr("payload_mail_tooltip"))
[845]1445
1446 self._flightType = createFlightTypeComboBox()
1447 PIREPEditor.tableAttachWidget(table, 0, 2,
1448 xstr("pirepView_flightType"),
1449 self._flightType)
[849]1450 self._flightType.connect("changed", self._updateButtons)
[847]1451 self._flightType.set_tooltip_text(xstr("pirepEdit_flight_type_tooltip"))
[845]1452
[996]1453 self._online = Gtk.CheckButton(xstr("pirepEdit_online"))
[845]1454 table.attach(self._online, 2, 3, 2, 3)
[849]1455 self._online.connect("toggled", self._updateButtons)
[847]1456 self._online.set_tooltip_text(xstr("pirepEdit_online_tooltip"))
[845]1457
1458 PIREPViewer.addVFiller(mainBox)
1459
1460 mainBox.pack_start(PIREPViewer.getLabel(xstr("pirepView_delayCodes")),
1461 False, False, 0)
1462
1463 (textWindow, self._delayCodes) = PIREPViewer.getTextWindow()
1464 mainBox.pack_start(textWindow, False, False, 0)
[847]1465 self._delayCodes.set_tooltip_text(xstr("pirepEdit_delayCodes_tooltip"))
[845]1466
1467 return frame
1468
1469 def _buildCommentsTab(self):
1470 """Build the tab with the comments and flight defects."""
[846]1471 return FlightInfo(self._gui, callbackObject = self)
[845]1472
1473 def _buildLogTab(self):
1474 """Build the log tab."""
[996]1475 mainBox = Gtk.VBox()
[845]1476
1477 (logWindow, self._log) = PIREPViewer.getTextWindow(heightRequest = -1)
1478 addFaultTag(self._log.get_buffer())
1479 mainBox.pack_start(logWindow, True, True, 0)
1480
1481 return mainBox
1482
1483 def _upperChanged(self, entry, arg = None):
1484 """Called when the value of some entry widget has changed and the value
1485 should be converted to uppercase."""
1486 entry.set_text(entry.get_text().upper())
[846]1487 self._updateButtons()
[845]1488 #self._valueChanged(entry, arg)
1489
1490 def _upperChangedComboBox(self, comboBox):
1491 """Called for combo box widgets that must be converted to uppercase."""
1492 entry = comboBox.get_child()
1493 if comboBox.get_active()==-1:
1494 entry.set_text(entry.get_text().upper())
[846]1495 self._updateButtons()
[845]1496 #self._valueChanged(entry)
1497
[846]1498 def _updateButtons(self, *kwargs):
1499 """Update the activity state of the buttons."""
1500 pirep = self._pirep
1501 bookedFlight = pirep.bookedFlight
1502
1503 departureMinutes = \
1504 bookedFlight.departureTime.hour*60 + bookedFlight.departureTime.minute
1505 departureDifference = abs(Flight.getMinutesDifference(self._blockTimeStart.minutes,
1506 departureMinutes))
1507 flightStartDifference = \
1508 Flight.getMinutesDifference(self._flightTimeStart.minutes,
1509 self._blockTimeStart.minutes)
1510 arrivalMinutes = \
1511 bookedFlight.arrivalTime.hour*60 + bookedFlight.arrivalTime.minute
1512 arrivalDifference = abs(Flight.getMinutesDifference(self._blockTimeEnd.minutes,
1513 arrivalMinutes))
1514 flightEndDifference = \
1515 Flight.getMinutesDifference(self._blockTimeEnd.minutes,
1516 self._flightTimeEnd.minutes)
1517
1518 timesOK = self._flightInfo.hasComments or \
1519 self._flightInfo.hasDelayCode or \
1520 (departureDifference<=Flight.TIME_ERROR_DIFFERENCE and
1521 arrivalDifference<=Flight.TIME_ERROR_DIFFERENCE and
1522 flightStartDifference>=0 and flightStartDifference<30 and
1523 flightEndDifference>=0 and flightEndDifference<30)
1524
1525 text = self._sid.get_child().get_text()
1526 sid = text if self._sid.get_active()!=0 and text and text!="N/A" \
1527 else None
1528
1529 text = self._star.get_child().get_text()
1530 star = text if self._star.get_active()!=0 and text and text!="N/A" \
1531 else None
1532
1533 text = self._transition.get_child().get_text()
1534 transition = text if self._transition.get_active()!=0 \
1535 and text and text!="N/A" else None
1536
1537
1538 buffer = self._userRoute.get_buffer()
1539 route = buffer.get_text(buffer.get_start_iter(),
1540 buffer.get_end_iter(), True)
1541
[1033]1542 numPassengers = \
1543 self._flownNumPassengers.get_value_as_int() + \
1544 self._flownNumChildren.get_value_as_int() + \
1545 self._flownNumInfants.get_value_as_int()
1546
1547 minCabinCrew = 0 if numPassengers==0 else \
1548 (bookedFlight.maxPassengers // 50) + 1
1549
[849]1550 self._okButton.set_sensitive(self._modified and timesOK and
[846]1551 self._flightInfo.faultsFullyExplained and
[1033]1552 numPassengers<=bookedFlight.maxPassengers and
1553 self._flownNumCabinCrew.get_value_as_int()>=minCabinCrew and
[846]1554 self._fuelUsed.get_value_as_int()>0 and
1555 self._departureRunway.get_text_length()>0 and
1556 self._arrivalRunway.get_text_length()>0 and
1557 self._departureMETAR.get_buffer().get_char_count()>0 and
1558 self._arrivalMETAR.get_buffer().get_char_count()>0 and
1559 self._filedCruiseLevel.get_value_as_int()>=50 and
1560 self._modifiedCruiseLevel.get_value_as_int()>=50 and
1561 sid is not None and (star is not None or
1562 transition is not None) and route!="" and
1563 self._approachType.get_text()!="")
1564
[853]1565 def _okClicked(self, button):
1566 """Called when the OK button has been clicked.
1567
1568 The PIREP is updated from the data in the window."""
1569 if not askYesNo(xstr("pirepEdit_save_question"), parent = self):
[999]1570 self.response(Gtk.ResponseType.CANCEL)
[853]1571
1572 pirep = self._pirep
1573
1574 pirep.filedCruiseAltitude = \
1575 self._filedCruiseLevel.get_value_as_int() * 100
1576 pirep.cruiseAltitude = \
1577 self._modifiedCruiseLevel.get_value_as_int() * 100
1578
1579 pirep.route = getTextViewText(self._userRoute)
1580
1581 pirep.departureMETAR = getTextViewText(self._departureMETAR)
1582 pirep.departureRunway = self._departureRunway.get_text()
1583 pirep.sid = self._sid.get_child().get_text()
1584
1585 pirep.arrivalMETAR = getTextViewText(self._arrivalMETAR)
1586 pirep.star = None if self._star.get_active()==0 \
1587 else self._star.get_child().get_text()
1588 pirep.transition = None if self._transition.get_active()==0 \
1589 else self._transition.get_child().get_text()
1590 pirep.approachType = self._approachType.get_text()
1591 pirep.arrivalRunway = self._arrivalRunway.get_text()
1592
1593 pirep.blockTimeStart = \
1594 self._blockTimeStart.getTimestampFrom(pirep.blockTimeStart)
1595 pirep.blockTimeEnd = \
1596 self._blockTimeEnd.getTimestampFrom(pirep.blockTimeEnd)
1597 pirep.flightTimeStart = \
1598 self._flightTimeStart.getTimestampFrom(pirep.flightTimeStart)
1599 pirep.flightTimeEnd = \
1600 self._flightTimeEnd.getTimestampFrom(pirep.flightTimeEnd)
1601
1602 pirep.fuelUsed = self._fuelUsed.get_value()
1603
[1033]1604 pirep.numCabinCrew = self._flownNumCabinCrew.get_value()
[853]1605 pirep.numPassengers = self._flownNumPassengers.get_value()
1606 pirep.bagWeight = self._flownBagWeight.get_value()
1607 pirep.cargoWeight = self._flownCargoWeight.get_value()
1608 pirep.mailWeight = self._flownMailWeight.get_value()
1609
1610 pirep.flightType = flightTypes[self._flightType.get_active()]
1611 pirep.online = self._online.get_active()
1612
1613 pirep.delayCodes = self._flightInfo.delayCodes
1614 pirep.comments = self._flightInfo.comments
1615 pirep.flightDefects = self._flightInfo.faultsAndExplanations
1616
[999]1617 self.response(Gtk.ResponseType.OK)
[853]1618
1619
[845]1620#------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.