Unable to implement mpl_connect - user-interface

I'm working on a GUI that basically hold multiple widgets that each contain a figure as well as a few buttons/whatever. One of the figures is supposed to be interactive, calling a function whenever the user clicks on any part of the plot. Yet I can't get the function to fire using mpl_connect, even after playing with focus and whatnot. I'm somewhat new to PySide/Qt, so I don't exactly understand why my code is behaving like this (I've been searching for days for a solution, but haven't found anything about it).
I used Qt Designer to create the layout for the GUI. I'm using Spyder from Anaconda 2.2.0 (32-bit), Python 2.7, and PySide to develop the GUI. If it's any help, I come from more of a MATLAB background where I developed a full version of the GUI I'm trying to make in Python.
Below is the relevant code (scroll down a bit to see where the problem is):
from PySide import QtCore, QtGui
from PySide.QtCore import *
from PySide.QtGui import *
import numpy as np
import matplotlib
matplotlib.use('Qt4Agg')
matplotlib.rcParams['backend.qt4']='PySide'
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
import matplotlib.pyplot as plt
from PySide.QtGui import QPalette, QCursor
import matplotlib.colors as colors
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1316, 765)
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.widget = QtGui.QWidget(self.centralwidget)
self.widget.setGeometry(QtCore.QRect(75, 40, 375, 490))
self.widget.setObjectName("widget")
color = self.centralwidget.palette().color(QPalette.Window)
self.leftPlot = MatplotlibWidget(None,'','','',False,color)
self.setupPlot(self.widget,self.leftPlot)
self.leftPlot.figure.tight_layout()
self.leftImage = self.leftPlot.axes.imshow(self.defaultSlide, cmap = mymap)
Snippet of interest:
self.leftPlot.figure.canvas.setFocusPolicy(QtCore.Qt.StrongFocus)
self.leftPlot.figure.canvas.setFocus()
cid = self.leftPlot.figure.canvas.mpl_connect('button_release_event', self.getCoordinates) # doesn't get called
plt.show()
def getCoordinates(self, event):
print 'dasdsadadsa'
print 'button=%d, x=%d, y=%d, xdata=%f, ydata=%f'%(event.button, event.x, event.y, event.xdata, event.ydata)
The rest:
class MatplotlibWidget(FigureCanvas):
def __init__(self, parent=None,xlabel='x',ylabel='y',title='Title',showTicks=False,color=None):
super(MatplotlibWidget, self).__init__(Figure())
self.setParent(parent)
if color != None:
self.figure = Figure(facecolor=(color.red()/256.0,color.green()/256.0,color.blue()/256.0),frameon=0)
else:
self.figure = Figure(frameon=0)
self.canvas = FigureCanvas(self.figure)
self.axes = self.figure.add_subplot(111)
self.axes.set_xlabel(xlabel)
self.axes.set_ylabel(ylabel)
self.axes.set_title(title)
self.axes.get_xaxis().set_visible(showTicks)
self.axes.get_yaxis().set_visible(showTicks)
class ControlMainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(ControlMainWindow, self).__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
plt.show()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
mySW = ControlMainWindow()
mySW.show()
sys.exit(app.exec_())
I'm aware the code is messy, but any input is greatly appreciated.

Update (2015-09-04) : I've updated the MWE I provided as part of my original answer to use instead the approach that is suggested in the matplotlib documentation to embed a mpl figure in an application. This approach does not use the pyplot interface (as in my original answer) and use the Object Oriented API of mpl instead. Also, since all the mpl artists (figure, axes, etc.) know each other, there is no need to explicitly create new class variables. This allows a structure of code that is, IMO, easier to read and to maintain.
The problem comes from the fact that you are not connecting correctly your event to self.leftPlot (FigureCanvasQTAgg), but to self.leftPlot.figure.canvas (FigureCanvasQTAgg.figure.FigureCanvasQTAgg) instead. You are creating a canvas within a canvas in the MatplotlibWidget class (which is already a subclass of FigureCanvasQTAgg). You only need to create one mpl canvas, pass a figure to it, and then connect the event to it directly.
I've put together a MWE to demonstrate how this can be done using the Object Oriented API of Matplotlib as suggested in the documentation:
from PySide import QtGui
import numpy as np
import sys
import matplotlib as mpl
mpl.use('Qt4Agg')
mpl.rcParams['backend.qt4']='PySide'
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg
class ControlMainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(ControlMainWindow, self).__init__(parent)
self.setupUi()
def setupUi(self):
figure = mpl.figure.Figure(figsize=(5, 5))
leftPlot = MatplotlibWidget(figure)
self.setCentralWidget(leftPlot)
class MatplotlibWidget(FigureCanvasQTAgg):
def __init__(self, fig):
super(MatplotlibWidget, self).__init__(fig)
#-- set up an axe artist --
ax = fig.add_axes([0.1, 0.1, 0.85, 0.85])
ax.plot(np.arange(15), np.arange(15))
self.draw()
#---- setup event ----
self.mpl_connect('button_press_event', self.onclick)
def onclick(self, event):
x, y = event.x, event.y
print(x, y)
if x != None and y != None:
ax = self.figure.axes[0]
ax.plot(event.xdata, event.ydata, 'ro')
self.draw()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
mySW = ControlMainWindow()
mySW.show()
sys.exit(app.exec_())
The code above results in:

Related

pyqtgrapgh: Image Item not in the center of GraphicsView

I am using pyqtgraph to visualate 2D arrays. Due to the fact that I have to refresh the visualated Image because of a real time application which I want to evaluate I am using GraphicsView(). Then I create a ImageItem and I want to add the Item to the created window. Unfortunately the image is only visible in the left upper corner of the window. I know that I can show the full image without GraphicsView but as I said I will need GraphicsView later to visualate the updated values in real time for an application.
Here is my current code:
import numpy as np
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
import struct
import sounddevice as sd
from scipy.fftpack import fft
import sys
import time
class Fenster(pg.GraphicsLayoutWidget): #objektorientiert arbeiten!!
def __init__(self): #constructor
super().__init__() #ruft den constructor von QWidget auf
self.initMe()
class Fenster(pg.GraphicsLayoutWidget):
def __init__(self):
super().__init__()
self.initMe()
def initMe(self):
self.win = pg.GraphicsView()
self.win.show()
self.plot_data = np.fromfunction(lambda i, j: (1+0.3*np.sin(i)) * (i)**2 + (j)**2, (100, 100))
self.img = pg.ImageItem(image=self.plot_data)
self.cm = pg.colormap.get('CET-L9')
self.bar = pg.ColorBarItem( values= (0, 20_000), colorMap=self.cm )
self.bar.setImageItem(self.img)
self.win.setCentralItem(self.img)
w = Fenster()
That is the output image:
How can I set that the image fullfill the whole window?
How can I do that the created ColorBarItem will be showed in the window? (optionally)
Thanks in advance.
I found the solution. You have to create a PlotItem. Then add the ImageItem to the PlotItem and set the PlotItem in the center of GraphicsView
self.win = pg.GraphicsView()
self.win.show()
self.p = pg.PlotItem()
self.win.setCentralItem(self.p)
self.plot_data = np.fromfunction(lambda i, j: (1+0.3*np.sin(i)) * (i)**2 + (j)**2, (100, 100))
self.img = pg.ImageItem(image=self.plot_data)
self.cm = pg.colormap.get('CET-L9')
self.bar = pg.ColorBarItem( values= (0, 20_000), colorMap=self.cm )
self.bar.setImageItem(self.img)
self.p.addItem(self.img)
I also figured out to update the plot (live plot). Leave a comment if I should share.

tkinter GUI freezes while plotting voltages of an Arduino

I am just trying to write a GUI to help me while measuring. For now, I want to be able to plot -Voltages for example- in real-time from my Arduino UNO. Sadly this code just works fine for around 5 seconds, after this the tkinter windows freezes. Amazingly the v and t list works. Would you please give me a hint to fix this problem? I just spend hours.
from pyfirmata import Arduino, util
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from tkinter import *
import time
import threading
board=Arduino('COM3')
iterator = util.Iterator(board)
iterator.start()
Tvl = board.get_pin('a:0:i')
class mclass(threading.Thread):
def __init__(self, window):
threading.Thread.__init__(self)
self.window = window
self.box = Entry(window)
self.button = Button (window, text="check", command=self.plot)
self.box.pack ()
self.button.pack()
self.t=[]
self.v=[]
self.fig = Figure(figsize=(6,6))
self.a = self.fig.add_subplot(111)
self.a.invert_yaxis()
self.a.set_title ("Estimation Grid", fontsize=16)
self.a.set_ylabel("Y", fontsize=14)
self.a.set_xlabel("X", fontsize=14)
self.canvas = FigureCanvasTkAgg(self.fig, master=self.window)
self.canvas.get_tk_widget().pack(side=BOTTOM, fill=BOTH, expand=False)
def plot (self):
global t,v
clock=time.perf_counter()
while time.perf_counter()-clock<=float(self.box.get()):
self.v.append(Tvl.read())
self.t.append(time.perf_counter()-clock)
if len(self.v)>=25:
del self.v[0]
del self.t[0]
self.a.clear()
self.a.plot(self.t,self.v)
self.canvas.draw()
window= Tk()
t= mclass(window)
t.start()
window.mainloop()
I thank you for your comments. In the end patthoyts hint worked quite well. I would be verry interested why while loops in this case dont work.
Beneath ist the new stable Code with some more Features added.
thanks and greets peterudo
from pyfirmata import Arduino, util
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from tkinter import *
import time
import threading
board=Arduino('COM3')
iterator = util.Iterator(board)
iterator.start()
Tvl = board.get_pin('a:0:i')
class mclass(threading.Thread):
def __init__(self, window):
threading.Thread.__init__(self)
self.window = window
self.box = Entry(window)
self.box.pack ()
self.button = Button (window, text="check", command=self.plot)
self.button.pack()
self.button2 = Button(window,text="stop", command=self.start_stop)
self.button2.pack()
self.w=Scale(window, from_=0, to=10000)
self.w.pack()
self.w2=Scale(window, from_=0, to=100000)
self.w2.pack()
self.t=[]
self.v=[]
self.ss=True
self.fig = Figure(figsize=(6,6))
self.a = self.fig.add_subplot(111)
self.a.invert_yaxis()
self.a.set_title ("Estimation Grid", fontsize=16)
self.a.set_ylabel("Y", fontsize=14)
self.a.set_xlabel("X", fontsize=14)
self.canvas = FigureCanvasTkAgg(self.fig, master=self.window)
self.canvas.get_tk_widget().pack(side=BOTTOM, fill=BOTH, expand=False)
def plot (self):
window.after(10, self.plot)
if self.ss==True:
self.v.append(Tvl.read())
self.t.append(time.perf_counter())
if self.t[0]<self.t[len(self.t)-1]-0.0001*self.w2.get():
del self.v[0]
del self.t[0]
self.a.clear()
self.a.set_ylim(Tvl.read()-0.0001*self.w.get(),Tvl.read()+0.0001*self.w.get())
self.a.set_xlim(self.t[len(self.t)-1]-0.001*self.w2.get()+1,time.perf_counter())
self.a.plot(self.t,self.v)
self.canvas.draw()
def start_stop(self):
if self.ss==True:
self.ss=False
else:
self.ss=True
window= Tk()
t= mclass(window)
t.start()
window.mainloop()

python - checkbox in new window update fails and stays false

import matplotlib
from tkinter import *
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
import tkinter as tk
from tkinter import ttk
import numpy as np
import matplotlib.animation as animation
from matplotlib import style
from matplotlib import pyplot as plt
from scipy.misc import *
import webbrowser
import subprocess
import csv
import matplotlib.ticker as mticker
import matplotlib.dates as mdates
from os import startfile
LARGE_FONT = ("Times", 11, "bold italic")
NORM_FONT = ("Helvetica", 9)
SMALL_FONT = ("Helvetica",7)
HELP_FONT=("Times", 9 , "bold")
class PAL3_guide(tk.Tk):
def __init__(self,*args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage,Tut01):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame=self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self,parent, controller):
tk.Frame.__init__(self,parent, background="white")
button_PAL = ttk.Button(self, text="Setup Guide", command=lambda:controller.show_frame(Tut01))
button_PAL.pack()
class Tut01(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
var=BooleanVar()
label = tk.Label(self, text="Guide", font=LARGE_FONT)
label.pack(pady=10, padx=10)
label = tk.Label(self, text="Check the methods to accomplish", font=NORM_FONT)
label.pack(pady=10, padx=10)
c1 = Checkbutton(self, state=ACTIVE).pack()
c1Label = tk.Label(self, text="digestion1",font=SMALL_FONT).pack()
c2 = Checkbutton(self, state=ACTIVE).pack()
c2Label = tk.Label(self, text="digestion2",font=SMALL_FONT).pack()
c3 = Checkbutton(self, text="enrichment",state=ACTIVE, variable=var, command=lambda:onClick()).pack()
def onClick():
var.get()
if var == True:
print("hi")
else:
print("hiww")
app = PAL3_guide()
app.geometry("1280x920")
app.mainloop()
Please check last code lines around the c3 checkbox.
I dont get why my boolean variable var is not getting updated by clicking the checkbox on the 2nd page. By clicking the checkbox (c3) the GUI should act differently. However as mentioned above it is not getting updated.. and stays False. I feel it keeps the value from start and does not get changed. Thanks for help ..
EDIT:
Above the if statement missed the var.get() and I could make it work in smaller setup.. however, in the real tool I am calling a plt.figure() in the beginning since it included and anmiate function, If I erase this call, everything works like it should, might be that this figure calling forces the checkbutton to have the first onvalue ?
I would change var, to be self.var and set the off and on values explicitly:
offvalue=
The value corresponding to a non-checked button. The default is 0. (offValue/Value)
onvalue=
The value corresponding to a checked button. The default is 1. (onValue/Value)
c3 = Checkbutton(self, text="enrichment",state=ACTIVE,
variable=self.var, command=onClick,
onvalue=True, offvalue=False)
c3.pack()
command= does not take a function call, just a function.
You would have to write either
command=lambda: print(self.var.get())
OR:
def printVar():
print(self.var.get())
THEN:
..., command=printVar, ...
NOTICE the abscence of the paranthesis!
You are supposed to be settting command to a function.
When you use parenthesis, you CALL the function, you DO NOT want that here.

Python GUI QCore.Aplication error

I had tried various workarounds about this problem and tried to fix code according to other examples, but ultimately I had failed to make a workable code. While I do have idea of why it fails, I lack skill to create a workaround about this error. Could you please help me with making this code to work?
The problem:
Then I press ''Duomenų apdorojimas'' and proceed to press ''Pavaizduoti signalą'' I get error saying: QCoreApplication::exec: The event loop is already running. I tried various workarounds about this and some legacy code is left inside my functions. I will tidy and optimize my code later, I just need to know how to work properly with GUI in order to avoid this problem. If needed I will send you entire program with txt files, but this part is essential and here problem arises.
import os
import os.path
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
import pyqtgraph as pg
from tkinter import *
import tkinter.messagebox
import sys
from functools import partial
import matplotlib.pyplot as p
class Window(QtGui.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.setGeometry(50, 50, 500, 300)
self.setWindowTitle("")
self.setWindowIcon(QtGui.QIcon('pythonlogo.png'))
openFile = QtGui.QAction("&Atverkite duomenų failą", self)
openFile.setShortcut("Ctrl+Q")
openFile.setStatusTip('Duomenų failas')
openFile.triggered.connect(self.file_open)
extractAction = QtGui.QAction("&Duomenys iš Arduino", self)
extractAction.setShortcut("Ctrl+W")
extractAction.setStatusTip('Prijunkite iš Arduino ateinančius duomenis')
extractAction.triggered.connect(self.upload_usb)
saveFile = QtGui.QAction("&Įšsaugoti failą", self)
saveFile.setShortcut("Ctrl+E")
saveFile.setStatusTip('Nurodykite failo direktoriją bei pavadinimą')
saveFile.triggered.connect(self.file_save)
quitFile = QtGui.QAction("&Išeiti", self)
quitFile.setShortcut("Ctrl+R")
quitFile.setStatusTip('Programa bus uždaryta')
quitFile.triggered.connect(self.close_application)
openEditor = QtGui.QAction("&Skaitytuvas", self)
openEditor.setShortcut("Ctrl+T")
openEditor.setStatusTip('Skaitytuvas visados įjungtas')
openEditor.triggered.connect(self.editor)
additionalData = QtGui.QAction("&Paciento duomenys", self)
additionalData.setShortcut("Ctrl+A")
additionalData.setStatusTip('Įveskite paciento amžiaus grupę')
additionalData.triggered.connect(self.group)
dataProcessing = QtGui.QAction("&Filtruoti signalą", self)
dataProcessing.setShortcut("Ctrl+S")
dataProcessing.setStatusTip('Bus atliekami signalo apdorojimo procesai')
dataProcessing.triggered.connect(self.editor)
showGraph = QtGui.QAction("&Pavaizduoti signalą", self)
showGraph.setShortcut("Ctrl+D")
showGraph.setStatusTip('Bus atvaizduotas šiuo metu turimas signalas')
showGraph.triggered.connect(self.close_application)
mainMenu = self.menuBar()
fileMenu = mainMenu.addMenu('&Progamos funkcijos')
fileMenu.addAction(openFile)
fileMenu.addAction(extractAction)
fileMenu.addAction(saveFile)
fileMenu.addAction(quitFile)
resultsMenu = mainMenu.addMenu("&Duomenų apdorojimas")
resultsMenu.addAction(dataProcessing)
resultsMenu.addAction(showGraph)
editorMenu = mainMenu.addMenu("&Programos nustatymai")
editorMenu.addAction(openEditor)
editorMenu.addAction(additionalData)
self.statusBar()
self.home()
def openFile(self,n):
print(n)
def home(self):
self.show()
def close_application(self):
graphics()
sys.exit()
def run():
app = QtGui.QApplication(sys.argv)
GUI = Window()
sys.exit(app.exec_())
run()
Well, it turns out that I was trying to open semi-new program in graphics(). I found out how not to create GUI. Thank you for you help, but I have found out solution. For the next time I will post code in onedrive or something in order for people to get whole program with its supporting documents.

Putting stuff in windows

How can I put stuff in the main window? I want to create a line edit in the main window(beneath the menu bar, maybe with some decription laber in front of it). How is this done? I used grid layout and this box layout, nothing works.
(sry for another trivial question, there are only few tutorials on pyside out there, and most of them only cover how to create single windows with buttons ect.)
import sys
from PySide import QtGui, QtCore, QtWebKit
class FirstClass(QtGui.QMainWindow, QtGui.QWidget):
def __init__(self):
super(FirstClass, self).__init__()
self.startingUI()
def startingUI(self):
self.setWindowTitle('Hauptfenster')
self.resize(800, 400)
self.statusBar()
#Menueinstellungen an sich
menue = self.menuBar()
#Actions des Menues:
#datei menue
menuleiste_datei = menue.addMenu('File')
datei_exit = QtGui.QAction('Exit', self)
datei_exit.setStatusTip('Close the programm')
menuleiste_datei.addAction(datei_exit)
datei_exit.triggered.connect(self.close)
#Einstellungen menue
menuleiste_configurations = menue.addMenu('Configurations')
configurations_settings = QtGui.QAction('Settings', self)
configurations_settings.setStatusTip('Configurations(Settings)')
menuleiste_configurations.addAction(configurations_settings)
configurations_settings.triggered.connect(self.newwindow)
self.lineedit = QtGui.QLineEdit()
self.layout = QtGui.QHBoxLayout()
self.layout.addWidget(self.lineedit)
self.setLayout(self.layout)
self.show()
def newwindow(self):
self.wid = QtGui.QWidget()
self.wid.resize(250, 150)
self.setWindowTitle('NewWindow')
self.wid.show()
def main():
app = QtGui.QApplication(sys.argv)
start = FirstClass()
sys.exit(app.exec_())
if __name__== '__main__':
main()
I do not believe creating a class with a multiple inheritance is recommended best practice. If an attribute is not found in FirstClass, then it searches left to right (QtGui.QMainWindow to QtGui.QWidget). From my perspective, this would turn into a nightmare to support and debug. My guess this is why the self.layout is not working properly.
I made separate classes for QtGui.QMainWindow and QtGui.QWidget. FirstWindowClass sets the central widget as FirstWidgetClass. FirstWidgetClass has your QLineEdit and I went ahead and inserted a label. I changed QHBoxLayout to QGridLayout to help you understand how it works.
Some tips from my learning experiences with Python and Pyside these past couple months:
Remember you can always look at PyQt examples and majority will work directly with PySide modules.
I recommend looking over http://srinikom.github.io/pyside-docs/index.html as a lot of the modules have simple examples.
For my personal project, a lot of the solutions to my Qt questions were in C++ so do not be afraid to convert it to python.
import sys
from PySide import QtGui, QtCore, QtWebKit
class FirstWindowClass(QtGui.QMainWindow):
def __init__(self):
super(FirstWindowClass, self).__init__()
self.setWindowTitle('Hauptfenster')
self.resize(800, 400)
self.statusBar()
# Set central widget that expands to fill your window
self.main_widget = FirstWidgetClass(self)
self.setCentralWidget(self.main_widget)
#Menueinstellungen an sich
menue = self.menuBar()
#Actions des Menues:
#datei menue
menuleiste_datei = menue.addMenu('File')
datei_exit = QtGui.QAction('Exit', self)
datei_exit.setStatusTip('Close the programm')
menuleiste_datei.addAction(datei_exit)
datei_exit.triggered.connect(self.close)
#Einstellungen menue
menuleiste_configurations = menue.addMenu('Configurations')
configurations_settings = QtGui.QAction('Settings', self)
configurations_settings.setStatusTip('Configurations(Settings)')
menuleiste_configurations.addAction(configurations_settings)
configurations_settings.triggered.connect(self.newwindow)
# Open the window
self.show()
def newwindow(self):
self.wid = QtGui.QWidget()
self.wid.resize(250, 150)
self.wid.setWindowTitle('NewWindow')
self.wid.show()
class FirstWidgetClass(QtGui.QWidget):
def __init__(self, parent=None):
super(FirstWidgetClass, self).__init__()
self.label_example = QtGui.QLabel('Enter Data:')
self.lineedit = QtGui.QLineEdit()
self.layout = QtGui.QGridLayout()
self.layout.addWidget(self.label_example, 0, 0)
self.layout.addWidget(self.lineedit, 0, 1)
self.setLayout(self.layout)
self.show()
def main():
app = QtGui.QApplication(sys.argv)
start = FirstWindowClass()
sys.exit(app.exec_())
if __name__== '__main__':
main()

Resources