[aufgegeben] In QPixmap zeich. u. in QWidget darstellen ?

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
mephisto-online
User
Beiträge: 167
Registriert: Sonntag 29. September 2013, 17:05

Hallo im Forum,

Nachdem ich u.A. mit Eurer Hilfe mittlerweile Python recht gut verstanden und auch in PyQt schon einiges hinbekommen habe, finde ich bei der Programmierung meiner Grafiken einfach keinen Einstieg und hoffe, dass Ihr mir hier vielleicht nochmal einen kleinen Anstoß geben könnt.

Mein Vorhaben:
In Java/Swing habe ich diese Grafiken schon einmal erstellt und zwar, indem ich sie erst in ein Bitmap gezeichnet und dieses dann in einer View dargestellt habe, weil das Neuzeichen bei der Aktualisierung des Fensters mit einem Bitmap wesentlich schneller geht. Das möchte ich jetzt auch mit PyQt realisieren.

Nach dem, was ich jetzt gelesen und ausprobiert habe, kann ich mit einem QPainter sowohl in einem Widget, als auch in einem QPixmap zeichnen. In einem QWidget klappt das ohne Probleme. In einem QPixmap geht es auch, ohne dass sich der Interpreter beschwert (ich kann es aber nicht sehen :lol: ).

Wie kann ich denn eine QPixmap-Zeichnung in einem QWidget darstellen ? (oder macht man das sowieso anders ?)

Mit Google und Tutorials bin ich da bislang nicht weiter gekommen. :(

Grüße
mephisto-online

EDIT:

Nun gut ! Ich gebe es auf mit PyQt. Ich habe jetzt Abende lang nach Beispielen gesucht um zu kapieren, wie der Hase läuft mit Qt und den Grafiken. Eine gute Klassenreferenz reicht mir da einfach (noch) nicht aus ! Und für hier ist meine Frage offensichlich nicht substanziell genug oder es weiss wirklich niemand, wie das geht. Also werde ich mein Qt-Projekt wohl wohl besser in C++ oder Java realisieren, einfach, weil es besser dokumentiert ist und man auch alleine weiter kommt. Schade eigentlich ! Es hat angefangen, richtig Spass zu machen mit Python.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

mephisto-online hat geschrieben:Also werde ich mein Qt-Projekt wohl wohl besser in C++ oder Java realisieren, einfach, weil es besser dokumentiert ist und man auch alleine weiter kommt. Schade eigentlich ! Es hat angefangen, richtig Spass zu machen mit Python.
Du verstehst die C++-Dokumentation von Qt nicht, auf die PyQt verweist, und möchtest deshalb jetzt von Python auf C++ oder Java wechseln und dort Qt benutzen. Ich bin mir sicher, dass ich hier irgendwo die innere Logik übersehe ...
Das Leben ist wie ein Tennisball.
BlackJack

@EyDu: Bei Java wird er wohl nicht auf Qt setzen wollen sondern Swing oder AWT nutzen wollen. Nehme ich jedenfalls an. Das mit C++ habe ich auch nicht so recht verstanden, denn da ist Qt soweit ich das sehe die verbreitetste Crossplattform-Bibliothek.

@mephisto-online: Vielleicht fehlt den Leuten auch die Information was Du eigentlich *konkret* machen möchtest. Beziehungsweise was Du mit `QPixmap` konkret gemacht hast was nicht funktioniert hat.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

BlackJack hat geschrieben:@EyDu: Bei Java wird er wohl nicht auf Qt setzen wollen sondern Swing oder AWT nutzen wollen. Nehme ich jedenfalls an.
Das hoffe ich auch, aber ich befürchte das Schlimmste ;-)
Das Leben ist wie ein Tennisball.
Benutzeravatar
Madmartigan
User
Beiträge: 200
Registriert: Donnerstag 18. Juli 2013, 07:59
Wohnort: Berlin

Um das mal abzukürzen, der Wechsel von Python/Qt zu C++/Qt oder (noch schlimmer :twisted: ) zu Java/AWT aus den von dir genannten Gründen ist unnötig.
Wenn du bereits auf einem Widget zeichnen kannst und ebenso auf einem QPixmap, was unter der Haube letztendlich kaum einen Unterschied macht, was hält dich davon ab, das Pixmap mit dem QPainter auf einem Widget zu zeichnen?

Qt kommt imho mit einer der detailliertesten und anschaulichsten Dokumentationen daher, also ist das Problem (vorausgesetzt ich habe richtig interpretiert, was du wirklich machen willst) von dir doch eigentlich sofort lösbar. QPainter besitzt neben diversen drawxyz() Methoden auch eine drawPixmap() mit verschiedenen Möglichkeiten zur Parameterübergabe. Damit kannst du dein QPixmap pixel-genau auf dem Widget zeichnen.

Oder habe ich dich missverstanden?
Malta
User
Beiträge: 83
Registriert: Samstag 8. Januar 2011, 23:51

Als Beispiel: wie wäre es mit SimpleImageRuler, da wird ein Hintergrundbild geladen und der Benutzer kann darauf Linien eingezeichen und die Länge der Linien wird dann ausgegeben.
http://www.python-forum.de/viewtopic.php?f=9&t=32715
mephisto-online
User
Beiträge: 167
Registriert: Sonntag 29. September 2013, 17:05

Hallo !

Puh ! Da schein ich ja doch nicht alleine zu sein ! Danke schön ! Das Feedback habe ich gar nicht mitbekommen, weil ich unterwegs war und auf meinem Tab keine Mail eingeflogen ist :roll: . Entschuldigt !

Ich war voll frustriert, weil ich einfach keine einfachen Beispiele finden kann für das , was ich nun schon in C, Pascal, Java/AWT/Swing und auch schon im Ansatz in C# locker hinbekommen habe. Die Doku im Internet ist, was PyQt angeht (von QT selbst in C++ und die PyQt-Referenz in http://pyqt.sourceforge.net/Docs/PyQt4 mal abgesehen) doch sehr dürftig. Auf http://zetcode.com ist auch einiges ganz gut beschrieben. Und weil hier nun absolut nichts zurück kam und ich weiterkommen wollte, hatte ich halt in meinem Frust damit geliebäugelt, nun doch noch das für meine Zwecke eigentlich völlig überzogene C++ nun auch noch zu lernen, einfach, weil es da von Qt selbst schon sehr gute Tutorials hat. Mit QTJambi hatte ich es dann gestern auch noch versucht, funktioniert zwar, aber auf Java habe ich eigentlich gar keinen Bock mehr. Also habe ich dann heute erst mal noch ein paar Python-IDEs mit PyQt getestet (Eric, KDEDeveloper, Netbeans, Eclipse, das freie Wings), habe dann aber doch lieber wieder das PyCharm angeschmissen. Hatte gehofft, eine IDE zu finden, die nicht mit Java geschrieben ist. Iss aber nich.

Danach habe ich dann doch noch mal versucht, mein Grafikproblem anzugehen. Also nochmal, eigentlich ganz einfach: Ich möchte ein QPixmap definierter Größe erzeugen, dort eine (virtuelle) Zeichnung mit nem QPainter machen und diese Zeichnung in einem Qt-Designer-Widget darstellen. Das Zeichnen direkt in das Widget in paintEvent() ist kein Problem. Da die Zeichnung aber recht komplex ist (sowas wie ein Smith-Diagramm), möchte ich sie natürlich nicht immer neu zeichnen lassen, wenn das Fenster mal überdeckt wird o.ä.

Mein kleines Übungs-Programm:

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
from PyQt4 import QtCore
from PyQt4.QtCore import QPoint, Qt
from PyQt4.QtGui import QWidget, QPainter, QApplication, QPixmap, QColor, QLabel

class MyWidget(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.setGeometry(100, 100, 500, 500)
        self.label = QLabel(self)
        self.label.setScaledContents(True)
        self.pixmap = MyPixmap()
        self.label.setPixmap(self.pixmap)
        self.show()

    def paintEvent(self, ev):
        p = QPainter(self)
        p.drawImage(self.pixmap)

class MyPixmap(QPixmap):
    
    def __init__(self):
        super(MyPixmap, self).__init__()
        print('Init:'+str(self.height()), str(self.width()))
        self.painter = QPainter(self)
        self.draw_grid(self.painter)
 
    def draw_grid(self, painter):
        p = painter
        p.setPen(QColor("lightgray"))
        h = self.height()
        w = self.width()
        for i in range(1, h, 10):
            p.draw_line(0, i, w, i)

def main():
    app = QApplication(sys.argv)
    ex = MyWidget()
    ex.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()
Der Print-befehl liefert '0 0'

Ich verstehe es nicht !

Dem Pixmap eine definierte Größe zu verpassen, habe ich nicht hinbekommen.

Wahrscheinlich habe ich irgendwas überhaupt nicht begriffen !

Grüße
mephisto-online
BlackJack

@mephisto-online: Was hättest Du denn an der Stelle erwartet? Es werden ja nirgends die Aussmasse des `QPixmap`-Exemplars gesetzt. Selbst wenn sich das automatisch an ein Widget anpassen würde in dem es angezeigt wird, was es *nicht* tut — zu dem Zeitpunkt wo das `print()` ausgeführt wird, ist das `QPixmap`-Exemplar ja noch gar keinem Anzeigewidget zugeordnet.

Und wenn man mal in die Qt-Dokumentation schaut was der `QPixmap`-Konstruktur macht wenn man nichts übergibt, dann steht dort „Constructs a null pixmap.” und es wird auf die `isNull()`-Methode verwiesen in der steht was das bedeutet: „A null pixmap has zero width, zero height and no contents. You cannot draw in a null pixmap.”. Gleich unter dem Kontruktor ohne Argumente steht in der Dokumentation einer der zwei Ganzzahlwerte als Argumente nimmt die `width` und `height` heissen.


Warum bekommt `paint_grid()` den `QPainter` als Argument übergeben?

`QPainter` hat keine `draw_line()`-Methode, die heisst `drawLine()`. Man sollte das `QPainter`-Objekt auch nicht zu lange existieren lassen sonst bekommt man später Probleme mit dem Programmende. Solange nämlich der Painter noch zugriff auf das Pixmap-Exemplar hat, kann das nicht freigegeben werden und bei mir zumindest kommt es dadurch am Programmende recht zuverlässig zu einem Absturz.

Die Fenstergeometrie und das platzieren von Widgets sollte man nicht von Hand festlegen sondern Layout Manager verwenden. Besonders wenn man GUIs verändert oder erweitert würde das sonst viel unnötige Handarbeit nach sich ziehen.

Wenn die Pixmap schon in einem Label dargestellt wird, muss man sie nicht noch mal selbst auf das Widget malen, beziehungsweise sollte man das nicht, denn damit übermalt man ja das Label.

Der `show()`-Aufruf in der Widget-`__init__()` ist doppelt weil in der `main()`-Funktion noch mal diese Methode aufgerufen wird. Keines der vorhandenen Widgets `show()`\t sich selbst, also sollte der Aufruf in der `__init__()` verschwinden.

Ich komme dann auf so etwas:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
import sys
from PyQt4.QtGui import (
    QWidget, QPainter, QApplication, QPixmap, QColor, QLabel, QVBoxLayout
)


class GridPixmap(QPixmap):
   
    def __init__(self, width, height):
        super(GridPixmap, self).__init__(width, height)
        print('Init:', self.width(), self.height())
        self.draw_grid()
 
    def draw_grid(self):
        # 
        # FIXME Still a `LinePixmap` :-)
        # 
        self.fill()
        painter = QPainter(self)
        painter.setPen(QColor('lightgray'))
        width = self.width()
        for y in range(1, self.height(), 10):
            painter.drawLine(0, y, width, y)


class MyWidget(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        
        self.pixmap = GridPixmap(300, 300)

        label = QLabel(self)
        label.setPixmap(self.pixmap)
        label.setScaledContents(True)
        
        layout = QVBoxLayout(self)
        layout.addWidget(label)
        self.setLayout(layout)

 
def main():
    app = QApplication(sys.argv)
    widget = MyWidget()
    widget.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()
Nochmal zur PyQt-Dokumentation: Die reicht nicht, denn die beschreibt nur wie die Qt-Konstrukte auf Python abgebildet werden und die Unterschiede zur Qt-Dokumentation. Man muss auch für Python die Qt-Dokumentation für C++ lesen.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

BlackJack hat geschrieben:@EyDu: Bei Java wird er wohl nicht auf Qt setzen wollen sondern Swing oder AWT nutzen wollen.
Ich fand QtJambi eine tolle Sache (anno 2009 / 2010) - leider geriet die Entwicklung ins Stocken... wie es aktuell aussieht, weiß ich leider nicht.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
mephisto-online
User
Beiträge: 167
Registriert: Sonntag 29. September 2013, 17:05

Hallo,

That's it !
BlackJack hat geschrieben:...Es werden ja nirgends die Aussmasse des `QPixmap`-Exemplars gesetzt.
Und wenn man mal in die Qt-Dokumentation schaut was der `QPixmap`-Konstruktur macht wenn man nichts übergibt, dann steht dort „Constructs a null pixmap.” und es wird auf die `isNull()`-Methode verwiesen in der steht was das bedeutet: „A null pixmap has zero width, zero height and no contents. You cannot draw in a null pixmap.”. Gleich unter dem Kontruktor ohne Argumente steht in der Dokumentation einer der zwei Ganzzahlwerte als Argumente nimmt die `width` und `height` heissen.
Das war genau das, was ich einfach nirgendwo gefunden hatte. QPixmap wird immer für fertige Images beschrieben, die ja schon eine Größe haben.

Das hier in der Klassenreferenz
QPixmap.__init__ (self, int w, int h)
QPixmap.__init__ (self, QSize)
See also isNull().
hatte ich schlicht und ergreifend nicht gesehen ! (zu viel Wald, wo sind hier die Bäume ? :? ) Einen isNull()-Fehler hatte ich ja auch immer bekommen.
BlackJack hat geschrieben:Warum bekommt `paint_grid()` den `QPainter` als Argument übergeben?
`QPainter` hat keine `draw_line()`-Methode, die heisst `drawLine()`.
Da hast Du recht ! Ich hatte mein mittlerweile Test-Programm nicht gewissenhaft genug heruntergekürzt. war wohl auch schon etwas übermüdet ! Sorry !
BlackJack hat geschrieben:Man sollte das `QPainter`-Objekt auch nicht zu lange existieren lassen sonst bekommt man später Probleme mit dem Programmende. Solange nämlich der Painter noch zugriff auf das Pixmap-Exemplar hat, kann das nicht freigegeben werden ...
Guter Tip ! Würde ich normalerweise auch nicht so machen, wird ja schleisslich auch im "Haupt-Thread" angenagelt.
BlackJack hat geschrieben:Wenn die Pixmap schon in einem Label dargestellt wird, muss man sie nicht noch mal selbst auf das Widget malen, beziehungsweise sollte man das nicht, denn damit übermalt man ja das Label.
Also reicht es, wenn man das Pixmap dem Label zuweist und man muss es nicht explizit in einer überschriebenen paintEvent()-Methode darstellen. Verstanden !
BlackJack hat geschrieben:Die Fenstergeometrie und das platzieren von Widgets sollte man nicht von Hand festlegen sondern Layout Manager verwenden. Besonders wenn man GUIs verändert oder erweitert würde das sonst viel unnötige Handarbeit nach sich ziehen.
Das denke ich mal. Deshalb ordne ich die Komponenten im Qt-Designer in Layouts an, auch die geplanten Widgets mit Zeichnungen.
BlackJack hat geschrieben:Der `show()`-Aufruf in der Widget-`__init__()` ist doppelt weil in der `main()`-Funktion noch mal diese Methode aufgerufen wird. Keines der vorhandenen Widgets `show()`\t sich selbst, also sollte der Aufruf in der `__init__()` verschwinden.
Ist auch klar ! Hatte mich schon gewundert, dass der paintEvent immer zweimal ausgelöst wird.
BlackJack hat geschrieben:Nochmal zur PyQt-Dokumentation: Die reicht nicht, denn die beschreibt nur wie die Qt-Konstrukte auf Python abgebildet werden und die Unterschiede zur Qt-Dokumentation. Man muss auch für Python die Qt-Dokumentation für C++ lesen.
Deshalb tendiere ich ja auch immer wieder dazu, doch noch C++ zu lernen, eben weil PyQt die C++-Routinen von Qt lediglich "verscriptet". Trotz meiner C- und Java-Kenntnisse bekomme ich aber beim Anblick von C++-Code immer die Krise !

Danke jedenfalls für das "Auf-die-Sprünge-Helfen" ! Vielleicht sehe ich immer alles einfach viel zu kompliziert :oops:

Grüße
mephisto-online
mephisto-online
User
Beiträge: 167
Registriert: Sonntag 29. September 2013, 17:05

Noch ne kurze Frage:

Kann man irgendwie den kleinen Rand, den das Pixmap im Widget hat, weg bekommen ?
BlackJack

@mephisto-online: Kann man. Mit der `setContentsMargins()`-Methode auf dem Layout-Objekt.
mephisto-online
User
Beiträge: 167
Registriert: Sonntag 29. September 2013, 17:05

Thanks ! Geht aber nur beim Layout-Objekt.

The proof of the pudding is the eating ! :lol:
Antworten