def add_menu_section_action(text, menu, tag='b', pad=0.5): """Because QMenu.addSection() fails to render with some UI styles, and QWidgetAction defaults to no padding. :param text: Text for action's title :type text: str :param menu: QMenu to add section action :type menu: QMenu :param tag: Simple HTML tag (sans < or >) to style the text, e.g. b, i, u :type tag: str :param pad: Value for QLabel qss em and ex padding :type pad: float """ lbl = QLabel(f'<{tag}>{text}</{tag}>', menu) lbl.setStyleSheet( f'QLabel {{ padding-left: {pad}em; padding-right: {pad}em; ' f'padding-top: {pad}ex; padding-bottom: {pad}ex;}}') wa = QWidgetAction(menu) wa.setDefaultWidget(lbl) menu.addAction(wa) return wa
class ExpressRouteAction(QAction): def __init__(self, main): super().__init__(resources.icon_routes_express, tr("Quick route")) self.setCheckable(True) self.main = main self.widget = ExpressRouteWidget() self.widgetAction = QWidgetAction(self) self.widgetAction.setDefaultWidget(self.widget) self.menu = QMenu() # self.menu.aboutToShow.connect(self.start_tool) self.menu.addAction(self.widgetAction) self.setMenu(self.menu) self.triggered.connect(self.start_tool) def start_tool(self): self.tool = ExpressRouteTool(self, self.main.iface.mapCanvas()) self.main.iface.mapCanvas().setMapTool(self.tool)
def filterMenu(self, table, pos): index = table.columnAt(pos.x()) menu = QMenu() filter_operation = QComboBox() if table.types[index] in [10]: filter_operation.addItems([self.tr('Contains'),self.tr('Equals')]) else: filter_operation.addItems(['=','>','<']) filter_operation.setCurrentIndex(table.filter_op[index]) action_filter_operation = QWidgetAction(self) action_filter_operation.setDefaultWidget(filter_operation) if table.types[index] in [14]: if not isinstance(table.filters[index], QDate): filter_value = QDateEdit() else: filter_value = QDateEdit(table.filters[index]) elif table.types[index] in [15]: if not isinstance(table.filters[index], QTime): filter_value = QTimeEdit() else: filter_value = QTimeEdit(table.filters[index]) elif table.types[index] in [16]: if not isinstance(table.filters[index], QDateTime): filter_value = QDateTimeEdit() else: filter_value = QDateTimeEdit(table.filters[index]) else: filter_value = QLineEdit(table.filters[index]) action_filter_value = QWidgetAction(self) action_filter_value.setDefaultWidget(filter_value) menu.addAction(action_filter_operation) menu.addAction(action_filter_value) action_filter_apply = QAction(self.tr('Apply'), self) action_filter_apply.triggered.connect(partial(self.applyFilter, table, index, filter_value, filter_operation)) action_filter_cancel = QAction(self.tr('Cancel'), self) action_filter_cancel.triggered.connect(partial(self.applyFilter, table, index, None, filter_operation)) menu.addAction(action_filter_apply) menu.addAction(action_filter_cancel) menu.exec_(QtGui.QCursor.pos())
class ExpressActionBase(QAction): _icon = None # to be defined by subclasses _name = None # to be defined by subclasses _widget_ui = None # to be defined by subclasses _algorithm = None # to be defined by subclasses def __init__(self, main): super().__init__(self._icon, self._name) self.setCheckable(True) self.main = main # Build the widget self.widget = QWidget() uic.loadUi( os.path.join(os.path.dirname(__file__), "ui", self._widget_ui), self.widget) self.widget.dateTimeEdit.setDateTime(QDateTime.currentDateTime()) self.widgetAction = QWidgetAction(self) self.widgetAction.setDefaultWidget(self.widget) self.menu = QMenu() self.menu.addAction(self.widgetAction) self.setMenu(self.menu) # Build the tool self.tool = QgsMapToolEmitPoint(self.main.iface.mapCanvas()) self.tool.activated.connect(lambda: self.setChecked(True)) self.tool.deactivated.connect(lambda: self.setChecked(False)) self.tool.canvasClicked.connect(self.tool_clicked) # Connect the action self.triggered.connect(self.start_tool) def start_tool(self): self.main.iface.mapCanvas().setMapTool(self.tool) def make_params(self, point): DEPARR = ("DEPARTURE" if self.widget.deparrComboBox.currentIndex() == 0 else "ARRIVAL") input_layer = pointToLayer(point) time = self.widget.dateTimeEdit.dateTime().toUTC().toString(Qt.ISODate) transpt_type = self.widget.transptTypeComboBox.currentText() params = { "INPUT_" + DEPARR + "_SEARCHES": input_layer, "INPUT_" + DEPARR + "_TIME": "'" + time + "'", "INPUT_" + DEPARR + "_TRNSPT_TYPE": "'" + transpt_type + "'", "OUTPUT": "memory:", } if hasattr(self.widget, "travelTimeSpinBox"): travel_time = self.widget.travelTimeSpinBox.value() * 60 params.update({"INPUT_" + DEPARR + "_TRAVEL_TIME": travel_time}) return params def tool_clicked(self, point): params = self.make_params(point) class Feedback(QgsProcessingFeedback): def __init__(self): super().__init__() self.fatal_errors = [] def reportError(self, error, fatalError=False): log(error) if fatalError: self.fatal_errors.append(error) feedback = Feedback() try: # TODO : use QgsProcessingAlgRunnerTask to do this as a bg task processing.runAndLoadResults(self._algorithm, params, feedback=feedback) except QgsProcessingException as e: print(e) self.main.iface.messageBar().pushMessage( "Error", ", ".join(feedback.fatal_errors), level=Qgis.Critical, duration=0, )
class TrackingDisplay(QToolBar): ''' Display the position of a mobile and add action for centering the map on the vehicle and erasing the track ''' def __init__(self, mobile, parent=None): super(TrackingDisplay, self).__init__(parent) self.setMovable(True) self.setFloatable(True) self.mobile = mobile self.timedOut = True self.lastFix = 0.0 s = QSettings() self.defFormat = s.value('PosiView/Misc/DefaultFormat', defaultValue=0, type=int) self.format = self.defFormat & 3 self.withSuff = QgsCoordinateFormatter.FlagDegreesUseStringSuffix if bool(self.defFormat & 4) else QgsCoordinateFormatter.FormatFlag(0) self.createActions() self.mobile.newPosition.connect(self.onNewPosition) self.mobile.timeout.connect(self.onTimeout) def createActions(self): self.nameLabel = QLabel(self.mobile.name) self.nameLabel.setMinimumSize(80, 23) self.nameLabelAction = QWidgetAction(self) self.nameLabelAction.setDefaultWidget(self.nameLabel) self.addAction(self.nameLabelAction) self.enableAction = QAction("Enable Display", self) self.enableAction.setCheckable(True) self.enableAction.setChecked(True) icon = QIcon(':/plugins/PosiView/ledgrey.png') icon.addFile(':/plugins/PosiView/ledgreen.png', QSize(), QIcon.Normal, QIcon.On) self.enableAction.setIcon(icon) self.addAction(self.enableAction) self.enableAction.triggered.connect(self.onEnableClicked) self.enableAction.triggered.connect(self.mobile.setEnabled) self.addSeparator() self.posLabel = QLabel("--:--:-- 0.000000 0.000000") self.posLabel.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) widths = (180, 196, 204, 180, 184, 200, 208, 184) self.posLabel.setMinimumSize(widths[self.format], 23) self.posLabel.setStyleSheet('background: red; font-size: 8pt; color: white;') self.posLabelAction = QWidgetAction(self) self.posLabelAction.setDefaultWidget(self.posLabel) self.addAction(self.posLabelAction) self.centerAction = QAction(QIcon(':/plugins/PosiView/center.png'), "Center &Map", self) self.addAction(self.centerAction) self.deleteTrackAction = QAction(QIcon(':/plugins/PosiView/deletetrack.png'), 'Delete &Track', self) self.addAction(self.deleteTrackAction) self.deleteTrackAction.triggered.connect(self.mobile.deleteTrack) self.centerAction.triggered.connect(self.mobile.centerOnMap) @pyqtSlot(float, QgsPointXY, float, float) def onNewPosition(self, fix, pos, depth, altitude): s = str() if fix > 0: s = strftime('%H:%M:%S ', gmtime(fix)) else: s = '--:--:-- ' if self.format == 0: s += "{:f} {:f}".format(pos.y(), pos.x()) elif self.format == 1: s += ', '.join(QgsCoordinateFormatter.format(pos, QgsCoordinateFormatter.FormatDegreesMinutes, 4, self.withSuff ).rsplit(',')[::-1]) else: s += ', '.join(QgsCoordinateFormatter.format(pos, QgsCoordinateFormatter.FormatDegreesMinutesSeconds, 2, self.withSuff ).rsplit(',')[::-1]) if depth > -9999: s += "\nd = {:.1f}".format(depth) if altitude > -9999: if depth > -9999: s += " alt = {:.1f}".format(altitude) else: s += "\nalt = {:.1f}".format(altitude) self.posLabel.setText(s) if self.timedOut: if fix > self.lastFix: self.posLabel.setStyleSheet('background: lime; font-size: 8pt; color: black;') self.timedOut = False self.lastFix = fix @pyqtSlot() def onTimeout(self): if not self.timedOut: self.timedOut = True self.posLabel.setStyleSheet('background: red; font-size: 8pt; color: white;') @pyqtSlot(bool) def onEnableClicked(self, enable): self.timedOut = True if enable: self.posLabel.setStyleSheet('background: red; font-size: 8pt; color: white;') else: self.posLabel.setStyleSheet('background: white; font-size: 8pt; color: black;') def releaseMobile(self): self.mobile = None
class TrackingDisplay(QToolBar): ''' Display the position of a mobile and add action for centering the map on the vehicle and erasing the track ''' def __init__(self, mobile, parent=None): super(TrackingDisplay, self).__init__(parent) self.setMovable(True) self.setFloatable(True) self.mobile = mobile self.timedOut = True self.lastFix = 0.0 s = QSettings() self.defFormat = s.value('PosiView/Misc/DefaultFormat', defaultValue=0, type=int) self.format = self.defFormat & 3 self.withSuff = cf.FlagDegreesUseStringSuffix if bool( self.defFormat & 4) else cf.FormatFlag(0) try: self.sep = cf.separator() + ' ' except AttributeError: self.sep = ', ' self.createActions() self.mobile.newPosition.connect(self.onNewPosition) self.mobile.timeout.connect(self.onTimeout) self.posText = '0.000000, 0.000000' def createActions(self): self.nameLabel = QLabel(self.mobile.name) self.nameLabel.setMinimumSize(80, 23) self.nameLabelAction = QWidgetAction(self) self.nameLabelAction.setDefaultWidget(self.nameLabel) self.addAction(self.nameLabelAction) self.enableAction = QAction("Enable Display", self) self.enableAction.setCheckable(True) self.enableAction.setChecked(True) icon = QIcon(':/plugins/PosiView/ledgrey.png') icon.addFile(':/plugins/PosiView/ledgreen.png', QSize(), QIcon.Normal, QIcon.On) self.enableAction.setIcon(icon) self.addAction(self.enableAction) self.enableAction.triggered.connect(self.onEnableClicked) self.enableAction.triggered.connect(self.mobile.setEnabled) self.addSeparator() self.posLabel = QLabel("--:--:-- 0.000000, 0.000000") self.posLabel.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) widths = (180, 196, 204, 180, 184, 200, 208, 184) self.posLabel.setMinimumSize(widths[self.format], 23) self.posLabel.setStyleSheet( 'background: red; font-size: 8pt; color: white;') self.posLabelAction = QWidgetAction(self) self.posLabelAction.setDefaultWidget(self.posLabel) self.addAction(self.posLabelAction) self.centerAction = QAction(QIcon(':/plugins/PosiView/center.png'), "Center &Map", self) self.addAction(self.centerAction) self.deleteTrackAction = QAction( QIcon(':/plugins/PosiView/deletetrack.png'), 'Delete &Track', self) self.addAction(self.deleteTrackAction) self.deleteTrackAction.triggered.connect(self.mobile.deleteTrack) self.centerAction.triggered.connect(self.mobile.centerOnMap) @pyqtSlot(float, QgsPointXY, float, float) def onNewPosition(self, fix, pos, depth, altitude): s = str() if fix > 0: s = strftime('%H:%M:%S ', gmtime(fix)) else: s = '--:--:-- ' if self.format == 1: f, pr = cf.FormatDegreesMinutes, 4 elif self.format == 2: f, pr = cf.FormatDegreesMinutesSeconds, 2 else: f, pr = cf.FormatDecimalDegrees, 6 self.posText = self.sep.join( (cf.formatY(pos.y(), f, pr, self.withSuff), cf.formatX(pos.x(), f, pr, self.withSuff))) s += self.posText if depth > -9999: s += "\nd = {:.1f}".format(depth) if altitude > -9999: if depth > -9999: s += " alt = {:.1f}".format(altitude) else: s += "\nalt = {:.1f}".format(altitude) self.posLabel.setText(s) if self.timedOut: if fix > self.lastFix: self.posLabel.setStyleSheet( 'background: lime; font-size: 8pt; color: black;') self.timedOut = False self.lastFix = fix @pyqtSlot() def onTimeout(self): if not self.timedOut: self.timedOut = True self.posLabel.setStyleSheet( 'background: red; font-size: 8pt; color: white;') @pyqtSlot(bool) def onEnableClicked(self, enable): self.timedOut = True if enable: self.posLabel.setStyleSheet( 'background: red; font-size: 8pt; color: white;') else: self.posLabel.setStyleSheet( 'background: white; font-size: 8pt; color: black;') def mousePressEvent(self, event): if event.button() == Qt.LeftButton: if event.modifiers() == Qt.ControlModifier: QGuiApplication.clipboard().setText(self.posText) else: drag = QDrag(self) mimeData = QMimeData() mimeData.setText(self.posText) drag.setMimeData(mimeData) drag.exec_() def releaseMobile(self): self.mobile = None