Zach Tait
Zach Tait

Reputation: 220

How to create a button on a field in QWizardPage

I am attempting to create a field in a QWizardPage that the user can either click on and a popup window opens up (this would be a window with a map on that allows the user to pick 2 coordinates) or allows the user to input the coordinates themselves. Think along the lines of a file picker line where the user can either open the popup file browser or manually type in the file path.

The original command is QLineEdit.

Upvotes: 0

Views: 101

Answers (1)

eyllanesc
eyllanesc

Reputation: 243955

You have to create a custom widget that for example shows a QDoubleSpinBox for latitude and another for longitude, in addition to a button that allows to display a map (for example using QML). And then the widget is added to QWizardPage like any other widget.

import os
import sys
from pathlib import Path

from PySide2.QtCore import Property, Signal, Slot, Qt, QUrl
from PySide2.QtWidgets import (
    QApplication,
    QDialog,
    QDialogButtonBox,
    QDoubleSpinBox,
    QHBoxLayout,
    QLabel,
    QToolButton,
    QVBoxLayout,
    QWidget,
    QWizard,
    QWizardPage,
)
from PyQt5.QtPositioning import QGeoCoordinate
from PyQt5.QtQuickWidgets import QQuickWidget

CURRENT_DIRECTORY = Path(__file__).resolve().parent


class MapDialog(QDialog):
    def __init__(self, geo_widget):
        super().__init__(geo_widget)
        self.setWindowTitle("Map")
        self.map_widget = QQuickWidget(resizeMode=QQuickWidget.SizeRootObjectToView)
        self.map_widget.rootContext().setContextProperty("controller", geo_widget)
        filename = os.fspath(CURRENT_DIRECTORY / "main.qml")
        url = QUrl.fromLocalFile(filename)
        self.map_widget.setSource(url)

        button_box = QDialogButtonBox()
        button_box.setOrientation(Qt.Horizontal)
        button_box.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok)

        lay = QVBoxLayout(self)
        lay.addWidget(self.map_widget)
        lay.addWidget(button_box)

        button_box.accepted.connect(self.accept)
        button_box.rejected.connect(self.reject)


class GeoWidget(QWidget):
    coordinate_changed = Signal(name="coordinateChanged")

    def __init__(self, parent=None):
        super().__init__(parent)
        self._coordinate = QGeoCoordinate(0, 0)

        self._lat_spinbox = QDoubleSpinBox(
            minimum=-90.0, maximum=90.0, valueChanged=self.handle_value_changed
        )
        self._lng_spinbox = QDoubleSpinBox(
            minimum=-180.0, maximum=180.0, valueChanged=self.handle_value_changed
        )
        self.btn = QToolButton(text="map", clicked=self.handle_clicked)
        self.map_view = MapDialog(self)

        lay = QHBoxLayout(self)
        lay.addWidget(QLabel("Latitude:"))
        lay.addWidget(self._lat_spinbox)
        lay.addWidget(QLabel("Longitude:"))
        lay.addWidget(self._lng_spinbox)
        lay.addWidget(self.btn)

    @Property(QGeoCoordinate, notify=coordinate_changed)
    def coordinate(self):
        return self._coordinate

    @coordinate.setter
    def coordinate(self, coordinate):
        if self.coordinate == coordinate:
            return
        self._coordinate = coordinate
        self.coordinate_changed.emit()

    def handle_value_changed(self):
        coordinate = QGeoCoordinate(
            self._lat_spinbox.value(), self._lng_spinbox.value()
        )
        self.coordinate = coordinate

    @Slot(QGeoCoordinate)
    def update_from_map(self, coordinate):
        self.coordinate = coordinate
        self._lat_spinbox.setValue(self.coordinate.latitude())
        self._lng_spinbox.setValue(self.coordinate.longitude())

    def handle_clicked(self):
        self.map_view.exec_()


class WizardPage(QWizardPage):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.geo_widget1 = GeoWidget()
        self.geo_widget2 = GeoWidget()

        self.registerField("coordinate1", self.geo_widget1, b"coordinate")
        self.registerField("coordinate2", self.geo_widget2, b"coordinate")

        lay = QVBoxLayout(self)
        lay.addWidget(self.geo_widget1)
        lay.addWidget(self.geo_widget2)


def main():
    app = QApplication(sys.argv)

    w = QWizard()
    page = WizardPage()
    w.addPage(page)
    w.show()

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
import QtLocation 5.15
import QtQuick 2.15

Item {
    width: 400
    height: 400

    Map {
        id: map

        anchors.fill: parent
        zoomLevel: 14

        MouseArea {
            id: mouse
            anchors.fill: parent
            onClicked: controller.update_from_map(map.toCoordinate(Qt.point(mouse.x, mouse.y)))
        }

        plugin: Plugin {
            name: "osm"
        }

    }

}

Upvotes: 1

Related Questions