Draw lines with simplecv and pyqt4 - line

I am trying to integrate simplecv to pyqt4 with some mixed success. I was able to see a webcam capture in pyqt4 through simplecv, I can modify the image with simplecv and it shows ok in pyqt4 but when I try to add a geometry or text to the image it is not showing in pyqt4. If I run the simpleCV code on their own it works OK. Can someone help me understand why it is not working? By the way, as you can see I am new to pyqt4 and simpleCV. See the code that I currently have.
#!/usr/bin/env python
import os
import sys
import signal
from PyQt4 import uic, QtGui, QtCore
from webcamGUI3 import *
from SimpleCV import *
class Webcam(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self,parent)
self.MainWindow = Ui_MainWindow()
self.MainWindow.setupUi(self)
self.webcam = Camera(0,{ "width": 640, "height": 480 })
self.timer = QtCore.QTimer()
self.connect(self.timer, QtCore.SIGNAL('timeout()'), self.show_frame)
self.timer.start(1);
def show_frame(self):
ipl_image = self.webcam.getImage()
ipl_image.dl().circle((150, 75), 50, Color.RED, filled = True)
data = ipl_image.getBitmap().tostring()
image = QtGui.QImage(data, ipl_image.width, ipl_image.height, 3 * ipl_image.width, QtGui.QImage.Format_RGB888)
pixmap = QtGui.QPixmap()
pixmap.convertFromImage(image.rgbSwapped())
self.MainWindow.lblWebcam.setPixmap(pixmap)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
webcam = Webcam()
webcam.show()
app.exec_()
Any ideas?

I got my friend, is very simple you just add
  ipl_image = ipl_image.applyLayers()
see:
ipl_image = ipl_image = self.webcam.getImage().binarize().invert()
ipl_image.drawRectangle(30,50,100,100,color=Color.RED,width=3)
ipl_image.drawText('ola galera',80,190,fontsize=50)
ipl_image = ipl_image.applyLayers()
data = ipl_image.getBitmap().tostring()
image = QtGui.QImage(data, ipl_image.width, ipl_image.height, 3 * ipl_image.width, QtGui.QImage.Format_RGB888)
pixmap = QtGui.QPixmap()
pixmap.convertFromImage(image.rgbSwapped())
self.MainWindow.label.setPixmap(pixmap)

Related

PyQt5 window opens then closes in a second

from PyQt5.QtWidgets import QApplication, QPushButton, QMainWindow
from PyQt5.QtGui import QIcon
import sys
class Main(QMainWindow):
def __init__(self, parent=None) -> None:
super().__init__(parent=parent)
self.setWindowTitle("QPushButton")
left = 300
top = 200
width = 300
height = 250
self.setGeometry(left, top, width, height)
self.show()
def main():
App = QApplication(sys.argv)
Main()
sys.exit(App.exec())
if __name__=="__main__":
main()
I don't get it, it worked before now it closes within a second of opening. I'm thinking somehow my installation got corrupted. Do you have any ideas I tried
def main():
App = QApplication(sys.argv)
Main()
App.exec_()

Numpy to QImage Crashing

The following code crashes on clicking the button or after a few clicks when the signal is emitted from thread and caught in the main gui.
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QThread
import numpy as np
import time
from PyQt5.QtWidgets import QApplication, QDialog, QPushButton, QVBoxLayout
def convert_np_qimage(cv_img , width, height):
h, w, ch = cv_img.shape
bytes_per_line = ch * w
qim = QtGui.QImage(cv_img.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)
print(qim.size())
return qim
class VideoThread(QThread):
change_qimage_signal = pyqtSignal(QImage)
def __init__(self):
super().__init__()
def run(self):
print("run")
width = 1280
height = 1280
cv_img = np.zeros([height,width,3],dtype=np.uint8)
cv_img.fill(255)
print("image shape: ", cv_img.shape)
qimg = convert_np_qimage(cv_img, width, height)
self.change_qimage_signal.emit(qimg)
print("emitted")
def stop(self):
self.wait()
import sys
class Dialog(QDialog):
def __init__(self):
super(Dialog, self).__init__()
Dialog.resize(self, 640, 480)
button=QPushButton("Click")
button.clicked.connect(self.startThread)
mainLayout = QVBoxLayout()
mainLayout.addWidget(button)
self.setLayout(mainLayout)
self.setWindowTitle("QImage Example")
def startThread(self):
self.thread = VideoThread()
self.thread.change_qimage_signal.connect(self.getPixmap)
self.thread.start()
def getPixmap(self, qimg):
print("got qimage")
qpixmap = QPixmap.fromImage(qimg)
if __name__ == '__main__':
app = QApplication(sys.argv)
dialog = Dialog()
sys.exit(dialog.exec_())
The program doesn't crash if height and width are set to small number say 3.
The program also doesn't crash if we convert qimage to qpixmap before emitting and change the signal
type to QPixmap.
The program was originally written to get images from webcam using opencv. The numpy array created
by opencv crashes too for big image sizes.
The OS used is Windows10, pyqt version is 5.12.3
Any idea what might be the reason for the crash?
In Linux with PyQt5 5.15 I do not reproduce the problem, but that error is common and occurs because passing "data" does not copy the information but rather the data is shared, so at some point cv_img and all associated objects are destroyed including to "data" so when transmitting it through the signal and setting it in the QLabel that "data" is read but it no longer has reserved memory. The solution in this case is to copy "data":
qim = QtGui.QImage(
cv_img.data.tobytes(), w, h, bytes_per_line, QtGui.QImage.Format_RGB888
)
or copy the QImage.
return qim.copy()

How save a Matplotlib figure with Pickle in a Pyqt5 environment?

I'm facing an issue and I cannot get rid of it.
I'm trying to use the Pickle package in order to save a matplotlib figure to replot it if i want to.
So far I have the below code which open a Qt window and plot some curves in it if the 'if' condition in lfpViewer.__Init__() is 1 (I put 0 only to check the pickle load function).
So I added, to the toolbar, two buttons where I can save a .pickle of the current figure or load a .pickle from a previous figure.
import pickle
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
import os
import matplotlib
import numpy as np
matplotlib.use('Qt5Agg')
import matplotlib.patches as patches
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
class SurfViewer(QMainWindow):
def __init__(self, parent=None):
super(SurfViewer, self).__init__()
self.parent = parent
self.centralWidget = QWidget()
self.color = self.centralWidget.palette().color(QPalette.Background)
self.setCentralWidget(self.centralWidget)
self.plotview = QGroupBox(" ")
self.layout_plotview = QVBoxLayout()
self.mascenelfp = lfpViewer(self)
self.layout_plotview.addWidget(self.mascenelfp)
self.centralWidget.setLayout(self.layout_plotview)
class lfpViewer(QGraphicsView):
def __init__(self, parent=None):
super(lfpViewer, self).__init__(parent)
self.parent=parent
self.scene = QGraphicsScene(self)
self.setScene(self.scene)
self.setBackgroundBrush(QBrush(self.parent.color))# self.setBackgroundBrush(QBrush(QColor(200, 200, 200)))
self.figure = plt.figure(facecolor=[self.parent.color.red()/255,self.parent.color.green()/255,self.parent.color.blue()/255]) #Figure()
self.canvas = FigureCanvas(self.figure)
self.toolbar = NavigationToolbar(self.canvas, self)
self.save_button = QPushButton()
self.save_button.setIcon(QIcon(os.path.join('icons','SaveData.png')))
self.save_button.setToolTip("Save Figure Data")
self.toolbar.addWidget(self.save_button)
self.save_button.clicked.connect(self.saveFigData)
self.load_button = QPushButton()
self.load_button.setIcon(QIcon(os.path.join('icons','LoadData.png')))
self.load_button.setToolTip("Load Figure Data")
self.toolbar.addWidget(self.load_button)
self.load_button.clicked.connect(self.loaddatapickle)
if 0:
t=np.arange(1000)
self.axes_l=self.figure.add_subplot(311)
self.axes_l.plot(t, np.sin(2*3.14*100*t))
self.axes_Y=self.figure.add_subplot(312)
self.axes_Y.plot(t, np.cos(2*3.14*100*t))
self.axes_Yi=self.figure.add_subplot(313)
self.axes_Yi.plot(t, np.tan(2*3.14*100*t))
self.canvas.setGeometry(0, 0, 1600, 500 )
layout = QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
self.setLayout(layout)
def loaddatapickle(self):
fileName = QFileDialog.getOpenFileName(self,'Load Data', '', 'pickle (*.pickle)')
if (fileName[0] == '') :
return
fileName = str(fileName[0])
filehandler = open(fileName , 'rb')
self.figure = pickle.load(filehandler)
filehandler.close()
self.canvas.draw()
self.parent.parent.processEvents()
return
def saveFigData(self):
fileName = QFileDialog.getSaveFileName(self,'Save Figure Data', '', 'pickle (*.pickle)')
if (fileName[0] == '') :
return
fileName = str(fileName[0])
file_pi = open(fileName, 'wb')
pickle.dump(self.figure, file_pi, -1)
file_pi.close()
return
def main():
app = QApplication(sys.argv)
ex = SurfViewer(app)
ex.showMaximized()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The save seems works (well, at least, I have a file), but the load button do absolutly nothing !
Even If I have a .pickle file, I don't know if pickle save the correct binary of the figure because when I load the pickle file in debug mode, I get lot of red stuff.
Look for the below image :
If I do the code without PyQt5, it works fine, for instance, with the below code:
import pickle
import matplotlib
import numpy as np
matplotlib.use('Qt5Agg')
import matplotlib.pyplot as plt
def loaddatapickle():
filehandler = open('test.pickle' , 'rb')
figure = pickle.load(filehandler )
filehandler.close()
return figure
def saveFigData(figure):
file_pi = open('test.pickle', 'wb')
pickle.dump(figure , file_pi, 1)
file_pi.close()
return
figure = plt.figure( ) #Figure()
Save= 0
if Save==1:
t=np.arange(1000)
axes_l=figure.add_subplot(311)
axes_l.plot(t, np.sin(2*3.14*100*t))
axes_Y=figure.add_subplot(312)
axes_Y.plot(t, np.cos(2*3.14*100*t))
axes_Yi=figure.add_subplot(313)
axes_Yi.plot(t, np.tan(2*3.14*100*t))
saveFigData(figure)
else:
figure=loaddatapickle()
plt.show()
If somebody have an idea of what is going on here, please tell me !
Have a nice day.
I can't be sure if this solves the problem you are facing, but let's give it a try. I need to mention, I don't have QT5 available and I'm working with python 2.7 and matplotlib 2.0.0. But the solution here might be valid for general cases.
When adapting the program to pyqt4 and running it, I found out that the pickling works fine. Also the unpickling did not throw any error, so I suspected that there might be a problem of displaying the unpickled figure.
What turns out to allow loading the figure is to not only load the figure into self.figure but to recreate the canvas with this unpickled figure and newly add it to the layout:
def loaddatapickle(self):
#needed to change some stuff here, since in Qt4 the dialog directly returns a string
fileName = QFileDialog.getOpenFileName(self,'Load Data', '' )
if (fileName == '') :
return
fileName = str(fileName)
filehandler = open(fileName , 'rb')
self.figure = pickle.load(filehandler)
filehandler.close()
# remove the old canvas
self.layout().removeWidget(self.canvas)
# create a new canvas
self.canvas = FigureCanvas(self.figure)
# add the new canvas at the position of the old one
self.layout().addWidget(self.canvas, 1)
self.canvas.draw()
self.parent.parent.processEvents()
return
Of course it would be better to directly update the canvas with the new figure, but I haven't found any way to do that.

How to change ipython qtconsole input

I'm making a guide with pyqt and I'm including an ipython qtconsole widget.
try:
from qtconsole.rich_jupyter_widget import RichJupyterWidget as ipythonWidget
from qtconsole.inprocess import QtInProcessKernelManager
except:
from IPython.qt.console.rich_ipython_widget import RichIPythonWidget as ipythonWidget
from IPython.qt.inprocess import QtInProcessKernelManager
I want to modify the qtconsole input from my code but is not working. I've tried the set_next_input function but it doesn't work and I can't find another function I can use to acomplish what I want. Is even possible to achieve what I want? and if so, how can I do it?
Here is my code:
try:
from qtconsole.rich_jupyter_widget import RichJupyterWidget as ipythonWidget
from qtconsole.inprocess import QtInProcessKernelManager
except:
from IPython.qt.console.rich_ipython_widget import RichIPythonWidget as ipythonWidget
from IPython.qt.inprocess import QtInProcessKernelManager
import sys
from PyQt4 import QtGui
class sympyIpython(QtGui.QWidget):
def __init__(self):
super().__init__()
self.ipython = IpythonWidget()
v = QtGui.QVBoxLayout(self)
button = QtGui.QPushButton('append to input')
v.addWidget(self.ipython)
v.addWidget(button)
button.clicked.connect(self.symClicked)
def symClicked(self):
self.ipython.kernel.shell.set_next_input(' appended text')
class IpythonWidget(ipythonWidget):
def __init__(self):
super().__init__()
self.kernel_manager = QtInProcessKernelManager()
self.kernel_manager.start_kernel()
self.kernel = self.kernel_manager.kernel
self.kernel.gui = 'qt4'
self.kernel_client = self.kernel_manager.client()
self.kernel_client.start_channels()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
m = sympyIpython()
m.show()
sys.exit(app.exec_())
Reposting as an answer:
To change the text at the prompt in the Qt console, set input_buffer on the widget object:
jupyter_widget.input_buffer = 'text'

using ginput in embedded matplotlib figure in PyQt4

I'm trying to use the 'ginput' to measure distance in a matplotlib figure by allowing the user to mouse click the locations. I am able to do this independently in the matplotlib figure, but I'm having problems when I tried to set the figure onto a matplotlib canvas and then embed it into PyQt4 widget. Below is my code, most of which were taken from the matplotlib examples. My solution will be to click a set of locations, and pass the (x,y) coordinates to the 'dist_calc' function to get the distance.
import sys
from PyQt4 import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
import random
import numpy as np
class Window(QtGui.QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.fig = Figure((6.5, 5.0), tight_layout=True)
self.ax = self.fig.add_subplot(111)
self.canvas = FigureCanvas(self.fig)
self.toolbar = NavigationToolbar(self.canvas, self)
self.button = QtGui.QPushButton('Plot')
self.button.clicked.connect(self.plot)
self.ndist = QtGui.QPushButton('Measure')
self.ndist.clicked.connect(self.draw_line)
self.toolbar.addWidget(self.button)
self.toolbar.addWidget(self.ndist)
self.fig.tight_layout()
layout = QtGui.QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
self.setLayout(layout)
def plot(self):
data = [random.random() for i in range(20)]
self.ax.hold(False)
self.ax.plot(data, '*-')
self.canvas.draw()
def draw_line(self):
self.xy = plt.ginput(0)
x = [p[0] for p in self.xy]
y = [p[1] for p in self.xy]
self.ax.plot(x,y)
self.ax.figure.canvas.draw()
self.get_dist(x, y)
def get_dist(self, xpts, ypts):
npts = len(xpts)
distArr = []
for i in range(npts-1):
apt = [xpts[i], ypts[i]]
bpt = [xpts[i+1], ypts[i+1]]
dist =self.calc_dist(apt,bpt)
distArr.append(dist)
tdist = np.sum(distArr)
print(tdist)
def calc_dist(self,apt, bpt):
apt = np.asarray(apt)
dist = np.sum((apt - bpt)**2)
dist = np.sqrt(dist)
return dist
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = Window()
main.show()
sys.exit(app.exec_())
According to this comment by one of the lead Matplotlib developers, you must not import pyplot when you're embedding Matplotlib in Qt. Pyplot sets up its own gui, mainloop and canvas, which interfere with the Qt event loop.
Changing the line self.xy = plt.ginput(0) into self.xy = self.fig.ginput(0) did not help but gave an insightful error:
AttributeError: 'FigureCanvasQTAgg' object has no attribute 'manager'
Figure.show works only for figures managed by pyplot, normally created by pyplot.figure().
In short, I don't think this is possible. ginput is a blocking function and seems only to be implemented for a Matplotlib event loop. I'm afraid that you will have to build the functionality you want using Matplotlib mouse events, which do work when embedding in PyQt. Just be sure not to use pyplot!
Edit: I just remembered, perhaps the LassoSelector is what you need.