Geokartenkacheln in QGraphicsView zusammenstellen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
kussji
User
Beiträge: 78
Registriert: Mittwoch 16. Mai 2018, 09:58

So habe mal etwas Zeit gefunden mein Vorhaben weiter zu machen.
Aktuell will ich bei einem Mausklick in die GraphicsView die aktuellen 'absoluten' Koordinaten der Items in der Scene anzeigen. Nun wahrscheinlich suche ich schon wieder zuweit :D .
Der erste Code habe ich irgendwo gefunden. Der macht mal aus meiner Sicht das was ich möchte. Was ich nicht verstehe, wer ruft die Methode 'def mousePressEvent' auf?
Wie kann ich das jetzt auf mein Code anwenden, da ich meine GUI per QTDesigner erstelle sprich ui-File. Die Idee wäre in der 'Ui_MainWindow' ein entsprechender Slot einzurichten aber da scheint für graphicsView nichts passendes da zu sein.

Code: Alles auswählen

import sys
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene

class MainWindow(QtWidgets.QMainWindow):
    central_widget = None
    layout_container = None

    def __init__(self):
        super(MainWindow, self).__init__()
        self.central_widget = QtWidgets.QWidget()
        self.layout_container = QtWidgets.QVBoxLayout()
        self.central_widget.setLayout(self.layout_container)
        self.setCentralWidget(self.central_widget)
        self.layout_container.addWidget(MyGraphicsView())

class MyGraphicsView(QGraphicsView):
    def __init__(self):
        super(MyGraphicsView, self).__init__()
        self.setScene(QGraphicsScene())
 
    def mousePressEvent(self, event):
        print('view', event.pos())
        print('scene', self.mapToScene(event.pos()))

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    mw = MainWindow()
    mw.show()
    sys.exit(app.exec_())

Code: Alles auswählen

from os import path
import sys
from pathlib import Path
from PyQt5 import QtGui, QtWidgets, QtCore, uic
from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsItem, QGraphicsPixmapItem, QGraphicsLineItem

MAIN_PATH = Path('g_nav')
IMAGE_PATH = Path('g_nav', 'image')
ORTHO_PATH = Path('g_nav', 'ortho', 'ortho_full')
GUI_NAME = 'g_nav_gui.ui'

class MapDraw():
    def __init__(self):
        self.scene = QGraphicsScene()

    def add_tile(self, pixmap, x, y):
        tile = QGraphicsPixmapItem()
        tile.setPixmap(pixmap)
        tile.moveBy(x, y)
        self.scene.addItem(tile)

class Ui_MainWindow(QtWidgets.QMainWindow, QGraphicsView):
    def __init__(self, parent=None):
        super().__init__(parent)

        ui_path = Path(MAIN_PATH, GUI_NAME)
        self.ui = uic.loadUi(ui_path, self)
  
        self.ui.graphicsView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.ui.graphicsView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.ui.graphicsView.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)

        self.geo_map = MapDraw()
        self.ui.graphicsView.setScene(self.geo_map.scene)

        self.pixmap = QtGui.QPixmap(str(Path(ORTHO_PATH, 'tile27.png')))
        self.geo_map.add_tile(self.pixmap, 0, 0)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = Ui_MainWindow()
    MainWindow.show()
    sys.exit(app.exec_())
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist Mist. Stattdessen nimmt man die protected methods der scene here: https://doc.qt.io/qt-5/qgraphicsscene.h ... PressEvent - uns die kannst du ja problemlos ableiten & bekommst dann sogar schon spezifische Events: https://doc.qt.io/qt-5/qgraphicsscenemouseevent.html
kussji
User
Beiträge: 78
Registriert: Mittwoch 16. Mai 2018, 09:58

Hallo
Ein neue Frage aber in Zusammenhang mit dem gleichen Thema, weswegen ich keinen neuen Thread starte - okey?
Bin da ein rechtes Stück weiter mit meiner Sache. Ich lade mehrere Geokacheln in die Szene, weiter werden verschiedene Items (Linien, Pixmap, etc) in die Szene gelanden. Diese Items werden dann programmgesteuert verschoben und rotiert, soweit so gut. Nun soll aber auch die View rotiert werden, da die Karten inkl. Items durchaus Kopf stehen sollen. Das erreiche ich mit "GraphicsView.rotate(10)" für z.B. 10° rechts.
Mein Problem besteht darin, dass rotate immer relativ um angegebenen Wert vor oder zurück (+/-) rotiert. Ich gäbe aber gerne den Drehwert absolut rein, weil er mir in dieser Form vorliegt (also 0-359° (Kompass)).

Gibt es das bereits und ich habe es nicht gefunden oder sollte ich mein geschildertes Vorhaben überdenken. Irgendwelche coole Ideen? Ich gerade nicht :roll:

Danke
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Na klar, mit https://doc.qt.io/qt-5/qgraphicsview.html#setTransform und https://doc.qt.io/qt-5/qtransform.html#rotate kannst du beliebige affine Transformationen auf deinen View anwenden.
kussji
User
Beiträge: 78
Registriert: Mittwoch 16. Mai 2018, 09:58

Hallo,
vorweg: für mich ist absolute Rotation und nicht relative die Knacknuss.

Danke, habe mir mal die Transformationen angesehen. Auf folgendes bin ich gekommen:

Code: Alles auswählen

    def map_rotate(self, angle):
        transform = QtGui.QTransform()
        transform.rotate(-angle)
        self.ui.graphicsView.setTransform(self.transform)
 
in der Tat macht es zwar das was ich will, aber ist das "gut" so? Bastel?
Die Funktion "map_rotate" wird zyklisch (500ms) aufgerufen (GUI-update). Woran ich mich störe ist die erste Zeile "transform = QtGui.QTransform ()". Das kann ich doch im init der GUI machen ala: "self.transform=QtGuiTransform()" und dann in meiner Funktion die entsprechende erste Zeile weg lassen? Wenn ich das tue, dann habe ich keine absolute Rotation mehr, sondern die View wird zyklisch um den entsprechenden Winkel gedreht. Warum das so ist kann ich mir vorstellen. Muss ich am Ende meiner Funktion etwas "zurücksetzen"?
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das was du da zeigst ist wahlweise sinnlos, oder falsch. Denn das transform Objekt ist ein lokales, was du dann aber setzt ist ein Objekt-Attribut, self.transform. Die haben also nix miteinander zu tun. Wenn es also funktioniert, dann ist diese lokale transform überflüssig, und der wichtige Teil woanders. Oder es kracht. Oder es ist falsch abgeschrieben.

Warum stört dich die anlage einer popeligen Matrix mit 9 Double werten? Nahezu jeder String ist teurer. Was genau ist dran jetzt so „böse“?

Wenn du immer die gleiche Matrix benutzt, dann ist natürlich der Effekt der des sukzessiven rotierens Wieder da. Das ist doch nicht verwunderlich. Das ist das, was rotate macht.
kussji
User
Beiträge: 78
Registriert: Mittwoch 16. Mai 2018, 09:58

Sorry. Mein Code hatte ein Fehler - natürlich macht das so keinen Sinn.
Gemeint habe ich:

Code: Alles auswählen

    def map_rotate(self, angle):
        transform = QtGui.QTransform()
        transform.rotate(-angle)
        self.ui.graphicsView.setTransform(transform)
was ich sagen/frage wollte, war das:

Code: Alles auswählen

...
    def __init__ (self):
        self.transform = QtGui.QTransform()
...
    def map_rotate(self, angle):
        self.transform.rotate(-angle)
        self.ui.graphicsView.setTransform(self.transform)
        
gestört hat mich bei jedem zyklischen Aufruf die wiederkehrende Erstellung der Matrix. Die ist ja aber in meinem Fall notwendig. (Fand das einfach nicht schick - aber eher aus Unwissenheit :-))
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Je weniger Zustand man hat, und je kürzer der lebt, desto schicker. Also genau das Gegenteil. Alles, was man Berechnen *kann*, statt es aufzuheben, ist erstmal gut. Denn alles, was man aufhebt, kann irgendwann veraltet, und im Widerspruch zur gewünschten Realität stehen. Natürlich gilt das nicht absolut, aber es gibt ganze sprachfamilien, die funktionalen Sprachen, die um dieses Prinzip gebaut sind. Und das erfolgreich.
Antworten