#!/usr/bin/env python #**************************************************************************** # routeview.py, provides the view for the route table # # FlyWay, a VFR/IFR Route Planner for Pilots # Copyright (C) 2002, Douglas W. Bell # # This is free software; you can redistribute it and/or modify it under the # terms of the GNU General Public License, Version 2. This program is # distributed in the hope that it will be useful, but WITTHOUT ANY WARRANTY. #***************************************************************************** from route import RouteLeg from units import Time, Angle from textpaint import drawCenterText, drawRightText from tmpcontrol import TmpEdit from flydialogs import SearchDlg import flymainwin from qt import * class RouteView(QScrollView): """Main route window""" wpStart = (50, 70) descStart = 80 colStart = 130 colSize = 68 rowSize = 22 totalWidth = 510 margin = 10 paramStart = 15 editStart = 30 headings = ('Course', 'Heading', 'Distance', 'Grnd Spd', 'ETE', 'ETA') def __init__(self, parent=None, name=None): QScrollView.__init__(self, parent, name) self.viewport().setBackgroundMode(QWidget.PaletteBase) self.viewport().setFocusPolicy(QWidget.StrongFocus) self.numSelect = 0 self.tmpEdit = None self.normFont = self.font() self.ulFont = self.font() self.ulFont.setUnderline(1) self.boldFont = self.font() self.boldFont.setWeight(QFont.Bold) normSize = self.font().pointSize() # self.smFont = self.font() # self.smFont.setPointSize(normSize - 2) self.smFont = QFont('helvetica', normSize - 1) self.lgFont = QFont('helvetica', normSize + 8) self.recalc() def recalc(self, updateViews=1): """Update legs & view size & repaint""" option = flymainwin.FlyMainWin.option flymainwin.FlyMainWin.route.updateLegs() self.resizeContents(RouteView.totalWidth, RouteView.wpStart[1] \ + len(flymainwin.FlyMainWin.route.wpList) \ * RouteView.rowSize * 2) self.airspeedStr = 'Airspeed: %d kts' % \ option.intData('Airspeed', 1, RouteLeg.maxAirspeed) windSpeed = option.intData('WindSpeed', 0, RouteLeg.maxWindSpeed) if windSpeed: self.windStr = 'Wind: %s at %d kts' % \ (Angle(option.intData('WindDir', 0, 360)).\ angleStr(), windSpeed) else: self.windStr = 'Wind: none' is12hrClock = option.boolData('12hrClock') self.departStr = 'Departure Time: %s' % \ Time(option.intData('DepartHour', 0, 23) \ + option.intData('DepartMinute', 0, 59) \ / 60.0).timeStr(not is12hrClock, is12hrClock) self.setSelectWp(self.numSelect) self.viewport().update() if updateViews: self.emit(PYSIGNAL('recalcDone'), ()) def paintTable(self, painter, startRow=None, stopRow=None): """Paint table of parameters, waypoints and legs""" if startRow == None: startRow = -1 if stopRow == None: stopRow = len(flymainwin.FlyMainWin.route.wpList) * 2 if startRow < 0: # paint parameters and column headings painter.setFont(self.normFont) painter.drawText(RouteView.margin, RouteView.paramStart, \ self.airspeedStr) drawCenterText(painter, RouteView.totalWidth / 2, \ RouteView.paramStart, self.windStr) drawRightText(painter, RouteView.totalWidth - RouteView.margin, \ RouteView.paramStart, self.departStr) painter.drawLine(RouteView.margin, \ RouteView.paramStart + 5, \ RouteView.totalWidth - RouteView.margin, \ RouteView.paramStart + 5) painter.setFont(self.ulFont) self.__drawRow(painter, RouteView.colStart, \ RouteView.wpStart[1] - RouteView.rowSize, \ RouteView.headings) y = RouteView.wpStart[1] startRow = startRow * RouteView.rowSize + RouteView.wpStart[1] stopRow = stopRow * RouteView.rowSize + RouteView.wpStart[1] for wp in flymainwin.FlyMainWin.route.wpList: # paint waypoints if startRow <= y < stopRow: painter.setFont(self.boldFont) drawCenterText(painter, RouteView.wpStart[0], y, wp.ident()) painter.setFont(self.smFont) painter.drawText(RouteView.descStart, y, wp.freqDescription()) y += 2 * RouteView.rowSize painter.setFont(self.normFont) y = RouteView.wpStart[1] + RouteView.rowSize for leg in flymainwin.FlyMainWin.route.legList: # paint legs if startRow <= y < stopRow: self.__drawRow(painter, RouteView.colStart, y, leg.output()) y += 2 * RouteView.rowSize if len(flymainwin.FlyMainWin.route.legList) > 1 and \ startRow <= y < stopRow: painter.setFont(self.ulFont) # paint totals painter.drawText(RouteView.descStart, y, \ 'Totals:') painter.setFont(self.normFont) self.__drawRow(painter, RouteView.colStart, y, leg.outputTotals()) def drawContents(self, painter, clipX=None, clipY=None, \ clipW=None, clipH=None): """Paint view""" if clipX == None: # none is for wrong overridden function return QFrame.drawContents(self, painter) pal = self.palette() painter.setPen(pal.color(QPalette.Active, QColorGroup.Text)) self.paintTable(painter) if flymainwin.FlyMainWin.route.wpList: painter.setFont(self.lgFont) painter.drawText(RouteView.margin, RouteView.wpStart[1] + 3 \ + RouteView.rowSize * 2 * self.numSelect, \ chr(187)) def __drawRow(self, painter, x, y, list): for text in list: drawCenterText(painter, x, y, text) x += RouteView.colSize def insertWaypoint(self): rowPos = RouteView.wpStart[1] + RouteView.rowSize * 2 * self.numSelect self.ensureVisible(RouteView.wpStart[0], rowPos, 12, 12) self.tmpEdit = TmpEdit('', self.viewport()) pos = self.contentsToViewport(RouteView.editStart, rowPos - 16) self.tmpEdit.setGeometry(pos[0], pos[1], RouteView.totalWidth \ - RouteView.editStart - RouteView.margin, \ RouteView.rowSize) self.tmpEdit.show() self.tmpEdit.setFocus() self.connect(self.tmpEdit, PYSIGNAL('editDone'), self.insertDone) flymainwin.FlyMainWin.toolBar.setEnabled(0) self.emit(PYSIGNAL('statusBarMsg'), \ ('Enter ID or search string', 1000000)) def insertDone(self): """Called by tmpEdit after return or focus change""" wp = None self.emit(PYSIGNAL('statusBarMsg'), ('', 1000)) flymainwin.FlyMainWin.toolBar.setEnabled(1) text = str(self.tmpEdit.text()) if text: QApplication.setOverrideCursor(Qt.waitCursor) results = flymainwin.FlyMainWin.route.findWp(text) if not results: # none found self.tmpEdit.clear() self.tmpEdit.active = 1 self.emit(PYSIGNAL('statusBarMsg'), \ ('Nothing found, try again', 1000000)) QApplication.restoreOverrideCursor() return if len(results) == 1: # one find wp = results[0] QApplication.restoreOverrideCursor() else: # multiple finds dlg = SearchDlg(results, text, self, None, 1) QApplication.restoreOverrideCursor() if dlg.exec_loop() == QDialog.Accepted: wp = dlg.result() QApplication.restoreOverrideCursor() if flymainwin.FlyMainWin.route.wpList: del flymainwin.FlyMainWin.route.wpList[self.numSelect] if wp: flymainwin.FlyMainWin.route.wpList.insert(self.numSelect, wp) flymainwin.FlyMainWin.docModified = 1 else: if self.numSelect: self.numSelect -= 1 self.tmpEdit.close(1) self.tmpEdit = None self.setFocus() self.recalc() def editInBefore(self): """Add new waypoint before the selected one""" wpList = flymainwin.FlyMainWin.route.wpList if wpList: wpList.insert(self.numSelect, wpList[self.numSelect]) self.recalc(0) self.insertWaypoint() def editInAfter(self): """Add new waypoint after the selected one""" if flymainwin.FlyMainWin.route.wpList: flymainwin.FlyMainWin.route.wpList.insert(self.numSelect + 1, \ flymainwin.FlyMainWin.route.\ wpList[self.numSelect]) self.numSelect += 1 self.recalc(0) self.insertWaypoint() def editDelete(self): """Delete selected waypoint""" if flymainwin.FlyMainWin.route.wpList: del flymainwin.FlyMainWin.route.wpList[self.numSelect] if self.numSelect >= len(flymainwin.FlyMainWin.route.wpList) and \ flymainwin.FlyMainWin.route.wpList: self.numSelect = len(flymainwin.FlyMainWin.route.wpList) - 1 flymainwin.FlyMainWin.docModified = 1 self.recalc() def editUp(self): """Move the selected waypoint upward""" if self.numSelect > 0: flymainwin.FlyMainWin.route.wpList[self.numSelect], \ flymainwin.FlyMainWin.route.wpList[self.numSelect - 1] = \ flymainwin.FlyMainWin.route.wpList[self.numSelect - 1], \ flymainwin.FlyMainWin.route.wpList[self.numSelect] self.numSelect = self.numSelect - 1 flymainwin.FlyMainWin.docModified = 1 self.recalc() def editDown(self): """Move the selected waypoint downward""" if self.numSelect < len(flymainwin.FlyMainWin.route.wpList) - 1: flymainwin.FlyMainWin.route.wpList[self.numSelect], \ flymainwin.FlyMainWin.route.wpList[self.numSelect + 1] = \ flymainwin.FlyMainWin.route.wpList[self.numSelect + 1], \ flymainwin.FlyMainWin.route.wpList[self.numSelect] self.numSelect = self.numSelect + 1 flymainwin.FlyMainWin.docModified = 1 self.recalc() def editReverse(self): """Reverse the order of all waypoints""" if flymainwin.FlyMainWin.route.wpList: flymainwin.FlyMainWin.route.wpList.reverse() self.numSelect = 0 flymainwin.FlyMainWin.docModified = 1 self.recalc() def setSelectWp(self, num): """Set selected waypoint number""" if num < len(flymainwin.FlyMainWin.route.wpList): self.ensureVisible(RouteView.wpStart[0], RouteView.wpStart[1] \ + RouteView.rowSize * 2 * num, 12, 12) self.numSelect = num self.emit(PYSIGNAL('selChange'), \ (flymainwin.FlyMainWin.route.wpList[num],)) else: self.numSelect = 0 self.emit(PYSIGNAL('selChange'), (None,)) self.viewport().update() def contentsMousePressEvent(self, event): """Mouse waypoint selection control""" line = (event.pos().y() - RouteView.wpStart[1] + 16) \ / RouteView.rowSize if not line % 2 and \ 0 <= line / 2 < len(flymainwin.FlyMainWin.route.wpList): self.setSelectWp(line / 2) def contentsMouseReleaseEvent(self, event): """Mouse waypoint selection control""" if event.button() == Qt.RightButton: line = (event.pos().y() - RouteView.wpStart[1] + 16) \ / RouteView.rowSize if not line % 2 and line / 2 == self.numSelect: pt = self.mapToGlobal(self.contentsToViewport(event.pos())) flymainwin.FlyMainWin.routePopup.popup(pt) def keyPressEvent(self, event): """Key bindings""" if event.key() == Qt.Key_Up: if self.numSelect > 0: self.setSelectWp(self.numSelect - 1) elif event.key() == Qt.Key_Down: if self.numSelect < len(flymainwin.FlyMainWin.route.wpList) - 1: self.setSelectWp(self.numSelect + 1) elif event.key() == Qt.Key_PageUp: self.scrollBy(0, -self.visibleHeight() + RouteView.rowSize) elif event.key() == Qt.Key_PageDown: self.scrollBy(0, self.visibleHeight() - RouteView.rowSize) elif event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: if flymainwin.FlyMainWin.option.boolData('AddOnEnter'): self.editInAfter() elif event.key() == Qt.Key_Delete: if flymainwin.FlyMainWin.option.boolData('DelKey'): self.editDelete() else: QWidget.keyPressEvent(self, event)