This has been fixed for now!
I am trying to make a GUI using PyQt4 with an embedded matplotlib canvas. When I scroll with the cursor over the canvas I would like to be able to control the behaviour based on an additional key press (control in this case). However, the key attribute of the mouseEvent linked to the 'scroll_event' is always None. I tested that my code correctly registers the key for a mouseEvent generated by 'button_press_event'.
In the example below the on_press method correctly prints the key that was pressed at the same time. Whereas on_scoll always prints None.
How can I get access the key that was pressed during the mouse scroll event?
Thanks in advance!
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import matplotlib
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
class GraphicTool(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.create_menu()
def on_press(self, event):
print event.key
def on_scroll(self, event):
print event.key
if event.key == 'ctrl':
# do something
pass
else:
# do something else
pass
def create_main_frame(self):
self.main_frame = QWidget()
# Create the mpl Figure and FigCanvas objects.
# 10x8 inches, 100 dots-per-inch
self.dpi = 100
self.fig = Figure((10.0, 8.0), dpi=self.dpi)
self.canvas = FigureCanvas(self.fig)
self.canvas.setParent(self.main_frame)
self.canvas.setFocusPolicy(Qt.ClickFocus)
self.canvas.setFocus()
self.axes = self.fig.add_subplot(111)
self.canvas.mpl_connect('scroll_event', self.on_scroll)
self.canvas.mpl_connect('button_press_event', self.on_press)
# Create the navigation toolbar, tied to the canvas
self.mpl_toolbar = NavigationToolbar(self.canvas,
self.main_frame)
vbox = QVBoxLayout()
vbox.addWidget(self.canvas)
vbox.addWidget(self.mpl_toolbar)
self.main_frame.setLayout(vbox)
self.setCentralWidget(self.main_frame)
def main():
app = QApplication(sys.argv)
viewer = GraphicTool()
viewer.show()
viewer.raise_()
app.exec_()
if __name__ == "__main__":
main()
The short example above actually does work as expected. However, when I incorporate it into a bigger project it fails. I will keep debugging to see if any other events are disturbing the scroll event.
I have checked your code. This is working as expected.
def on_scroll(self, event):
print event.xdata
print event.ydata
print event.key
if event.key == 'ctrl':
# do something
pass
else:
# do something else
pass
This is the output of your code. I have just added xdata and ydata to your code.
0.480977867785
0.57896567718
control
0.480977867785
0.57896567718
shift
0.480977867785
0.57896567718
shift
Related
I am currently trying to embed a graph I want to plot in a pyqt4 user interface I designed. As I am almost completely new to programming - I do not get how people did the embedding in the examples I found - this one (at the bottom) and that one.
It would be awesome if anybody could post a step-by-step explanation or at least a very small, very simple code only creating e.g. a graph and a button in one pyqt4 GUI.
It is not that complicated actually. Relevant Qt widgets are in matplotlib.backends.backend_qt4agg. FigureCanvasQTAgg and NavigationToolbar2QT are usually what you need. These are regular Qt widgets. You treat them as any other widget. Below is a very simple example with a Figure, Navigation and a single button that draws some random data. I've added comments to explain things.
import sys
from PyQt4 import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
import random
class Window(QtGui.QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
# a figure instance to plot on
self.figure = Figure()
# this is the Canvas Widget that displays the `figure`
# it takes the `figure` instance as a parameter to __init__
self.canvas = FigureCanvas(self.figure)
# this is the Navigation widget
# it takes the Canvas widget and a parent
self.toolbar = NavigationToolbar(self.canvas, self)
# Just some button connected to `plot` method
self.button = QtGui.QPushButton('Plot')
self.button.clicked.connect(self.plot)
# set the layout
layout = QtGui.QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
layout.addWidget(self.button)
self.setLayout(layout)
def plot(self):
''' plot some random stuff '''
# random data
data = [random.random() for i in range(10)]
# create an axis
ax = self.figure.add_subplot(111)
# discards the old graph
ax.clear()
# plot data
ax.plot(data, '*-')
# refresh canvas
self.canvas.draw()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = Window()
main.show()
sys.exit(app.exec_())
Edit:
Updated to reflect comments and API changes.
NavigationToolbar2QTAgg changed with NavigationToolbar2QT
Directly import Figure instead of pyplot
Replace deprecated ax.hold(False) with ax.clear()
Below is an adaptation of previous code for using under PyQt5 and Matplotlib 2.0.
There are a number of small changes: structure of PyQt submodules, other submodule from matplotlib, deprecated method has been replaced...
import sys
from PyQt5.QtWidgets import QDialog, QApplication, QPushButton, QVBoxLayout
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
import random
class Window(QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
# a figure instance to plot on
self.figure = plt.figure()
# this is the Canvas Widget that displays the `figure`
# it takes the `figure` instance as a parameter to __init__
self.canvas = FigureCanvas(self.figure)
# this is the Navigation widget
# it takes the Canvas widget and a parent
self.toolbar = NavigationToolbar(self.canvas, self)
# Just some button connected to `plot` method
self.button = QPushButton('Plot')
self.button.clicked.connect(self.plot)
# set the layout
layout = QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
layout.addWidget(self.button)
self.setLayout(layout)
def plot(self):
''' plot some random stuff '''
# random data
data = [random.random() for i in range(10)]
# instead of ax.hold(False)
self.figure.clear()
# create an axis
ax = self.figure.add_subplot(111)
# discards the old graph
# ax.hold(False) # deprecated, see above
# plot data
ax.plot(data, '*-')
# refresh canvas
self.canvas.draw()
if __name__ == '__main__':
app = QApplication(sys.argv)
main = Window()
main.show()
sys.exit(app.exec_())
For those looking for a dynamic solution to embed Matplotlib in PyQt5 (even plot data using drag and drop). In PyQt5 you need to use super on the main window class to accept the drops. The dropevent function can be used to get the filename and rest is simple:
def dropEvent(self,e):
"""
This function will enable the drop file directly on to the
main window. The file location will be stored in the self.filename
"""
if e.mimeData().hasUrls:
e.setDropAction(QtCore.Qt.CopyAction)
e.accept()
for url in e.mimeData().urls():
if op_sys == 'Darwin':
fname = str(NSURL.URLWithString_(str(url.toString())).filePathURL().path())
else:
fname = str(url.toLocalFile())
self.filename = fname
print("GOT ADDRESS:",self.filename)
self.readData()
else:
e.ignore() # just like above functions
For starters the reference complete code gives this output:
I'm currently trying to make a graphical interface using PyQt5. Everything has been working fine until now (I have some experience in this after all), but I've been struggling with this problem for a few days now and I can't find a solution anywhere.
In my app I display a FigureCanvas from matplotlib (along with various other widgets) where I have a key event connected to a function which picks a point from a mouse click and saves this point in a QTableWidget. However, I want the user to be able to edit the y-value that was picked up using a validator, so I changed the second column from holding a QTableWidgetItem to a QLineEdit widget. Everything works except when I press enter after editing the QLineEdit widget in the table, the focus doesn't change as expected (I set the focus to go back to the canvas but the cell stays active with the cursor blinking).
In the same app I have another QLineEdit widget elsewhere in the layout which works as expected. When I press enter in this box, the function connected to its returnPressed signal sends the focus back to the canvas.
Can anyone please help me figure out what is going on?
Also, I don't understand why the app start up with focus being on the QLineEdit when I explicitly set focus to the canvas while creating the main frame. Do you know how to fix this?
I've stripped back my app to a minimal example pasted below and the behavior remains the same.
Just to summarize:
I click on the canvas and press 'a' which call the function 'add_point'.
I then click a point in the plot which creates a new row in the Widget and sets focus to the second column.
I input whatever float value I want here and press Enter.
The function 'print_val' is then called but the focus doesn't return to the canvas as expected. The cursor stays blinking in the table cell.
When I edit the leftmost QLineEdit (outside the table) and hit Enter, the function 'func' is called and correctly sends the focus back to the canvas.
Why do the two instances of QLineEdit behave differently?
For reference, the output of the function 'print_val' gives me:
TableWidget has focus? False
Cell editor has focus? False
Thanks in advance!
import sys
from matplotlib.backends.backend_qt5agg import FigureCanvas, NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class GraphicInterface(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.setWindowTitle('Test')
self._main = QWidget()
self.setCentralWidget(self._main)
# Create Toolbar and Menubar
toolbar = QToolBar()
toolbar.setMovable(False)
toolbar_fontsize = QFont()
toolbar_fontsize.setPointSize(14)
quit_action = QAction("Close", self)
quit_action.setFont(toolbar_fontsize)
quit_action.setStatusTip("Quit the application")
quit_action.triggered.connect(self.close)
quit_action.setShortcut("ctrl+Q")
toolbar.addAction(quit_action)
# =============================================================
# Start Layout:
layout = QHBoxLayout(self._main)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
self.line_editor = QLineEdit("3")
self.line_editor.setValidator(QIntValidator(1, 100))
self.line_editor.returnPressed.connect(self.func)
layout.addWidget(self.line_editor)
# Create Table for Pixel Identifications:
pixtab_layout = QVBoxLayout()
label_pixtab_header = QLabel("Pixel Table\n")
label_pixtab_header.setAlignment(Qt.AlignCenter)
self.linetable = QTableWidget()
self.linetable.verticalHeader().hide()
self.linetable.setColumnCount(2)
self.linetable.setHorizontalHeaderLabels(["Pixel", "Wavelength"])
self.linetable.setColumnWidth(0, 80)
self.linetable.setColumnWidth(1, 90)
self.linetable.setFixedWidth(180)
pixtab_layout.addWidget(label_pixtab_header)
pixtab_layout.addWidget(self.linetable)
layout.addLayout(pixtab_layout)
# Create Figure Canvas:
right_layout = QVBoxLayout()
self.fig = Figure(figsize=(6, 6))
self.canvas = FigureCanvas(self.fig)
self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.canvas.updateGeometry()
self.canvas.mpl_connect('key_press_event', self.on_key_press)
self.canvas.setFocusPolicy(Qt.StrongFocus)
self.canvas.setFocus()
right_layout.addWidget(self.canvas)
self.mpl_toolbar = NavigationToolbar(self.canvas, self)
right_layout.addWidget(self.mpl_toolbar)
layout.addLayout(right_layout)
# Plot some random data:
self.ax = self.fig.add_subplot(111)
self.ax.plot([0, 1, 5, 10, 20], [-1, 2, -4, 5, -2])
def on_key_press(self, e):
if e.key == "a":
self.add_point()
def add_point(self):
print("Select point...")
point = self.fig.ginput(1)
x0, _ = point[0]
rowPosition = self.linetable.rowCount()
self.linetable.insertRow(rowPosition)
item = QTableWidgetItem("%.2f" % x0)
item.setFlags(Qt.ItemIsEnabled)
item.setBackground(QColor('lightgray'))
self.linetable.setItem(rowPosition, 0, item)
y_item = QLineEdit("")
y_item.setValidator(QDoubleValidator())
y_item.returnPressed.connect(self.print_val)
self.linetable.setCellWidget(rowPosition, 1, y_item)
self.linetable.cellWidget(rowPosition, 1).setFocus()
def print_val(self):
rowPosition = self.linetable.rowCount()
editor = self.linetable.cellWidget(rowPosition-1, 1)
y_in = float(editor.text())
print(" Table value: %.2f" % y_in)
# and set focus back to canvas:
self.canvas.setFocus()
# Check which widget has focus:
focus_widget = self.focusWidget()
print(focus_widget.__class__)
print("TableWidget has focus? %r" % self.linetable.hasFocus())
print("Cell editor has focus? %r" % editor.hasFocus())
def func(self):
# dome some stuff
print(self.line_editor.text())
# and set focus back to canvas:
self.canvas.setFocus()
if __name__ == '__main__':
# Launch App:
qapp = QApplication(sys.argv)
app = GraphicInterface()
app.show()
qapp.exec_()
If you want to validate the information of a QTableWidget it is not necessary to insert a QLineEdit but use the one created by the delegate and set a QValidator there:
class ValidatorDelegate(QtWidgets.QStyledItemDelegate):
def createEditor(self, parent, option, index):
editor = super(ValidatorDelegate, self).createEditor(parent, option, index)
if isinstance(editor, QtWidgets.QLineEdit):
editor.setValidator(QtGui.QDoubleValidator(editor))
return editor
And then open the editor with the edit method of the view, to change the focus you can use the delegate's closeEditor signal:
import sys
from matplotlib.backends.backend_qt5agg import (
FigureCanvas,
NavigationToolbar2QT as NavigationToolbar,
)
from matplotlib.figure import Figure
from PyQt5 import QtCore, QtGui, QtWidgets
class ValidatorDelegate(QtWidgets.QStyledItemDelegate):
def createEditor(self, parent, option, index):
editor = super(ValidatorDelegate, self).createEditor(parent, option, index)
if isinstance(editor, QtWidgets.QLineEdit):
editor.setValidator(QtGui.QDoubleValidator(editor))
return editor
class GraphicInterface(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(GraphicInterface, self).__init__(parent)
self.setWindowTitle("Test")
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
# Create Toolbar and Menubar
toolbar = QtWidgets.QToolBar(movable=False)
toolbar_fontsize = QtGui.QFont()
toolbar_fontsize.setPointSize(14)
quit_action = QtWidgets.QAction("Close", self)
quit_action.setFont(toolbar_fontsize)
quit_action.setStatusTip("Quit the application")
quit_action.triggered.connect(self.close)
quit_action.setShortcut("ctrl+Q")
toolbar.addAction(quit_action)
self.line_editor = QtWidgets.QLineEdit("3")
self.line_editor.setValidator(QtGui.QIntValidator(1, 100))
label_pixtab_header = QtWidgets.QLabel(
"Pixel Table\n", alignment=QtCore.Qt.AlignCenter
)
self.linetable = QtWidgets.QTableWidget()
self.linetable.verticalHeader().hide()
self.linetable.setColumnCount(2)
self.linetable.setHorizontalHeaderLabels(["Pixel", "Wavelength"])
self.linetable.setColumnWidth(0, 80)
self.linetable.setColumnWidth(1, 90)
self.linetable.setFixedWidth(180)
delegate = ValidatorDelegate(self.linetable)
self.linetable.setItemDelegate(delegate)
layout = QtWidgets.QHBoxLayout(self._main)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
layout.addWidget(self.line_editor)
pixtab_layout = QtWidgets.QVBoxLayout()
pixtab_layout.addWidget(label_pixtab_header)
pixtab_layout.addWidget(self.linetable)
layout.addLayout(pixtab_layout)
# Create Figure Canvas:
right_layout = QtWidgets.QVBoxLayout()
self.fig = Figure(figsize=(6, 6))
self.canvas = FigureCanvas(self.fig)
self.canvas.setSizePolicy(
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding
)
self.canvas.updateGeometry()
self.canvas.mpl_connect("key_press_event", self.on_key_press)
self.canvas.setFocusPolicy(QtCore.Qt.StrongFocus)
self.canvas.setFocus()
right_layout.addWidget(self.canvas)
self.mpl_toolbar = NavigationToolbar(self.canvas, self)
right_layout.addWidget(self.mpl_toolbar)
layout.addLayout(right_layout)
delegate.closeEditor.connect(self.canvas.setFocus)
# Plot some random data:
self.ax = self.fig.add_subplot(111)
self.ax.plot([0, 1, 5, 10, 20], [-1, 2, -4, 5, -2])
def on_key_press(self, e):
if e.key == "a":
self.add_point()
def add_point(self):
print("Select point...")
point = self.fig.ginput(1)
if not point:
return
x0, _ = point[0]
row = self.linetable.rowCount()
self.linetable.insertRow(row)
item = QtWidgets.QTableWidgetItem("%.2f" % x0)
item.setFlags(QtCore.Qt.ItemIsEnabled)
item.setBackground(QtGui.QColor("lightgray"))
self.linetable.setItem(row, 0, item)
index = self.linetable.model().index(row, 1)
self.linetable.edit(index)
if __name__ == "__main__":
# Launch App:
qapp = QtWidgets.QApplication(sys.argv)
app = GraphicInterface()
app.show()
qapp.exec_()
I'm trying to adjust widget size to figure size. For some reason the size of the canvas and the widget is not changing. I tried to set SizePolicy of the container widget and the canvas, same result.
import sys
import matplotlib.pyplot as plt
from PyQt4 import QtGui
class CanvasOnWidget(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self._layout = QtGui.QVBoxLayout(self)
self._fig = plt.figure()
self._layout.addWidget(self._fig.canvas)
plt.plot(range(10),range(10))
self._fig.canvas.mpl_connect('button_press_event', self._resize)
def _resize(self, event):
w,h = self._fig.get_size_inches()
if event.button == 1: # left click
h-=1
elif event.button == 3: # right click
h+=1
self._fig.set_size_inches(w, h, forward=True)
self._fig.canvas.draw()
self._fig.canvas.flush_events()
print self.size()
print self._fig.canvas.size()
print self._fig.get_size_inches()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = CanvasOnWidget()
main.show()
sys.exit(app.exec_())
I guess you need to inform the widget about your desire to change its size.
This can be done using .resize. Due to there being a border around the canvas you need to take this border into account when setting the new widget size.
import sys
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from PyQt4 import QtGui
class CanvasOnWidget(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self._layout = QtGui.QVBoxLayout(self)
self.setLayout(self._layout)
self._fig = Figure()
self.canv = FigureCanvas(self._fig)
self._layout.addWidget(self.canv)
ax = self._fig.add_subplot(111)
ax.plot(range(10),range(10))
self._fig.canvas.mpl_connect('button_press_event', self._resize)
def _resize(self, event):
w,h = self._fig.get_size_inches()
dw = self.size().width()-w*self._fig.dpi
dh = self.size().height()-h*self._fig.dpi
if event.button == 1: # left click
h-=1
elif event.button == 3: # right click
h+=1
self._fig.set_size_inches(w, h, forward=True)
self._fig.canvas.draw_idle()
self.resize(w*100+dw,h*100+dh)
print self.size()
print self._fig.canvas.size()
print self._fig.get_size_inches()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = CanvasOnWidget()
main.show()
sys.exit(app.exec_())
Note that I have changed to code in order to place a Figure into a FigureCanvasQT. Using pyplot inside a Qt GUI will lead to problems.
However, if you want to use pyplot, the whole code can be simplified a lot.
import matplotlib.pyplot as plt
class Plot():
def __init__(self):
self._fig = plt.figure()
ax = self._fig.add_subplot(111)
ax.plot(range(10),range(10))
self._fig.canvas.mpl_connect('button_press_event', self._resize)
def _resize(self, event):
w,h = self._fig.get_size_inches()
if event.button == 1: # left click
h-=1
elif event.button == 3: # right click
h+=1
self._fig.set_size_inches(w, h, forward=True)
self._fig.canvas.draw_idle()
print self._fig.get_size_inches()
if __name__ == '__main__':
app = Plot()
plt.show()
I am currently trying to embed a graph I want to plot in a pyqt4 user interface I designed. As I am almost completely new to programming - I do not get how people did the embedding in the examples I found - this one (at the bottom) and that one.
It would be awesome if anybody could post a step-by-step explanation or at least a very small, very simple code only creating e.g. a graph and a button in one pyqt4 GUI.
It is not that complicated actually. Relevant Qt widgets are in matplotlib.backends.backend_qt4agg. FigureCanvasQTAgg and NavigationToolbar2QT are usually what you need. These are regular Qt widgets. You treat them as any other widget. Below is a very simple example with a Figure, Navigation and a single button that draws some random data. I've added comments to explain things.
import sys
from PyQt4 import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
import random
class Window(QtGui.QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
# a figure instance to plot on
self.figure = Figure()
# this is the Canvas Widget that displays the `figure`
# it takes the `figure` instance as a parameter to __init__
self.canvas = FigureCanvas(self.figure)
# this is the Navigation widget
# it takes the Canvas widget and a parent
self.toolbar = NavigationToolbar(self.canvas, self)
# Just some button connected to `plot` method
self.button = QtGui.QPushButton('Plot')
self.button.clicked.connect(self.plot)
# set the layout
layout = QtGui.QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
layout.addWidget(self.button)
self.setLayout(layout)
def plot(self):
''' plot some random stuff '''
# random data
data = [random.random() for i in range(10)]
# create an axis
ax = self.figure.add_subplot(111)
# discards the old graph
ax.clear()
# plot data
ax.plot(data, '*-')
# refresh canvas
self.canvas.draw()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = Window()
main.show()
sys.exit(app.exec_())
Edit:
Updated to reflect comments and API changes.
NavigationToolbar2QTAgg changed with NavigationToolbar2QT
Directly import Figure instead of pyplot
Replace deprecated ax.hold(False) with ax.clear()
Below is an adaptation of previous code for using under PyQt5 and Matplotlib 2.0.
There are a number of small changes: structure of PyQt submodules, other submodule from matplotlib, deprecated method has been replaced...
import sys
from PyQt5.QtWidgets import QDialog, QApplication, QPushButton, QVBoxLayout
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
import random
class Window(QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
# a figure instance to plot on
self.figure = plt.figure()
# this is the Canvas Widget that displays the `figure`
# it takes the `figure` instance as a parameter to __init__
self.canvas = FigureCanvas(self.figure)
# this is the Navigation widget
# it takes the Canvas widget and a parent
self.toolbar = NavigationToolbar(self.canvas, self)
# Just some button connected to `plot` method
self.button = QPushButton('Plot')
self.button.clicked.connect(self.plot)
# set the layout
layout = QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
layout.addWidget(self.button)
self.setLayout(layout)
def plot(self):
''' plot some random stuff '''
# random data
data = [random.random() for i in range(10)]
# instead of ax.hold(False)
self.figure.clear()
# create an axis
ax = self.figure.add_subplot(111)
# discards the old graph
# ax.hold(False) # deprecated, see above
# plot data
ax.plot(data, '*-')
# refresh canvas
self.canvas.draw()
if __name__ == '__main__':
app = QApplication(sys.argv)
main = Window()
main.show()
sys.exit(app.exec_())
For those looking for a dynamic solution to embed Matplotlib in PyQt5 (even plot data using drag and drop). In PyQt5 you need to use super on the main window class to accept the drops. The dropevent function can be used to get the filename and rest is simple:
def dropEvent(self,e):
"""
This function will enable the drop file directly on to the
main window. The file location will be stored in the self.filename
"""
if e.mimeData().hasUrls:
e.setDropAction(QtCore.Qt.CopyAction)
e.accept()
for url in e.mimeData().urls():
if op_sys == 'Darwin':
fname = str(NSURL.URLWithString_(str(url.toString())).filePathURL().path())
else:
fname = str(url.toLocalFile())
self.filename = fname
print("GOT ADDRESS:",self.filename)
self.readData()
else:
e.ignore() # just like above functions
For starters the reference complete code gives this output:
I want draw a cross in my paintEvent() which moves with the mouse. I'm using python via pyqt.
I'm using the code below, but the result isn't good.
from __future__ import division
import sys
import platform
# Qt4 bindings for core Qt functionalities (non-GUI)
import PyQt4.QtCore as QtCore
# Python Qt4 bindings for GUI objects
import PyQt4.QtGui as QtGui
from PyQt4.QtGui import *
from PyQt4.QtCore import *
# import the Qt4Agg FigureCanvas object, that binds Figure to
# Qt4Agg backend. It also inherits from QWidget
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
# Matplotlib Figure object
from matplotlib.figure import Figure
class C_MplCanvas(FigureCanvas):
def __init__(self):
# setup Matplotlib Figure and Axis
self.fig = Figure()
#self.cursor = Cursor()
self.fig.set_facecolor('black')
self.fig.set_edgecolor('black')
#self.ax = self.fig.add_subplot(111)
#self.ax.patch.set_facecolor('black')
# initialization of the canvas
FigureCanvas.__init__(self, self.fig)
#super(FigureCanvas, self).__init__(self.fig)
# we define the widget as expandable
FigureCanvas.setSizePolicy(self,
QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding)
# notify the system of updated policy
FigureCanvas.updateGeometry(self)
self.xx=0
self.yy=0
self.justDoubleClicked=False
def contextMenuEvent(self, event):
menu = QMenu(self)
oneAction = menu.addAction("&One")
twoAction = menu.addAction("&Two")
self.connect(oneAction, SIGNAL("triggered()"), self.one)
self.connect(twoAction, SIGNAL("triggered()"), self.two)
'''
if not self.message:
menu.addSeparator()
threeAction = menu.addAction("Thre&e")
self.connect(threeAction, SIGNAL("triggered()"),
self.three)
'''
menu.exec_(event.globalPos())
def one(self):
self.message = QString("Menu option One")
self.update()
def two(self):
self.message = QString("Menu option Two")
self.update()
def three(self):
self.message = QString("Menu option Three")
self.update()
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.TextAntialiasing)
#painter.drawText(self.rect(), Qt.AlignCenter, text)
#
#painter.setPen('red')
pen=painter.pen()
painter.setPen(QColor(255, 0, 0))
painter.drawLine(self.xx-100,self.yy,self.xx+100,self.yy)
painter.drawLine(self.xx,self.yy-100,self.xx,self.yy+100)
self.update()
def mouseReleaseEvent(self, event):
if self.justDoubleClicked:
self.justDoubleClicked = False
else:
self.setMouseTracking(not self.hasMouseTracking())
self.update()
def mouseMoveEvent(self, event):
self.xx=event.pos().x()
self.yy=event.pos().y()
self.update()
class C_MPL(QWidget):
def __init__(self, parent = None):
# initialization of Qt MainWindow widget
super(C_MPL, self).__init__(parent)
#QtGui.QWidget.__init__(self, parent)
# instantiate a widget, it will be the main one
#self.main_widget = QtGui.QWidget(self)
#vbl = QtGui.QVBoxLayout(self.main_widget)
# set the canvas to the Matplotlib widget
self.canvas = C_MplCanvas()
# create a vertical box layout
self.vbl = QtGui.QVBoxLayout()
# add mpl widget to vertical box
self.vbl.addWidget(self.canvas)
# set the layout to th vertical box
self.setLayout(self.vbl)
if __name__ == "__main__":
import sys
'''
def valueChanged(a, b):
print a, b
'''
app = QApplication(sys.argv)
form = C_MPL()
#form.connect(form, SIGNAL("valueChanged"), valueChanged)
form.setWindowTitle("C_MPL")
#form.move(0, 0)
form.show()
#form.resize(400, 400)
app.exec_()
#bmu: That's great,just like I want.And now there another question:
cid0=self.mpl_connect('axes_enter_event', self.enter_axes)
cid1=self.mpl_connect('button_press_event', self.onpick)
cid2=self.mpl_connect('motion_notify_event', self.onmove)
cid3=self.mpl_connect('draw_event', self.clear)
cid4=self.mpl_connect('key_press_event',self.press)
the strange thing is the 'key_press_event' can't be triggerd but all other events can. there r a case: macosx backend ignores multiple mpl_connect() calls
https://github.com/matplotlib/matplotlib/pull/585,
but i think it's differnt with me.
i got cid0,1,2,3,4 which is different each other
So , any idea can share? I an very crazy now.....
Following is my code,u can test it if u have the same problem:
import sys
import platform
from PyQt4 import QtGui, QtCore
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt4 import NavigationToolbar2QT as NavigationToolbar
import time
from PyQt4.QtCore import *
from PyQt4.QtGui import *
#Just a test
class MplCanvas(FigureCanvas):
def __init__(self):
# initialization of the canvas
self.fig=Figure()
FigureCanvas.__init__(self,self.fig )
self.ax = self.fig.add_axes([.15, .15, .75, .75])
self.canvas = self.ax.figure.canvas
#my added
#self.ax = self.fig.add_axes([.15, .15, .75, .75])
#cursor = C_Cursor(self.LvsT, useblit=True, color='red', linewidth=2 )
x=np.arange(0,20,0.1)
self.ax.plot(x,x*x,'o')
self.ax.set_xlim(-2,2)
self.ax.set_ylim(-2,2)
self.visible = True
self.horizOn = True
self.vertOn = True
self.useblit = True
#if self.useblit:
#lineprops['animated'] = True
self.lineh = self.ax.axhline(self.ax.get_ybound()[0], visible=False)
self.linev = self.ax.axvline(self.ax.get_xbound()[0], visible=False)
self.background = None
self.needclear = False
self.count = 0
cid0=self.mpl_connect('axes_enter_event', self.enter_axes)
cid1=self.mpl_connect('button_press_event', self.onpick)
cid2=self.mpl_connect('motion_notify_event', self.onmove)
cid3=self.mpl_connect('draw_event', self.clear)
cid4=self.mpl_connect('key_press_event',self.press)
self.draw()
def clear(self, event):
'clear the cursor'
if self.useblit:
self.background = self.canvas.copy_from_bbox(self.ax.bbox)
self.linev.set_visible(False)
self.lineh.set_visible(False)
def onmove(self, event):
'on mouse motion draw the cursor if visible'
print("move")
if event.inaxes != self.ax:
self.linev.set_visible(False)
self.lineh.set_visible(False)
if self.needclear:
self.canvas.draw()
self.needclear = False
return
self.needclear = True
if not self.visible: return
self.linev.set_xdata((event.xdata, event.xdata))
self.lineh.set_ydata((event.ydata, event.ydata))
self.linev.set_visible(self.visible and self.vertOn)
self.lineh.set_visible(self.visible and self.horizOn)
self._update()
def _update(self):
if self.useblit:
if self.background is not None:
self.canvas.restore_region(self.background)
self.ax.draw_artist(self.linev)
self.ax.draw_artist(self.lineh)
self.canvas.blit(self.ax.bbox)
else:
self.canvas.draw_idle()
return False
#
def enter_axes(self,event):
print "Enter"
def onpick(self,event):
print "click"
print 'you pressed', event.canvas
a = np.arange(10)
print a
print self.count
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(a)
fig.show()
def press(self,event):
print ('press', event.key)
self.fig.canvas.draw()
class MplWidget(QtGui.QWidget):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self, parent)
self.vbl = QtGui.QVBoxLayout()
self.canvas = MplCanvas()
self.vbl.addWidget(self.canvas)
self.setLayout(self.vbl)
if __name__ == "__main__":
app = QApplication(sys.argv)
form = MplWidget()
form.show()
#form.resize(400, 400)
app.exec_()
#all:Tks all. I resolved this problem after added:
self.canvas.setFocusPolicy( Qt.ClickFocus )
self.canvas.setFocus()
maybe this a little bug bcs i need explicity setfocus on cavans,maybe if not:)
details u can check https://github.com/matplotlib/matplotlib/issues/707
Here is an example using matplotlib event handling (however I am wondering, if it is really useful to have something like a second cursor).
import numpy as np
import matplotlib.pyplot as plt
class MouseCross(object):
def __init__(self, ax, **kwargs):
self.ax = ax
self.line, = self.ax.plot([0], [0], visible=False, **kwargs)
def show_cross(self, event):
if event.inaxes == self.ax:
self.line.set_data([event.xdata], [event.ydata])
self.line.set_visible(True)
else:
self.line.set_visible(False)
plt.draw()
if __name__ == '__main__':
fig, ax = plt.subplots()
ax.plot(np.random.random(100) * 10.0)
# note that not every "normal" matplotlib marker will work
# math symbols work fine
cross = MouseCross(ax, marker=r'$\bigoplus$', markersize=30,
color='red',)
fig.canvas.mpl_connect('motion_notify_event', cross.show_cross)
plt.tight_layout()
plt.show()
Question...are you trying to change what the cursor looks like over your widget? If so, the easiest thing is to do get rid of the paintEvent entirely, and add setCursor to your init method:
class C_MplCanvas(FigureCanva):
def __init__( self ):
# setup code
...
self.setCursor(Qt.CrossCursor)
This will tell Qt to use the Cross Cursor whenever the mouse is over that particular widget. This will actually replace the arrow (which may or may not be what you want). If that isn't what you want, I would recommend doing something where you create a PNG image of what you do want, create a child QLabel, and move the child widget around in your mouseMoveEvent.
If you need more help on that lemme know.