Embedding the matplotlib toolbar in pyqt4 using matplotlib custom widget - python

I have been following this chapter to embed a matplotlib Figure into a QTdesigner generated GUI. So far it works, but I need to also embed the toolbar within the GUI to manipulate the plot and save it. How can I modify the example code to add the toolbar? I have googled many sources and they all have their own custom code that does not work with the example given in the book.
I believe I need to modify the custom widget class. The code for the widget class is here:
Original code (from book):
from PyQt4 import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
class MplCanvas(FigureCanvas):
def __init__(self):
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
FigureCanvas.__init__(self, self.fig)
FigureCanvas.setSizePolicy(self,
QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
class MplWidget(QtGui.QWidget):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self, parent)
self.canvas = MplCanvas()
self.vbl = QtGui.QVBoxLayout()
self.vbl.addWidget(self.canvas)
self.setLayout(self.vbl)
My question is how can I add code such that the toolbar will display with the canvas?
I know I must import the api using:
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
Do I need to create a new class and/or custom widget to put it in?

Ok, after more fiddling around and looking at link, I modified the class MplWidget to:
class MplWidget(QtGui.QWidget):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self, parent)
self.canvas = MplCanvas()
self.mpl_toolbar = NavigationToolbar(self.canvas, self)
self.vbl = QtGui.QVBoxLayout()
self.vbl.addWidget(self.canvas)
self.vbl.addWidget(self.mpl_toolbar)
self.setLayout(self.vbl)
And now it works. Thanks!

Related

How to display navbar inside figure?

I am building an application in PyQt5 that requires a lot of visualization. I use matplotlib and create a custom MplWidget the following way:
import sys
from PyQt5.QtWidgets import QWidget, QMainWindow, QVBoxLayout, QApplication
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg, NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
class MplWidget(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.canvas = FigureCanvasQTAgg(Figure())
vertical_layout = QVBoxLayout()
vertical_layout.addWidget(self.canvas)
self.canvas.axes = self.canvas.figure.add_subplot(111)
self.setLayout(vertical_layout)
self.canvas.toolbar = NavigationToolbar(self.canvas, self)
self.layout().addWidget(self.canvas.toolbar)
self.layout().addWidget(self.canvas)
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
widget = MplWidget()
self.setCentralWidget(widget)
self.show()
app = QApplication(sys.argv)
w = MainWindow()
app.exec_()
It looks like this:
Is there a way to put the navbar inside the figure? There are applications where options (that are contained by the navbar) are invisible until the user hovers the mouse over them, then the navbar appears inside the figure (in the upper left corner for example). This way it does not disturb the figure and takes up less space. I want to do something like that with matplotlib. Can I put widgets on top of each other like this?

Updating child widget in Qt

I have a simple project with the following classes
class MainWindow(QMainWindow)
class Home(QWidget)
class Login(QWidget)
All I want is to be able to nest the QWidget classes (make them children of the QMainWindow) and display them INSIDE the MainWindow. I can't seam to figure out how to make the QWidgets "appear" after I've called them in the MainWindow.
Code is bellow:
import sys
from gui.MainWindow import Ui_MainWindow
from gui.home import Ui_Home
from gui.login import Ui_Login
from PyQt4.QtGui import QMainWindow, QApplication, QWidget
class Home(QWidget, Ui_Home):
def __init__(self):
QWidget.__init__(self)
self.setupUi(self)
class Login(QWidget, Ui_Login):
def __init__(self):
QWidget.__init__(self)
self.setupUi(self)
class MainWindow(QMainWindow,Ui_MainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setupUi(self)
#INSERT pushButton.click to go to HOME here
#INSERT pushButton.click to go to LOGIN here
def setHome(self):
self.label_Screen.setText("HOME")
self.mainwidget = Home()
#NEEDS SOMETHING HERE
def setLogin(self):
self.label_Screen.setText("LOGIN")
self.mainwidget = Login()
#NEEDS SOMETHING HERE
if __name__ == '__main__':
app = QApplication(sys.argv)
Main = MainWindow()
Main.show()
sys.exit(app.exec_())
I think I just need something where I've tagged "#NEEDS SOMETHING HERE", but I'm not sure what!
Cheers!
RESOLVED: thanks to kh25
Just had to add a layout to the QMainWindow and change the setHome to this:
def setHome(self):
self.label_Screen.setText("HOME")
self.currentScreen = Home()
self.layout.addWidget(self.currentScreen)
self.setLayout(self.layout)
The equivalent should be done for the setLogin method also.
You need to create a layout and add the widgets to this layout first. There are various types of layout. Read here:
http://doc.qt.io/qt-4.8/layout.html
For a simple case like yours I'd suggest either using a QHBoxLayout or QVBoxLayout.
Declare this layout. Call addWidget() on the layout for each of the Login and Home widgets and then call setLayout() on the QMainWindow.

Menus and toolbar

I am new in programming and I have created a simple application with one class in Python and PySide which manipulates phone bill csv files. Now I want an option for mobile too.
How can I add a menubar, when my class inherits from QWidget? Should I write another class which inherits from QMainWindow and then make an instance of my first class as a central widget? Is this the right way to do this?
class MyWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
....
class MyWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MyWindow, self).__init__(parent)
widget = MyWidget()
self.setCentralWidget(widget)
...
There's no need for a QMainWindow, you can simply create a QMenuBar in your widget.
class MyWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
self.menu=QtGui.QMenuBar()
self.menu.addAction("do something")
layout=QtGui.QVBoxLayout()
layout.addWidget(self.menu)
A QMainWindow is basically a widget which already has a layout with a menu bar, a toolbar, a status bar, etc. If you don't need all of those functionality, you can use a simple QWidget and add only what you want.

QStyledItemDelegate with QComboBox: Shows Index and not Text

I have a QStyledItemDelegate for a table. In one cell I have a QComboBox created through the delegate's createEditor. I add some items to the combobox listing via self.addItem("an item"); however, when I go into the table and actually select the items I have added, they get replaced with index values starting from 0.
How can I have the QComboBox display the actual text I added in addItem instead of the index they're getting stored in?
Here is a small standalone example of my problem:
import sys
from PySide import QtCore, QtGui, QtSql
class EditDelegate(QtGui.QStyledItemDelegate):
def __init__(self, parent=None):
super(EditDelegate, self).__init__(parent)
def createEditor(self, parent, option, index):
editor = TheEditor(parent)
return editor
class TheEditor(QtGui.QComboBox):
def __init__(self, parent=None):
super(TheEditor, self).__init__(parent)
self.addItem("Item 1")
self.addItem("Item 2")
self.addItem("Item 3")
self.setEditable(True)
class TheTable(QtGui.QTableWidget):
def __init__(self, columns, parent=None):
super(TheTable, self).__init__(parent)
self.setItemDelegate(EditDelegate())
self.setEditTriggers(QtGui.QAbstractItemView.AllEditTriggers)
self.setColumnCount(1)
self.setRowCount(1)
self.setHorizontalHeaderLabels(["QCombo"])
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setCentralWidget(TheTable(self))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
frame = MainWindow()
frame.show()
app.exec_()
Updating PySide to the latest version resolves the issue.
It seems that the default combobox delegate is messing up with the data it receives.You could have fixed that with a custom delegate to paint the correct data. But since you have already solved it, Congratulations!.

How to dynamically change child widgets with Python and Qt?

I would like to create a widget that has a child widget that I can dynamically change. Here is what I tried:
import sys
from PySide.QtCore import *
from PySide.QtGui import *
class Widget(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setLayout(QVBoxLayout())
self.child = QLabel("foo", self)
self.layout().addWidget(self.child)
def update(self):
self.layout().removeWidget(self.child)
self.child = QLabel("bar", self)
self.layout().addWidget(self.child)
app = QApplication(sys.argv)
widget = Widget()
widget.show()
widget.update()
app.exec_()
The problem is that this doesn't actually remove the "foo" label visually. It is still rendered on top of "bar". Screenshot of the problem. How do I remove the old widget so that only the new widget is shown?
I know that I can change the text property of the label. This is not what I want in my application, I need to change the actual widget (to a different widget type).
removeWidget() only removes the item from the layout, it doesn't delete it. You can delete the child widget by calling setParent(None).
def update(self):
self.layout().removeWidget(self.child)
self.child.setParent(None)
self.child = QLabel("bar", self)
self.layout().addWidget(self.child)

Categories

Resources