FigureCanvas Fail To Appear When Receiving Signal From Popup - python

I'm hoping I can get some help with a problem I've been stuck on all day. I've looked through previous questions but nothing seems to quite match up with the issue I'm facing. Perhaps a fresh pair of eyes can guide me in the right direction. I'll include my code at the end of my question.
Background:
I'm working on a simple application that consists of the main window and a popup window. The main window contains only one button that opens the popup window when pressed. The popup window contains two checkbox options, an ok button, and a cancel button. When pressed, the popup's ok button returns a signal to the main window. This signal contains a list of 1s and 0s, depending on the status of the checkboxes. The purpose of the list is so that a function in the main window can determine which canvases to plot (either plot 1, plot 2, or both).
It should be noted that the main window is organized using a grid layout, and each canvas is meant to be displayed on a row in column 3 with a corresponding label in column 4. The desired outcome when creating both canvases is as shown in the figure:
Problem:
Everything works fine until the end when the signal returns to the slot at popup_input() within the main window. No errors occur but the canvases simply do not appear. Taking the code for creating the figures from popup_input() and placing it instead into open_options() does seem to work and gives the figure as shown above. However, my goal is to have the plots appear after the user makes their selection. Does anyone have any idea why the canvases are failing to appear?
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import QDialog, QApplication, QMainWindow, QLabel, QPushButton
from PyQt5.QtGui import QFont
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.ticker import FormatStrFormatter
class Options(QMainWindow):
popup_response = QtCore.pyqtSignal(object)
def __init__(self):
super().__init__()
self._title = 'Plotting Options'
self.setWindowTitle(self._title)
self.selected_plots = [1, 1] # Always have both options selected by default
self.option1 = QtWidgets.QCheckBox('Option 1')
self.option1.setChecked(True)
self.option2 = QtWidgets.QCheckBox('Option 2')
self.option2.setChecked(True)
self.checkbox_layout = QtWidgets.QHBoxLayout()
self.checkbox_layout.addWidget(self.option1)
self.checkbox_layout.addWidget(self.option2)
self.ok_button = QtWidgets.QPushButton('OK', self)
self.ok_button.clicked.connect(lambda: self.clicked_ok())
self.cancel_button = QtWidgets.QPushButton('Cancel', self)
self.cancel_button.clicked.connect(lambda: self.clicked_cancel())
self.button_layout = QtWidgets.QHBoxLayout()
self.button_layout.addWidget(self.ok_button)
self.button_layout.addWidget(self.cancel_button)
self._popup = QtWidgets.QWidget()
self.setCentralWidget(self._popup)
self.layout = QtWidgets.QGridLayout(self._popup)
self.layout.addLayout(self.checkbox_layout, 0, 0)
self.layout.addLayout(self.button_layout, 1, 0)
def finalize_selected_plots(self):
if self.option1.isChecked() == False:
self.selected_plots[0] = 0
if self.option2.isChecked() == False:
self.selected_plots[1] = 0
return self.selected_plots
def clicked_ok(self):
self.plots = self.finalize_selected_plots()
# Send selection back to Results Window
main_window = MainWindow()
static_reply = self.plots
self.popup_response.connect(main_window.popup_input)
self.popup_response.emit(static_reply)
self.plots = [1,1]
self.close()
def clicked_cancel(self):
self.close()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.exPopup = Options()
self.data_to_plot = [[1, 5, 10], [2, 4, 6], [6, 4, 2]] # Dummy values
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
self.button_options = QtWidgets.QPushButton('Plot Options', self)
self.button_options.clicked.connect(self.open_options)
self.option_layout = QtWidgets.QHBoxLayout()
self.option_layout.addWidget(self.button_options)
self.layout = QtWidgets.QGridLayout(self._main)
self.layout.addLayout(self.option_layout, 0, 2)
def open_options(self):
self.exPopup.show()
#QtCore.pyqtSlot(object)
def popup_input(self, reply):
plot_title_font_size = 15
data = self.data_to_plot
if reply[0] != 0:
self.figure_xy1 = FigureCanvas(Figure(figsize=(5, 5)))
self._figure1 = self.figure_xy1.figure.subplots()
self._figure1.grid()
self.layout.addWidget(self.figure_xy1, 1, 3)
self.figure_label1 = QLabel('Test Plot 1', self)
self.figure_label1.setFont(QFont('Times', plot_title_font_size))
self.figure_label1.setAlignment(QtCore.Qt.AlignLeft)
self.layout.addWidget(self.figure_label1, 1, 4)
x = data[0]
y = data[1]
self._figure1.plot(x, y, '-')
self._figure1.set_xlabel('x')
self._figure1.set_ylabel('y1')
self._figure1.figure.canvas.draw()
if reply[1] != 0:
self.figure_xy2 = FigureCanvas(Figure(figsize=(5, 5)))
self._figure2 = self.figure_xy2.figure.subplots()
self._figure2.grid()
self.layout.addWidget(self.figure_xy2, 2, 3)
self.figure_label2 = QLabel('Test Plot 2', self)
self.figure_label2.setFont(QFont('Times', plot_title_font_size))
self.figure_label2.setAlignment(QtCore.Qt.AlignLeft)
self.layout.addWidget(self.figure_label2, 2, 4)
x = data[0]
y = data[2]
self._figure2.plot(x, y, '-')
self._figure2.set_xlabel('x')
self._figure2.set_ylabel('y2')
self._figure2.figure.canvas.draw()
if __name__ == '__main__':
app = QApplication(sys.argv)
widget = QtWidgets.QStackedWidget()
main_window = MainWindow()
widget.addWidget(main_window)
widget.setWindowTitle("Main Window")
widget.show()
try:
sys.exit(app.exec_())
except:
print("Exiting")

I have found a solution to my issue. With the help of jmacey over on Reddit's pyqt page, here's what I figured out:
My popup should inherit QDialog vs QMainWindow
Simplified dialog button scheme by utilizing QDialogButtonBox
Include accepted and rejected signals for my ok and cancel buttons, respectively. I also included an extra signal for clicked_ok that would perform my desired function as the dialog closes.
popup dialog should be executed from my main window. If I click ok in my dialog, then it will run my code related to plotting.
Here are my updated classes:
class Options(QDialog):
popup_response = QtCore.pyqtSignal(object)
def __init__(self, parent=None):
super().__init__(parent)
self._title = 'Plotting Options'
self.setWindowTitle(self._title)
self.setGeometry(100, 100, 300, 200)
self.selected_plots = [1, 1] # Always have both options selected by default
self.option1 = QtWidgets.QCheckBox('Option 1')
self.option1.setChecked(True)
self.option2 = QtWidgets.QCheckBox('Option 2')
self.option2.setChecked(True)
self.checkbox_layout = QtWidgets.QHBoxLayout()
self.checkbox_layout.addWidget(self.option1)
self.checkbox_layout.addWidget(self.option2)
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.button_box.accepted.connect(self.clicked_ok)
self.button_box.rejected.connect(self.reject)
self.popup_layout = QtWidgets.QVBoxLayout()
self.popup_layout.addLayout(self.checkbox_layout)
self.popup_layout.addWidget(self.button_box)
self.setLayout(self.popup_layout)
def finalize_selected_plots(self):
if self.option1.isChecked() == False:
self.selected_plots[0] = 0
if self.option2.isChecked() == False:
self.selected_plots[1] = 0
return self.selected_plots
def clicked_ok(self):
print("clicked ok")
self.plots = self.finalize_selected_plots()
# Send selection back to Results Window
# main_window = MainWindow()
# static_reply = self.plots
# self.popup_response.connect(main_window.popup_input)
# self.popup_response.emit(static_reply)
self.plots = [1,1]
self.close()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
#self.exPopup = Options()
self.data_to_plot = [[1, 5, 10], [2, 4, 6], [6, 4, 2]] # Dummy values
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
self.button_options = QtWidgets.QPushButton('Plot Options', self)
#self.button_options.clicked.connect(self.open_options)
self.button_options.clicked.connect(self.popup_input)
self.option_layout = QtWidgets.QHBoxLayout()
self.option_layout.addWidget(self.button_options)
self.layout = QtWidgets.QGridLayout(self._main)
self.layout.addLayout(self.option_layout, 0, 2)
# def open_options(self):
# self.exPopup.show()
##QtCore.pyqtSlot(object)
def popup_input(self):
plot_title_font_size = 15
data = self.data_to_plot
self.grab_popup = Options(self)
if self.grab_popup.exec():
print("Pressed ok")
if reply[0] != 0:
self.figure_xy1 = FigureCanvas(Figure(figsize=(5, 5)))
self._figure1 = self.figure_xy1.figure.subplots()
self._figure1.grid()
self.layout.addWidget(self.figure_xy1, 1, 3)
self.figure_label1 = QLabel('Test Plot 1', self)
self.figure_label1.setFont(QFont('Times', plot_title_font_size))
self.figure_label1.setAlignment(QtCore.Qt.AlignLeft)
self.layout.addWidget(self.figure_label1, 1, 4)
x = data[0]
y = data[1]
self._figure1.plot(x, y, '-')
self._figure1.set_xlabel('x')
self._figure1.set_ylabel('y1')
self._figure1.figure.canvas.draw()
if reply[1] != 0:
self.figure_xy2 = FigureCanvas(Figure(figsize=(5, 5)))
self._figure2 = self.figure_xy2.figure.subplots()
self._figure2.grid()
self.layout.addWidget(self.figure_xy2, 2, 3)
self.figure_label2 = QLabel('Test Plot 2', self)
self.figure_label2.setFont(QFont('Times', plot_title_font_size))
self.figure_label2.setAlignment(QtCore.Qt.AlignLeft)
self.layout.addWidget(self.figure_label2, 2, 4)
x = data[0]
y = data[2]
self._figure2.plot(x, y, '-')
self._figure2.set_xlabel('x')
self._figure2.set_ylabel('y2')
self._figure2.figure.canvas.draw()
else:
print("Pressed Cancel")

Related

PyQtgraph - Draw ROI by mouse click & drag

I would like to draw ROI's by click and drag events in the PlotWidget. The issue is that several click interactions are already reserved for the PlotWidget, second it is hard to tell the right position of the mouse in the PlotWidget - especially when the image scale has been changed or if the window scale has been changed.
import pyqtgraph as pg
import pyqtgraph.opengl as gl
from pyqtgraph.Qt import QtCore, QtGui, QtWidgets
from PyQt5 import Qt
from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
import vtk, sys
import numpy as np
from PIL import Image
class GUI:
def __init__(self):
self.init_gui()
def proxyWidget(self, item, width=None, height=None):
proxy = QtGui.QGraphicsProxyWidget()
if(height != None):
height = item.sizeHint().height() if height==None else height
item.setMaximumHeight(height)
if(width!=None):
width = item.sizeHint().width() if width==None else width
item.setMaximumWidth(width)
proxy.setWidget(item)
return proxy
def init_gui(self, win_height=800, win_width=1800):
pg.setConfigOptions(imageAxisOrder='row-major')
pg.setConfigOption('background', 'w')
pg.setConfigOption('foreground', 'k')
self.w = pg.GraphicsWindow(size=(win_width,win_height), border=True)
self.img = pg.ImageItem()
self.list_imgs = QtGui.QListWidget()
self.btn_Del_Mark = QtGui.QPushButton('Del Mark')
self.btn_MarkPed = QtGui.QPushButton('Mark ped')
self.lbl_list1 = QtGui.QLabel("List Images")
self.lbl_list2 = QtGui.QLabel("List Markings")
self.list_imgs = QtGui.QListWidget()
self.list_marks = QtGui.QListWidget()
self.layout = QtGui.QGridLayout()
self.w.setLayout(self.layout)
#self.w_3d = pg.GraphicsWindow()
self.vtkWidget = QVTKRenderWindowInteractor()
#self.w_3d.addItem(self.proxyWidget(self.vtkWidget))
self.vtkWidget.Initialize()
self.vtkWidget.Start()
self.ren = vtk.vtkRenderer()
self.vtkWidget.GetRenderWindow().AddRenderer(self.ren)
self.iren = self.vtkWidget.GetRenderWindow().GetInteractor()
# Create source
source = vtk.vtkSphereSource()
source.SetCenter(0, 0, 0)
source.SetRadius(5.0)
# Create a mapper
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(source.GetOutputPort())
# Create an actor
actor = vtk.vtkActor()
actor.SetMapper(mapper)
self.ren.AddActor(actor)
self.ren.ResetCamera()
self.iren.Initialize()
self.iren.Start()
path = "/home/brain/uni/frustum-pointnets/dataset/KITTI/object/testing/image_2/000000.png"
imgdata = Image.open(path)
self.imgArr = np.array(imgdata)
#ToDo: undistort Image if neccessary
self.img.setImage(self.imgArr)
#self.vbLayout = self.w.addLayout(row=0, col=3, rowspan=10, colspan=20)
imageGraph = pg.PlotWidget(name='Signalgraph')
self.vb = imageGraph.plotItem.vb
self.lbl_list1.setAlignment(QtCore.Qt.AlignCenter)
self.lbl_list2.setAlignment(QtCore.Qt.AlignCenter)
self.vb.setAspectLocked()
self.vb.addItem(self.img)
self.vb.invertY(True)
self.vb.setMaximumSize(int(7/10.*win_width), int(9/20.*win_height))
self.layout.addWidget(imageGraph, 1 , 3, 10, 20)
self.layout.addWidget(self.vtkWidget , 11, 3, 10, 20)
self.layout.addWidget(self.lbl_list1 , 0, 1, 1, 1)
self.layout.addWidget(self.lbl_list2 , 0, 2, 1, 1)
self.layout.addWidget(self.list_imgs , 1, 1, 20,1)
self.layout.addWidget(self.list_marks, 1, 2, 20,1)
sizeHint = lambda: pg.QtCore.QSize(int(1./10.*win_width), int(0.9/20.*win_height))
self.lbl_list1.sizeHint = lambda: pg.QtCore.QSize(int(1./10.*win_width), int(0.9/20.*win_height))
self.lbl_list2.sizeHint = lambda: pg.QtCore.QSize(int(1./10.*win_width), int(0.9/20.*win_height))
self.list_imgs.sizeHint = lambda: pg.QtCore.QSize(int(1./10.*win_width), int(18/20.*win_height))
self.list_marks.sizeHint = lambda: pg.QtCore.QSize(int(1./10.*win_width), int(18/20.*win_height))
self.list_imgs.setMaximumWidth(int(1./10.*win_width))
self.list_marks.setMaximumWidth(int(1./10.*win_width))
self.vtkWidget.show()
if __name__ == "__main__":
app = QtGui.QApplication([])
guiobj = GUI()
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
I would like to start drawing the ROI by mouse click and stop drawing by mouse release... every hint would be helpful. Please consider that the content of the PlotWidget is drag-able and that it might need to be frozen while drawing a ROI.
EDIT:
I have tried to overwrite temporary the click events with the following lines, but somehow clickevents seem to be triggered somewhere else, since my functions do not get called...
def on_btn_MarkPed(self):
#self.vb.setMouseEnabled(x=False, y=False)
self.creatRoiByMouse("Pedestrian")
def on_btn_MarkCycl(self):
self.creatRoiByMouse("Cyclist")
def on_btn_MarkVehicle(self):
self.creatRoiByMouse("Vehicle")
def creatRoiByMouse(self, class2Mark):
self.img.mousePressEvent = self.ImgMousePressEvent
self.img.mouseReleaseEvent = self.ImgMouseReleaseEvent
def ImgMousePressEvent(self, event):
print(event)
pass
#
#
def ImgMouseReleaseEvent(self, event):
print(event)
pass
I know this post is old, but in case someone else finds it someday I thought I'd post my solution. It's inelegant, but it works for me. In my application, I have a button labeled "draw" that calls a function that overwrites the mouse drag event temporarily to simply draw a box instead. The overwritten mouse drag event restores the native mouse drag event with the finish signal. This code also overwrites the escape key to cancel the draw if pressed after pushing my draw button but before drawing. In my code, imageArrayItem is an existing imageItem in a plot widget, and Dialog is a QDialog containing the plot widget.
def clickDraw(self):
app.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))
imageArrayItem.getViewBox().setMouseMode(ViewBox.RectMode)
imageArrayItem.getViewBox().rbScaleBox.setPen(fn.mkPen((255, 255, 255), width = 1))
imageArrayItem.getViewBox().rbScaleBox.setBrush(fn.mkBrush(255, 255, 255, 100))
def mouseDragEvent(ev, axis = None): # This is a modified version of the original mouseDragEvent function in pyqtgraph.ViewBox
ev.accept() # accept all buttons
dif = (ev.pos() - ev.lastPos()) * -1
mouseEnabled = np.array(imageArrayItem.getViewBox().state['mouseEnabled'], dtype = np.float)
mask = mouseEnabled.copy()
if ev.button() & QtCore.Qt.LeftButton:
if imageArrayItem.getViewBox().state['mouseMode'] == ViewBox.RectMode:
if ev.isFinish():
QtCore.QTimer.singleShot(0, self.restoreCursor)
imageArrayItem.getViewBox().rbScaleBox.hide()
ax = QtCore.QRectF(Point(ev.buttonDownPos(ev.button())), Point(ev.pos()))
ax = imageArrayItem.getViewBox().childGroup.mapRectFromParent(ax)
imageArrayItem.getViewBox().mouseDragEvent = temp # reset to original mouseDragEvent
imageArrayItem.getViewBox().setMouseMode(ViewBox.PanMode)
else:
imageArrayItem.getViewBox().updateScaleBox(ev.buttonDownPos(), ev.pos()) # update shape of scale box
elif ev.button() & QtCore.Qt.MidButton: # allow for panning with middle mouse button
tr = dif*mask
tr = imageArrayItem.getViewBox().mapToView(tr) - imageArrayItem.getViewBox().mapToView(Point(0,0))
x = tr.x() if mask[0] == 1 else None
y = tr.y() if mask[1] == 1 else None
imageArrayItem.getViewBox()._resetTarget()
if x is not None or y is not None:
imageArrayItem.getViewBox().translateBy(x=x, y=y)
imageArrayItem.getViewBox().sigRangeChangedManually.emit(imageArrayItem.getViewBox().state['mouseEnabled'])
def keyPressE_mouseDrag(event): # Override "esc" key to cancel draw
if event.key() == QtCore.Qt.Key_Escape:
QtCore.QTimer.singleShot(0, self.restoreCursor)
imageArrayItem.getViewBox().rbScaleBox.hide()
imageArrayItem.getViewBox().mouseDragEvent = temp # reset to original mouseDragEvent
imageArrayItem.getViewBox().setMouseMode(ViewBox.PanMode)
else:
QtWidgets.QDialog.keyPressEvent(Dialog, event)
Dialog.keyPressEvent = keyPressE_mouseDrag
temp = imageArrayItem.getViewBox().mouseDragEvent # save original mouseDragEvent for later
imageArrayItem.getViewBox().mouseDragEvent = mouseDragEvent # set to modified mouseDragEvent

Insert VTK into the right spot of layout

I am trying to put the VTK 3D scene widget into the right spot of pyqtgraphics.GraphicsLayout(). But either the scene does not appear if i am not assigning the window as parent or if I do - the scene does not adapt to the free area, it is just floating in the upper left corner.
I am currently struggling to find a solution to put a viewbox and vtkrenderer into the same window.
One of my attempts was to assign the self.w (see below this paragraph)- which is my main window as a parent to the renderer but then I don't know how to tell the renderer to place itself in the lower right corner of the window instead of floating in the upper left corner - which also leads to overlapping of other elements in the window.
Creating new window and assigning this window as parent
self.vtkWidget = QVTKRenderWindowInteractor(self.w_3d)
Using refered window as parent -> leads to floating and overlapping rendered scene
self.vtkWidget = QVTKRenderWindowInteractor(self.w)
import pyqtgraph as pg
import pyqtgraph.opengl as gl
from pyqtgraph.Qt import QtCore, QtGui, QtWidgets
from PyQt5 import Qt
from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
import vtk, sys
class GUI:
def __init__(self):
self.init_gui()
def proxyWidget(self, item, width=None, height=None):
proxy = QtGui.QGraphicsProxyWidget()
if(height != None):
height = item.sizeHint().height() if height==None else height
item.setMaximumHeight(height)
if(width!=None):
width = item.sizeHint().width() if width==None else width
item.setMaximumWidth(width)
proxy.setWidget(item)
return proxy
def init_gui(self, win_height=800, win_width=1800):
#self.w = self
#self.w.scene().sigMouseClicked.connect(self.mousePressEvent) #mouseMoveEvent
#self.w.scene().sigMouseMoved.connect(self.mouseMoveEvent)
pg.setConfigOptions(imageAxisOrder='row-major')
pg.setConfigOption('background', 'w')
pg.setConfigOption('foreground', 'k')
self.w = pg.GraphicsWindow(size=(win_width,win_height), border=True)
self.img = pg.ImageItem()
self.list_imgs = QtGui.QListWidget()
self.btn_Del_Mark = QtGui.QPushButton('Del Mark')
self.btn_MarkPed = QtGui.QPushButton('Mark ped')
self.lbl_list1 = QtGui.QLabel("List Images")
self.lbl_list2 = QtGui.QLabel("List Markings")
self.list_imgs = QtGui.QListWidget()
self.list_marks = QtGui.QListWidget()
self.layout = pg.GraphicsLayout()
self.w_3d = pg.GraphicsWindow()
self.vb = pg.ViewBox()
self.lbl_list1.setAlignment(QtCore.Qt.AlignCenter)
self.lbl_list2.setAlignment(QtCore.Qt.AlignCenter)
self.vb.setAspectLocked()
self.vb.addItem(self.img)
self.vb.invertY(True)
self.vtkWidget = QVTKRenderWindowInteractor(self.w_3d)
self.w_3d.addItem(self.proxyWidget(self.vtkWidget))
self.vtkWidget.Initialize()
self.vtkWidget.Start()
self.ren = vtk.vtkRenderer()
self.vtkWidget.GetRenderWindow().AddRenderer(self.ren)
self.iren = self.vtkWidget.GetRenderWindow().GetInteractor()
# Create source
source = vtk.vtkSphereSource()
source.SetCenter(0, 0, 0)
source.SetRadius(5.0)
# Create a mapper
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(source.GetOutputPort())
# Create an actor
actor = vtk.vtkActor()
actor.SetMapper(mapper)
self.ren.AddActor(actor)
self.ren.ResetCamera()
self.iren.Initialize()
self.iren.Start()
self.vtkWidget.show()
self.layout.addItem(self.vb , 1, 3, 20, 20)
self.layout.addItem(self.proxyWidget(self.lbl_list1 , width=(int(1./10.*win_width)), height=(int(0.9/20.*win_height))), 0,1,1,1)
self.layout.addItem(self.proxyWidget(self.lbl_list2 , width=(int(1./10.*win_width)), height=(int(0.9/20.*win_height))), 0,2,1,1)
self.layout.addItem(self.proxyWidget(self.list_imgs , width=(int(1./10.*win_width))), 1,1,20,1)
self.layout.addItem(self.proxyWidget(self.list_marks , width=(int(1./10.*win_width))), 1,2,20,1)
self.w.addItem(self.layout)
if __name__ == "__main__":
app = QtGui.QApplication([])
guiobj = GUI()
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
EDIT:
Currently I have solved it by having two windows - after several days of failed attempts. But even this looks crap - since the rendered scene floats in the window without reaction to window resize events...
I would like to have those two windows - in one:
One of the failed attempts was as follows - but then pyqt acquires space in the layout without showing the renderer scene in the window... - just empty space:
self.vtkWidget = QVTKRenderWindowInteractor() #No Parent
#...see rest of code in the section above with exception of the following 2 lines
self.layout.addItem(self.proxyWidget(self.vtkWidget), 10, 3, 10, 20)
self.vtkWidget.show()
I was able to solve it - I have replaced the QGraphicsLayout with QGridlayout and removed all proxywidget-wrappers and I found a workaround for adding a viewbox by using the plotwidget.
I just need now to remove the axes - but this is secondary
import pyqtgraph as pg
import pyqtgraph.opengl as gl
from pyqtgraph.Qt import QtCore, QtGui, QtWidgets
from PyQt5 import Qt
from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
import vtk, sys
import numpy as np
from PIL import Image
class GUI:
def __init__(self):
self.init_gui()
def proxyWidget(self, item, width=None, height=None):
proxy = QtGui.QGraphicsProxyWidget()
if(height != None):
height = item.sizeHint().height() if height==None else height
item.setMaximumHeight(height)
if(width!=None):
width = item.sizeHint().width() if width==None else width
item.setMaximumWidth(width)
proxy.setWidget(item)
return proxy
def init_gui(self, win_height=800, win_width=1800):
pg.setConfigOptions(imageAxisOrder='row-major')
pg.setConfigOption('background', 'w')
pg.setConfigOption('foreground', 'k')
self.w = pg.GraphicsWindow(size=(win_width,win_height), border=True)
self.img = pg.ImageItem()
self.list_imgs = QtGui.QListWidget()
self.btn_Del_Mark = QtGui.QPushButton('Del Mark')
self.btn_MarkPed = QtGui.QPushButton('Mark ped')
self.lbl_list1 = QtGui.QLabel("List Images")
self.lbl_list2 = QtGui.QLabel("List Markings")
self.list_imgs = QtGui.QListWidget()
self.list_marks = QtGui.QListWidget()
self.layout = QtGui.QGridLayout()
self.w.setLayout(self.layout)
#self.w_3d = pg.GraphicsWindow()
self.vtkWidget = QVTKRenderWindowInteractor()
#self.w_3d.addItem(self.proxyWidget(self.vtkWidget))
self.vtkWidget.Initialize()
self.vtkWidget.Start()
self.ren = vtk.vtkRenderer()
self.vtkWidget.GetRenderWindow().AddRenderer(self.ren)
self.iren = self.vtkWidget.GetRenderWindow().GetInteractor()
# Create source
source = vtk.vtkSphereSource()
source.SetCenter(0, 0, 0)
source.SetRadius(5.0)
# Create a mapper
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(source.GetOutputPort())
# Create an actor
actor = vtk.vtkActor()
actor.SetMapper(mapper)
self.ren.AddActor(actor)
self.ren.ResetCamera()
self.iren.Initialize()
self.iren.Start()
path = "/home/brain/uni/frustum-pointnets/dataset/KITTI/object/testing/image_2/000000.png"
imgdata = Image.open(path)
self.imgArr = np.array(imgdata)
#ToDo: undistort Image if neccessary
self.img.setImage(self.imgArr)
#self.vbLayout = self.w.addLayout(row=0, col=3, rowspan=10, colspan=20)
imageGraph = pg.PlotWidget(name='Signalgraph')
self.vb = imageGraph.plotItem.vb
self.lbl_list1.setAlignment(QtCore.Qt.AlignCenter)
self.lbl_list2.setAlignment(QtCore.Qt.AlignCenter)
self.vb.setAspectLocked()
self.vb.addItem(self.img)
self.vb.invertY(True)
self.vb.setMaximumSize(int(7/10.*win_width), int(9/20.*win_height))
self.layout.addWidget(imageGraph, 1 , 3, 10, 20)
self.layout.addWidget(self.vtkWidget , 11, 3, 10, 20)
self.layout.addWidget(self.lbl_list1 , 0, 1, 1, 1)
self.layout.addWidget(self.lbl_list2 , 0, 2, 1, 1)
self.layout.addWidget(self.list_imgs , 1, 1, 20,1)
self.layout.addWidget(self.list_marks, 1, 2, 20,1)
sizeHint = lambda: pg.QtCore.QSize(int(1./10.*win_width), int(0.9/20.*win_height))
self.lbl_list1.sizeHint = lambda: pg.QtCore.QSize(int(1./10.*win_width), int(0.9/20.*win_height))
self.lbl_list2.sizeHint = lambda: pg.QtCore.QSize(int(1./10.*win_width), int(0.9/20.*win_height))
self.list_imgs.sizeHint = lambda: pg.QtCore.QSize(int(1./10.*win_width), int(18/20.*win_height))
self.list_marks.sizeHint = lambda: pg.QtCore.QSize(int(1./10.*win_width), int(18/20.*win_height))
self.list_imgs.setMaximumWidth(int(1./10.*win_width))
self.list_marks.setMaximumWidth(int(1./10.*win_width))
self.vtkWidget.show()
if __name__ == "__main__":
app = QtGui.QApplication([])
guiobj = GUI()
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

How to aesthetically show a generic number of axes in matplotlib?

I want to do a simple GUI that allows the user to add or remove traces from a plot for any number of traces. It looks like this:
The problems I'm having:
I don't know how to make the axes not to superpose with each other for a generic number of plots.
When I plot more than one trace, and then delete all but one, there are two axes showing for some reason. There should always be one axis per trace being shown.
Is there a way to fix these issues? You can find my code below. The only function that should be changed is update_canvas(), I believe. To try it out, just modify the list name_vars in the main with the number of variables you want. The rest of the example code is self-contained.
import numpy as np
from matplotlib.backends.qt_compat import QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(ApplicationWindow, self).__init__(parent)
global name_vars
self.x = np.array([1,2,3,4,5])
self.y = np.random.random((5, len(name_vars)))
self.num_vars = np.size(self.y,1)
self.name_vars = name_vars
self.tags_on = [0] * self.num_vars
self.colors = ['#1F77B4','#FF7F0E','#2CA02C','#D62728','#9467BD',
'#8C564B','#E377C2','#F7F7F7','#BCBD22','#17BECF']
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
canvas = FigureCanvas(Figure(figsize=(10, 10)))
self.canvas_ax = canvas.figure.subplots()
self.canvas_ax.set_xlabel("Time")
self.canvas_ax_twin = []
self.list_tags = QtWidgets.QComboBox(self)
for name in self.name_vars:
self.list_tags.addItem(name)
button_add = QtWidgets.QPushButton('Add', self)
button_remove = QtWidgets.QPushButton('Remove', self)
button_add.clicked.connect(self.add_plot)
button_remove.clicked.connect(self.remove_plot)
layout = QtWidgets.QGridLayout(self._main)
layout.addWidget(canvas, 0, 0)
dropdown_layout = QtWidgets.QHBoxLayout()
dropdown_layout.addWidget(self.list_tags)
dropdown_layout.addWidget(button_add)
dropdown_layout.addWidget(button_remove)
layout.addLayout(dropdown_layout, 1, 0)
self.show()
def add_plot(self):
selected_tag = self.list_tags.currentIndex()
self.tags_on[selected_tag] = 1
self.update_canvas()
def remove_plot(self):
selected_tag = self.list_tags.currentIndex()
self.tags_on[selected_tag] = 0
self.update_canvas()
def update_canvas(self):
# Delete all traces
self.canvas_ax.clear()
[i.clear() for i in self.canvas_ax_twin]
self.canvas_ax_twin = []
num_plots = 0
for ii in range(self.num_vars):
if self.tags_on[ii] == 1:
# If it's not the first trace, create a twin axis
if num_plots != 0:
self.canvas_ax_twin.append(self.canvas_ax.twinx())
self.canvas_ax_twin[-1].plot(self.x, self.y[:,ii], self.colors[num_plots])
self.canvas_ax_twin[-1].set_ylabel(self.name_vars[ii])
self.canvas_ax_twin[-1].yaxis.label.set_color(self.colors[num_plots])
self.canvas_ax_twin[-1].tick_params(axis='y', colors=self.colors[num_plots])
num_plots += 1
# If it's the first trace, use the original axis
else:
self.canvas_ax.plot(self.x, self.y[:,ii], self.colors[num_plots])
self.canvas_ax.set_ylabel(self.name_vars[ii])
self.canvas_ax.yaxis.label.set_color(self.colors[num_plots])
self.canvas_ax.tick_params(axis='y', colors=self.colors[num_plots])
num_plots += 1
# Show the final plot
self.canvas_ax.figure.canvas.draw()
if __name__ == '__main__':
# Edit the number of elements in name_vars to try the code
name_vars = ['V1','V2','V3','V4']
app = QtWidgets.QApplication([])
ex = ApplicationWindow()
ex.show()
app.exec_()
I would suggest to separate the logic from the actual plotting. This makes it easier to follow through. This solves the second question about not removing all axes.
The question about not letting the axes superimpose may be solved by setting the position of additional twin axes to some distance from the axes, depending on how many axes you have.
ax.spines["right"].set_position(("axes", 1+(n-1)*0.1))
where n is the axes number starting from 0. The main axes (n=0) should be excluded, and the first axes will stay at position 1. Further axes are positionned in steps of 0.1.
Then it makes sense to also adjust the right margin of the main axes to give enough space for the extra spines.
import numpy as np
from matplotlib.backends.qt_compat import QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None, name_vars=[]):
super(ApplicationWindow, self).__init__(parent)
self.x = np.array([1,2,3,4,5])
self.y = np.random.random((5, len(name_vars)))
self.num_vars = np.size(self.y,1)
self.name_vars = name_vars
self.tags_on = [0] * self.num_vars
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
self.figure = Figure(figsize=(10, 10))
canvas = FigureCanvas(self.figure)
self.left = self.figure.subplotpars.left
self.right = self.figure.subplotpars.right
self.canvas_ax = canvas.figure.subplots()
self.canvas_ax.set_xlabel("Time")
self.axes = [self.canvas_ax]
self.list_tags = QtWidgets.QComboBox(self)
for name in self.name_vars:
self.list_tags.addItem(name)
button_add = QtWidgets.QPushButton('Add', self)
button_remove = QtWidgets.QPushButton('Remove', self)
button_add.clicked.connect(self.add_plot)
button_remove.clicked.connect(self.remove_plot)
layout = QtWidgets.QGridLayout(self._main)
layout.addWidget(canvas, 0, 0)
dropdown_layout = QtWidgets.QHBoxLayout()
dropdown_layout.addWidget(self.list_tags)
dropdown_layout.addWidget(button_add)
dropdown_layout.addWidget(button_remove)
layout.addLayout(dropdown_layout, 1, 0)
self.show()
def add_plot(self):
selected_tag = self.list_tags.currentIndex()
self.tags_on[selected_tag] = 1
self.update_canvas()
def remove_plot(self):
selected_tag = self.list_tags.currentIndex()
self.tags_on[selected_tag] = 0
self.update_canvas()
def create_nth_axes(self, n, dataset):
if n == 0:
ax = self.canvas_ax
else:
ax = self.canvas_ax.twinx()
ax.spines["right"].set_position(("axes", 1+(n-1)*0.1))
for direction in ["left", "bottom", "top"]:
ax.spines[direction].set_visible(False)
# adjust subplotparams to make space for new axes spine
new_right = (self.right-self.left)/(1+(n-1)*0.1)+self.left
self.figure.subplots_adjust(right=new_right)
color = next(self.canvas_ax._get_lines.prop_cycler)['color']
ax.set_ylabel(self.name_vars[dataset], color=color)
ax.plot(self.x, self.y[:,dataset], color=color)
return ax
def clear_canvas(self):
# Clear main axes
self.canvas_ax.clear()
# clear and remove other axes
for ax in self.axes[1:]:
ax.clear()
ax.remove()
self.axes = [self.canvas_ax]
self.figure.subplots_adjust(right=0.9)
def update_canvas(self):
self.clear_canvas()
k = 0
for i, tag in enumerate(self.tags_on):
if tag:
ax = self.create_nth_axes(k, i)
if k > 0:
self.axes.append(ax)
k += 1
self.canvas_ax.figure.canvas.draw()
if __name__ == '__main__':
# Edit the number of elements in name_vars to try the code
name_vars = ['V1','V2','V3','V4']
app = QtWidgets.QApplication([])
ex = ApplicationWindow(name_vars=name_vars)
ex.show()
app.exec_()

Can I share the crosshair with two graph in pyqtgraph (pyqt5)

I am implemented two graphs in pyqt5 with pyqtgraph, and want to share cursor between two graphs.
I have a program result at the following:
And I want to share the crosshair like:
Can pyqtgraph do it ? Many thanks.
Following is my code (update), it will show 2 graph and the cursor will only allow move in graph2 only. However, I want the cursor can move and get data from graph1 and graph2.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
from pyqtgraph import MultiPlotWidget
class GUI_create_main_window(QWidget):
def __init__(self):
super().__init__()
main_layout = QVBoxLayout(self)
self.plot1 = pg.PlotWidget()
main_layout.addWidget(self.plot1)
self. plot2 = pg.PlotWidget()
main_layout.addWidget(self.plot2)
self.draw_cursor()
self.setLayout(main_layout)
self.show()
#hair cross event
def eventFilter(self, source, event):
try:
if (event.type() == QtCore.QEvent.MouseMove and
source is self.plot2.viewport()):
pos = event.pos()
if self.plot2.sceneBoundingRect().contains(pos):
mousePoint = self.vb.mapSceneToView(pos)
self.vLine.setPos(mousePoint.x())
self.hLine.setPos(mousePoint.y())
return QtGui.QWidget.eventFilter(self, source, event)
except Exception as e:
traceback.print_exc()
err = sys.exc_info()[1]
print(str(err))
def draw_cursor(self):
#cross hair
self.vLine = pg.InfiniteLine(angle=90, movable=False, pen=pg.mkPen('k', width=1))
self.hLine = pg.InfiniteLine(angle=0, movable=False, pen=pg.mkPen('k', width=1), label='{value:0.1f}',
labelOpts={'position':0.98, 'color': (200,0,0), 'movable': True, 'fill': (0, 0, 200, 100)})
self.plot2.addItem(self.vLine, ignoreBounds=True)
self.plot2.addItem(self.hLine, ignoreBounds=True)
self.vb = self.plot2.plotItem.vb
#set mouse event
self.plot2.setMouseTracking(True)
self.plot2.viewport().installEventFilter(self)
if __name__ == '__main__':
pg.setConfigOption('background', 'w')
pg.setConfigOption('foreground', 'k')
app = QApplication(sys.argv)
gui = GUI_create_main_window()
currentExitCode = app.exec_()
I know that it is kind of late but after struggling with similar problem I came up with a solution.
First of all we can capture mouse movements on pyqtgraph plots by installing 'SignalProxy' on their scene.
For example:
self.proxy = pg.SignalProxy(self.p.scene().sigMouseMoved, rateLimit=120, slot=self.mouseMoved)
This will call mouseMoved method on when mouse move signal is emitted.
We can get mouse position converted to scene position with
def mouseMoved(self, evt):
pos = evt[0]
mousePoint = self.p.vb.mapSceneToView(pos)
x, y = int(mousePoint.x()), int(mousePoint.y())
now we can set vLine pos to x and hLine pos to y on if we can have access to other graph's plot we can also set their vLine pos to x (since x axis should match)
we can link their x axis together (same movement on x axis is applied on other graph) by:
plot.setXLink(other_plot)
Side notes:
I set up two graphs by subclassing pyqtgraph's GraphicsLayoutWidget and adding plots to them individually I'm sure there should be a way of doing it in a single subclass but doing something like...
self.addPlot(title="Plot 1")
self.addPlot(title="Plot 2")
...creates two plots next to each other rather than stacked on top of
each other - we want to only link vLines anyways.
we can access other graph by something like this:
self.top_graph = TopGraph()
self.bottom_graph = BottomGraph()
self.top_graph.other_graph = self.bottom_graph # set bottom graph in top graph to access it in bottom graph
self.bottom_graph.other_graph = self.top_graph # set top graph in bottom graph to access it in top graph
maybe we can do global variables as well but I'm not sure that it is a good practice.
So it all adds up to:
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import pyqtgraph as pg
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
main_widget = QWidget()
main_layout = QVBoxLayout()
main_widget.setLayout(main_layout)
self.setCentralWidget(main_widget)
self.top_graph = TopGraph()
self.bottom_graph = BottomGraph()
self.top_graph.other_graph = self.bottom_graph # set bottom graph in top graph to access it in bottom graph
self.bottom_graph.other_graph = self.top_graph # set top graph in bottom graph to access it in top graph
self.top_graph.p.setXLink(self.bottom_graph.p)
#example plot
x = range(-50, 51)
y1 = [i**2 for i in x]
y2 = [-i**2 for i in x]
self.top_graph.p.plot(x, y1, pen=pg.mkPen("r", width=3))
self.bottom_graph.p.plot(x, y2, pen=pg.mkPen("g", width=3))
splitter = QSplitter(Qt.Vertical)
splitter.addWidget(self.top_graph)
splitter.addWidget(self.bottom_graph)
main_layout.addWidget(splitter)
class TopGraph(pg.GraphicsLayoutWidget):
def __init__(self):
super().__init__()
self.p = self.addPlot()
self.p.hideAxis("bottom")
self.vLine = pg.InfiniteLine(angle=90, movable=False, pen=pg.mkPen('k', width=1))
self.hLine = pg.InfiniteLine(angle=0, movable=False, pen=pg.mkPen('k', width=1), label='{value:0.1f}',
labelOpts={'position':0.98, 'color': (200,0,0), 'movable': True, 'fill': (0, 0, 200, 100)})
self.p.addItem(self.vLine, ignoreBounds=True)
self.p.addItem(self.hLine, ignoreBounds=True)
self.proxy = pg.SignalProxy(self.p.scene().sigMouseMoved, rateLimit=120, slot=self.mouseMoved)
def mouseMoved(self, evt):
pos = evt[0]
if self.p.sceneBoundingRect().contains(pos):
self.hLine.show() # show this graph's h line since we are now in control
mousePoint = self.p.vb.mapSceneToView(pos)
x, y = int(mousePoint.x()), int(mousePoint.y())
self.vLine.setPos(x)
self.hLine.setPos(y)
self.other_graph.vLine.setPos(x)
self.other_graph.hLine.hide() # hide other graphs h line since we don't controll it here
class BottomGraph(pg.GraphicsLayoutWidget):
def __init__(self):
super().__init__()
self.p = self.addPlot()
self.vLine = pg.InfiniteLine(angle=90, movable=False, pen=pg.mkPen('k', width=1))
self.hLine = pg.InfiniteLine(angle=0, movable=False, pen=pg.mkPen('k', width=1), label='{value:0.1f}',
labelOpts={'position':0.98, 'color': (200,0,0), 'movable': True, 'fill': (0, 0, 200, 100)})
self.p.addItem(self.vLine, ignoreBounds=True)
self.p.addItem(self.hLine, ignoreBounds=True)
self.proxy = pg.SignalProxy(self.p.scene().sigMouseMoved, rateLimit=120, slot=self.mouseMoved)
def mouseMoved(self, evt):
pos = evt[0]
if self.p.sceneBoundingRect().contains(pos):
self.hLine.show() # show this graph's h line since we are now in control
mousePoint = self.p.vb.mapSceneToView(pos)
x, y = int(mousePoint.x()), int(mousePoint.y())
self.vLine.setPos(x)
self.hLine.setPos(y)
self.other_graph.vLine.setPos(x)
self.other_graph.hLine.hide() # hide other graphs h line since we don't controll it here
def main():
pg.setConfigOption('background', 'w')
pg.setConfigOption('foreground', 'k')
app = QApplication([])
window = MainWindow()
window.setMinimumSize(600, 400)
window.show()
app.exec()
if __name__ == "__main__":
main()

Matplotlib Live Plot in PyQt4 GUI

I am writing a GUI using PyQt4, and am trying to implement a live updating pyplot chart using the matplotlib animate module.
The attached code works exactly as I am intending if an error conveniently occurs just before plt.show() is called in the main method (I have caused a div0 error here for demonstration).
If plt.show() is called however, 2 other windows are opened alongside the embedded graph (see screenshots). If any of these extra windows are closed, the embedded graph crashes. If plt.show() is removed from the code entirely, the chart live chart never works at all.
I am at a bit of a loss as to what is going on there. This behaviour is unexpected and quite confusing. It would be fantastic if anyone has any suggestions for how I might get everything working properly.
import sys
import json
import time
import math
import dateutil
import requests
from PyQt4 import QtGui, QtCore
import matplotlib.pyplot as plt
from matplotlib import style
import matplotlib.animation as animation
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
style.use('ggplot')
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.time = []
self.recorded_temp = []
self.recorded_setpoint = []
self.runtime = 0
self.element_status = ""
self.temp = 0
self.setpoint = 0
self.setWindowTitle("Distillation Control Panel")
#self.setGeometry(100, 100, 500, 100)
self.stacked_layout = QtGui.QStackedLayout()
self.connect_page = QtGui.QWidget()
self.home_page = QtGui.QWidget()
self.home_UI()
self.connect_UI()
self.stacked_layout.addWidget(self.connect_page)
self.stacked_layout.addWidget(self.home_page)
self.setLayout(self.stacked_layout)
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.update_gui)
self.show()
def main(self):
self.timer.start(1000)
ani = animation.FuncAnimation(self.fig, self.animate, interval=1000)
1 / 0 # Code works if an error occurs here
plt.show()
def home_UI(self):
grid = QtGui.QGridLayout()
runtime_indicator_label = QtGui.QLabel()
self.runtime = QtGui.QLabel()
runtime_indicator_label.setText("Runtime: ")
element_status_indicator = QtGui.QLabel()
self.element_status = QtGui.QLabel()
element_status_indicator.setText("Element Status: ")
temp_indicator_label = QtGui.QLabel()
self.temp = QtGui.QLabel()
temp_indicator_label.setText("Temperature: ")
setpoint_label = QtGui.QLabel()
self.setpoint = QtGui.QLabel()
setpoint_label.setText("Setpoint: ")
up_btn = QtGui.QPushButton("Increase", self)
up_btn.resize(up_btn.minimumSizeHint())
down_btn = QtGui.QPushButton("Decrease", self)
down_btn.resize(down_btn.minimumSizeHint())
self.fig = plt.figure(figsize=(15, 5))
self.canvas = FigureCanvas(self.fig)
self.toolbar = NavigationToolbar(self.canvas, self)
grid.addWidget(runtime_indicator_label, 0, 0, 2, 1)
grid.addWidget(self.runtime, 0, 1, 2, 1)
grid.addWidget(element_status_indicator, 0, 2, 2, 1)
grid.addWidget(self.element_status, 0, 3, 2, 1)
grid.addWidget(temp_indicator_label, 0, 4, 2, 1)
grid.addWidget(self.temp, 0, 5, 2, 1)
grid.addWidget(setpoint_label, 0, 6, 2, 1)
grid.addWidget(self.setpoint, 0, 7, 2, 1)
grid.addWidget(up_btn, 0, 8, 1, 1)
grid.addWidget(down_btn, 1, 8, 1, 1)
grid.addWidget(self.canvas, 3, 0, 1, 9)
grid.addWidget(self.toolbar, 4, 0, 1, 9)
self.home_page.setLayout(grid)
def connect_UI(self):
'''
User interface for connecting to distillation controller server. Enter local IP address for ESP8266
assigned by router
'''
grid = QtGui.QGridLayout()
addr = QtGui.QLineEdit()
addr.setPlaceholderText("Enter IP address for distiller server")
grid.addWidget(addr, 1, 1)
btn = QtGui.QPushButton("Connect!", self)
btn.clicked.connect(lambda: self.connect(addr.text()))
btn.setFocus()
grid.addWidget(btn, 1, 2)
self.connect_page.setLayout(grid)
def connect(self, addr):
'''
Check connection with controller server and verify address
'''
full_address = 'http://' + addr + "/connect"
try:
data = requests.get(str(full_address), timeout=1.0)
if data.status_code == requests.codes.ok:
QtGui.QMessageBox.information(self, 'test', "Connected!", QtGui.QMessageBox.Ok)
self.address = addr
self.stacked_layout.setCurrentIndex(1)
else:
# Add handling if non-200 http code returned
pass
except Exception:
QtGui.QMessageBox.information(self, 'test', "Device not found at {}. Please enter a valid address".format(addr), QtGui.QMessageBox.Ok)
self.connect_UI()
self.main()
def update_gui(self):
full_address = 'http://' + self.address + "/request_data"
data = json.loads(requests.get(full_address).text)
runtime = convertMillis(data["Runtime"])
temp = data['Temp']
setpoint = data["Setpoint"]
self.runtime.setText(runtime)
self.element_status.setText(data["Element status"])
self.temp.setText(str(temp))
self.setpoint.setText(str(setpoint))
self.time.append(dateutil.parser.parse(runtime))
self.recorded_temp.append(temp)
self.recorded_setpoint.append(setpoint)
def animate(self, i):
plt.cla()
plt.plot(self.time, self.recorded_temp)
plt.plot(self.time, self.recorded_setpoint)
plt.xlabel("Running Time", fontsize=6)
plt.ylabel("Temperature (°C)", fontsize=6)
plt.xticks(fontsize=6, rotation=45)
plt.yticks(fontsize=6)
plt.tight_layout()
plt.draw()
def convertMillis(millis):
seconds = math.floor((millis/1000)%60)
minutes = math.floor((millis/(1000*60))%60)
hours = math.floor((millis/(1000*60*60))%24)
return "{:02}:{:02}:{:02}".format(hours, minutes, seconds)
def main():
app = QtGui.QApplication(sys.argv)
GUI = Window()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I am unable to post screenshots, so here is a link to the image

Categories

Resources