matplotlib

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
mushroom
User
Beiträge: 58
Registriert: Sonntag 21. November 2010, 12:32

Hallo,

nach meinen ersten Versuchen mit Python und der Qt Gui bin ich mit meinem Projekt schon etwas vorwärts gekommen. Nun hatte ich vor aus einer Datei einen Datensatz in meinem GUI (ein QtDialog) mittels matplotlib graphisch darzustellen. Aus verschiedenen Quellen habe ich beispielhaft folgendes:

Code: Alles auswählen

# -*- coding: utf-8 -*-
"""
Created on Tue Dec 14 13:28:06 2010

@author: Markus
"""

# for command-line arguments
import sys 

#Python Qt4 bindings for GUI objects
from PyQt4 import QtCore, QtGui 

#GUI
from test import Ui_Dialog


#matplolib
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure


class MyDialog(QtGui.QMainWindow, Ui_Dialog):
    def __init__(self):
        super(MyDialog, self).__init__()
        self.plot = Freq()
        self.setCentralWidget(self.plot)
        
#        self.lineEdit.setText('blub')
        
class Freq(QtGui.QWidget):
    def __init__(self, *args):
        QtGui.QWidget.__init__(self, *args)
       
        self.fig = Figure()
        self.ax = self.fig.add_subplot(111)
        self.ax.plot(range(4))
        self.ax.grid()
        self.canvas = FigureCanvas(self.fig)
        self.canvas.setParent(self)

        
if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    myDialog = MyDialog()
    myDialog.show()
    sys.exit(app.exec_())
Das GUI ist hier: http://paste.pocoo.org/show/307111/. Obiges Beispiel funktioniert soweit erstmal. Nun ist mein Problem bei Verwendung der GUI, wenn ich

Code: Alles auswählen

class MyDialog(QtGui.QMainWindow, Ui_Dialog):
in

Code: Alles auswählen

class MyDialog(QtGui.QDialog, Ui_Dialog):
ändere, es nicht mehr funktioniert. Habe in das GUI deswegen mit dem QTDesigner direkt ein MatplotlibWidget eingefügt. Nur habe ich es nicht geschafft darauf zuzugreifen. Was muß ich tun?

Wo ich gerade bei Grafiken bin. Habe später noch vor 3D-Grafiken direkt im Dialog einzubinden. Die Grafik soll interaktiv sein, also dargestelltes Modell dreh- und zoombar sein. Dazu habe ich mir verschiedene Pakete schonmal angeschaut: VPython, VTK und Maya. Von der Bedienung her scheint mir ersteres sehr einfach zu sein. Allerding habe ich keine Möglichkeit gefunden, es direkt in die GUI einzubinden. Bei Maya scheint dies möglich zu sein, sieht mir aber ziemlich kompliziert aus. Habt ihr da Empfehlungen?

Grüße
Markus
lunar

@mushroom: Was Du zuerst tun musst, ist richtig fragen. „Funktioniert nicht“ ist keine sinnvolle Fehlerbeschreibung. Erkläre also bitte, was genau passiert und welche Fehlermeldungen dabei auftreten. Zeige auch bitte in Zukunft die vom Designer erstellte Datei (Endung ".ui") und nicht das, was der UI-Compiler daraus macht.

Sieht der Quelltext tatsächlich so wie gezeigt aus, dann kannst Du innerhalb von MyDialog einfach mit "self.mplwidget" auf das Steuerelement von "matplotlib" zugreifen. Falls das nicht klappt, dann zeige bitte die vollständige Fehlermeldung.
mushroom
User
Beiträge: 58
Registriert: Sonntag 21. November 2010, 12:32

Hallo lunar,

sorry, hatte mich da gestern wohl schon etwas zu lange mit dem problem beschäftigt. Hier zunächst erstmal die ui-Datei: http://paste.pocoo.org/show/307301/.

Das Beispiel oben funktioniert nun erstmal dahingehend, daß ein Fesnter erzeugt wird und entprechend auch geplottet wird. Wenn ich nun aber dazu übergehe und meine GUI test.ui bzw. test.py verwende unter Abänderung auf

Code: Alles auswählen

class MyDialog(QtGui.QDialog, Ui_Dialog):
erhalte ich als Fehler:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Users\Markus\Documents\Python\projects\test\main.py", line 45, in <module>
    myDialog = MyDialog()
  File "C:\Users\Markus\Documents\Python\projects\test\main.py", line 27, in __init__
    self.setCentralWidget(self.plot)
AttributeError: 'MyDialog' object has no attribute 'setCentralWidget'
Auskommentieren von

Code: Alles auswählen

self.setCentralWidget(self.plot)
lässt den Fehler entsprechend verschwinden und wird dann aber nur ein leeres Fenster erzeugt. Kommentiere ich nun die Zeile

Code: Alles auswählen

self.lineEdit.setText('blub')
wieder "ein", erhalte ich die Fehlermeldung

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Users\Markus\Documents\Python\projects\test\main.py", line 46, in <module>
    myDialog = MyDialog()
  File "C:\Users\Markus\Documents\Python\projects\test\main.py", line 30, in __init__
    self.lineEdit.setText('blub')
AttributeError: 'MyDialog' object has no attribute 'lineEdit'
Entsprechend bewirkt ein Hinzufügen der Zeile

Code: Alles auswählen

self.mplwidget.grid()
nur

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Users\Markus\Documents\Python\projects\test\main.py", line 46, in <module>
    myDialog = MyDialog()
  File "C:\Users\Markus\Documents\Python\projects\test\main.py", line 28, in __init__
    self.mplwidget.grid()
AttributeError: 'MyDialog' object has no attribute 'mplwidget'
Bei beiden letzten Änderungen verstehe ich nicht, warum es nicht funktioniert.

Grüße
Markus
Rekrul
User
Beiträge: 78
Registriert: Dienstag 7. Dezember 2010, 16:23

QDialog hat kein setCentralWidget().
lunar

Soll heißen, wenn Du im Designer ein "QMainWindow" entwirfst, dann musst Du das auch in eine Unterklasse von "QMainWindow" einfügen. Du kannst nicht wahlfrei verschiedene Typen mischen, und dann erwarten, dass die Oberfläche trotzdem korrekt funktioniert.

Am vom Designer erzeugten Quelltext sollten im Übrigen auch keine Änderungen durchgeführt werden, die Warnung am Anfang des Quelltexts ist nicht zum Spaß da.
mushroom
User
Beiträge: 58
Registriert: Sonntag 21. November 2010, 12:32

Genau das ist ja das Problem. Ich habe ja im QtDesigner ein QDialog entworfen und wollte dieses GUI nun verwenden, indem ich

Code: Alles auswählen

class MyDialog(QtGui.QMainWindow, Ui_Dialog):
in

Code: Alles auswählen

class MyDialog(QtGui.QDialog, Ui_Dialog):
geändert habe. Zugriff auf das MatplotlibWidget sollte dann, wie du schon geschrieben hast über self.mplwidget erfolgen und das eben funktioniert leider nicht.
lunar

Hast Du die im Designer erstellte Datei auch erneut kompiliert?
mushroom
User
Beiträge: 58
Registriert: Sonntag 21. November 2010, 12:32

Hallo lunar,

ja, die Datei ist neu kompiliert, funktioniert leider imer noch nicht mit dem Code wie ich ihn habe. Allerdings habe ich jetzt zumindest durch Ergänzen des Codes in der Methode __init__(self) (Klasse MyDialog) mit

Code: Alles auswählen

self.setupUi(self)
wieder Zugriff auf

Code: Alles auswählen

self.lineEdit.setText('blub')
Bleibt allerdings weiter das Problem, daß in der Grafik nichts geändert wird. Mit folgendem Code

Code: Alles auswählen

# -*- coding: utf-8 -*-
"""
Created on Tue Dec 14 13:28:06 2010

@author: Markus
"""

# for command-line arguments
import sys 

#Python Qt4 bindings for GUI objects
from PyQt4 import QtCore, QtGui 

#GUI
from test import Ui_Dialog


#matplolib
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np

#class MyDialog(QtGui.QMainWindow, Ui_Dialog):
class MyDialog(QtGui.QDialog, Ui_Dialog):
    def __init__(self):
        super(MyDialog, self).__init__()
        
#        self.setCentralWidget(self.plot)
        self.setupUi(self)
        self.mplwidget.plot = Freq()
        
        self.lineEdit.setText('blub')
        
        self.mplwidget.grid()
                
class Freq(QtGui.QWidget):
    def __init__(self, *args):
        QtGui.QWidget.__init__(self, *args)
       
        self.fig = Figure()
        self.ax = self.fig.add_subplot(111)
        self.ax.plot(range(40))
        self.ax.grid()
        self.canvas = FigureCanvas(self.fig)
        self.canvas.setParent(self)

        
if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    myDialog = MyDialog()
    myDialog.show()
    sys.exit(app.exec_())
erhalte ich die Fehlermeldung

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Users\Markus\Pythonprojekte\Test\main.py", line 50, in <module>
    myDialog = MyDialog()
  File "C:\Users\Markus\Pythonprojekte\Test\main.py", line 34, in __init__
    self.mplwidget.grid()
AttributeError: 'MatplotlibWidget' object has no attribute 'grid'
lunar

Woher kommt "MatplotlibWidget" denn überhaupt? Denn ganz offensichtlich entsprechen die Attribute dieses Objekts nicht Deinen Erwartungen. Das wäre jetzt folglich der Zeitpunkt, an dem ein Blick in die Dokumentation obligatorisch ist.
mushroom
User
Beiträge: 58
Registriert: Sonntag 21. November 2010, 12:32

So, mal wieder mit dem Problem nach den Feiertagen beschäftigt.
Also die Klasse MatplotlibWidget ist ein Element des QtGUI und wird über mplwidget angesprochen. Hier die von pyuic4 erstellte Datei http://paste.pocoo.org/show/313188/ bzw. das ui selbst http://paste.pocoo.org/show/313193/

Eine Suche nach dem Modul matplotlibwidget brachte leider keinen Erfolg, finde weder über google etwas brauchbares noch auf der Webseite von matplotlib oder der QT Reference.
mushroom
User
Beiträge: 58
Registriert: Sonntag 21. November 2010, 12:32

Hmm, wie es der Zufall so will, doch etwas gefunden: http://paste.pocoo.org/show/313201/

Code: Alles auswählen

# -*- coding: utf-8 -*-
#
# Copyright © 2009 Pierre Raybaut
# Licensed under the terms of the MIT License

"""
MatplotlibWidget
================

Example of matplotlib widget for PyQt4

Copyright © 2009 Pierre Raybaut
This software is licensed under the terms of the MIT License

Derived from 'embedding_in_pyqt4.py':
Copyright © 2005 Florent Rougon, 2006 Darren Dale
"""

__version__ = "1.0.1"

from PyQt4.QtGui import QSizePolicy, QIcon
from PyQt4.QtCore import QSize

import os.path as osp

from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as Canvas
from matplotlib.figure import Figure

from matplotlib import rcParams
rcParams['font.size'] = 9


def get_icon(name):
    """Return Matplotlib icon *name*"""
    return QIcon(osp.join(rcParams['datapath'], 'images', name))


class MatplotlibWidget(Canvas):
    """
    MatplotlibWidget inherits PyQt4.QtGui.QWidget
    and matplotlib.backend_bases.FigureCanvasBase
    
    Options: option_name (default_value)
    -------    
    parent (None): parent widget
    title (''): figure title
    xlabel (''): X-axis label
    ylabel (''): Y-axis label
    xlim (None): X-axis limits ([min, max])
    ylim (None): Y-axis limits ([min, max])
    xscale ('linear'): X-axis scale
    yscale ('linear'): Y-axis scale
    width (4): width in inches
    height (3): height in inches
    dpi (100): resolution in dpi
    hold (False): if False, figure will be cleared each time plot is called
    
    Widget attributes:
    -----------------
    figure: instance of matplotlib.figure.Figure
    axes: figure axes
    
    Example:
    -------
    self.widget = MatplotlibWidget(self, yscale='log', hold=True)
    from numpy import linspace
    x = linspace(-10, 10)
    self.widget.axes.plot(x, x**2)
    self.wdiget.axes.plot(x, x**3)
    """
    def __init__(self, parent=None, title='', xlabel='', ylabel='',
                 xlim=None, ylim=None, xscale='linear', yscale='linear',
                 width=4, height=3, dpi=100, hold=False):
        self.figure = Figure(figsize=(width, height), dpi=dpi)
        self.axes = self.figure.add_subplot(111)
        self.axes.set_title(title)
        self.axes.set_xlabel(xlabel)
        self.axes.set_ylabel(ylabel)
        if xscale is not None:
            self.axes.set_xscale(xscale)
        if yscale is not None:
            self.axes.set_yscale(yscale)
        if xlim is not None:
            self.axes.set_xlim(*xlim)
        if ylim is not None:
            self.axes.set_ylim(*ylim)
        self.axes.hold(hold)

        Canvas.__init__(self, self.figure)
        self.setParent(parent)

        Canvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding)
        Canvas.updateGeometry(self)

    def sizeHint(self):
        w, h = self.get_width_height()
        return QSize(w, h)

    def minimumSizeHint(self):
        return QSize(10, 10)



#===============================================================================
#   Example
#===============================================================================
if __name__ == '__main__':
    import sys
    from PyQt4.QtGui import QMainWindow, QApplication
    from numpy import linspace
    
    class ApplicationWindow(QMainWindow):
        def __init__(self):
            QMainWindow.__init__(self)
            self.mplwidget = MatplotlibWidget(self, title='Example',
                                              xlabel='Linear scale',
                                              ylabel='Log scale',
                                              hold=True, yscale='log')
            self.mplwidget.setFocus()
            self.setCentralWidget(self.mplwidget)
            self.plot(self.mplwidget.axes)
            
        def plot(self, axes):
            x = linspace(-10, 10)
            axes.plot(x, x**2)
            axes.plot(x, x**3)
        
    app = QApplication(sys.argv)
    win = ApplicationWindow()
    win.show()
    sys.exit(app.exec_())

Wende ich das gegebene Beispiel in meiner Datei http://paste.pocoo.org/show/313231/ an, so wird auch das dem Code mehr oder weniger Erwartete dargestellt. Es wird nicht in das vorhandene Widget gezeichnet sondern einfach ein neues Widget erzeugt und auf das vorhandene überlagert.
Antworten