I want to capture image from camera.I got a way to do it.But it works on PyQt5 successfully.failed on PySide2.
Here's the PySide2 code
import sys
from PySide2.QtWidgets import QWidget, QApplication
from PySide2.QtMultimedia import QCamera, QAbstractVideoSurface, QVideoSurfaceFormat, QVideoFrame
class CameraFrameGrabber(QAbstractVideoSurface):
def __init__(self, parent=None):
super(CameraFrameGrabber, self).__init__(parent)
print('init')
def supportedPixelFormats(self, type):
print("supportedPixelFormats() called")
print("supportedPixelFormats() finished")
return [QVideoFrame.Format_ARGB32, QVideoFrame.Format_ARGB32_Premultiplied,
QVideoFrame.Format_RGB32, QVideoFrame.Format_RGB24, QVideoFrame.Format_RGB565,
QVideoFrame.Format_RGB555, QVideoFrame.Format_ARGB8565_Premultiplied,
QVideoFrame.Format_BGRA32, QVideoFrame.Format_BGRA32_Premultiplied, QVideoFrame.Format_BGR32,
QVideoFrame.Format_BGR24, QVideoFrame.Format_BGR565, QVideoFrame.Format_BGR555,
QVideoFrame.Format_BGRA5658_Premultiplied, QVideoFrame.Format_AYUV444,
QVideoFrame.Format_AYUV444_Premultiplied, QVideoFrame.Format_YUV444,
QVideoFrame.Format_YUV420P, QVideoFrame.Format_YV12, QVideoFrame.Format_UYVY,
QVideoFrame.Format_YUYV, QVideoFrame.Format_NV12, QVideoFrame.Format_NV21,
QVideoFrame.Format_IMC1, QVideoFrame.Format_IMC2, QVideoFrame.Format_IMC3,
QVideoFrame.Format_IMC4, QVideoFrame.Format_Y8, QVideoFrame.Format_Y16,
QVideoFrame.Format_Jpeg, QVideoFrame.Format_CameraRaw, QVideoFrame.Format_AdobeDng]
# def isFormatSupported(self, format):
# print("isFormatSupported() called")
# def start(self, format):
# print("start() called")
def present(self, frame):
print('call present')
print(frame)
return True
# def stop(self):
# print("stop() called")
if __name__ == '__main__':
app = QApplication(sys.argv)
w = QWidget()
w.show()
w.setWindowTitle("Hello PyQt5")
the_camera = QCamera()
the_grabber = CameraFrameGrabber()
the_camera.setViewfinder(the_grabber)
the_camera.start()
sys.exit(app.exec_())
Here's the PyQt5 code
import sys
from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5.QtMultimedia import QCamera, QAbstractVideoSurface, QVideoSurfaceFormat, QVideoFrame
class CameraFrameGrabber(QAbstractVideoSurface):
def __init__(self, parent=None):
super(CameraFrameGrabber, self).__init__(parent)
print('init')
def supportedPixelFormats(self, type):
print("supportedPixelFormats() called")
print("supportedPixelFormats() finished")
return [QVideoFrame.Format_ARGB32, QVideoFrame.Format_ARGB32_Premultiplied,
QVideoFrame.Format_RGB32, QVideoFrame.Format_RGB24, QVideoFrame.Format_RGB565,
QVideoFrame.Format_RGB555, QVideoFrame.Format_ARGB8565_Premultiplied,
QVideoFrame.Format_BGRA32, QVideoFrame.Format_BGRA32_Premultiplied, QVideoFrame.Format_BGR32,
QVideoFrame.Format_BGR24, QVideoFrame.Format_BGR565, QVideoFrame.Format_BGR555,
QVideoFrame.Format_BGRA5658_Premultiplied, QVideoFrame.Format_AYUV444,
QVideoFrame.Format_AYUV444_Premultiplied, QVideoFrame.Format_YUV444,
QVideoFrame.Format_YUV420P, QVideoFrame.Format_YV12, QVideoFrame.Format_UYVY,
QVideoFrame.Format_YUYV, QVideoFrame.Format_NV12, QVideoFrame.Format_NV21,
QVideoFrame.Format_IMC1, QVideoFrame.Format_IMC2, QVideoFrame.Format_IMC3,
QVideoFrame.Format_IMC4, QVideoFrame.Format_Y8, QVideoFrame.Format_Y16,
QVideoFrame.Format_Jpeg, QVideoFrame.Format_CameraRaw, QVideoFrame.Format_AdobeDng]
# def isFormatSupported(self, format):
# print("isFormatSupported() called")
# def start(self, format):
# print("start() called")
def present(self, frame):
print('call present')
print(frame)
return True
# def stop(self):
# print("stop() called")
if __name__ == '__main__':
app = QApplication(sys.argv)
w = QWidget()
w.show()
w.setWindowTitle("Hello PyQt5")
the_camera = QCamera()
the_grabber = CameraFrameGrabber()
the_camera.setViewfinder(the_grabber)
the_camera.start()
sys.exit(app.exec_())
Both code run successfully.
present function called in PyQt5, not called in PySide2.
Is there any difference between the usage of PySide2 and PyQt5.
Who can tell me why it seem like this?Thanks.
Related
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import time
import sys
import numpy as np
class Mainthread(QThread):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.running = None
self.mutex = QMutex()
def run(self):
while self.running:
self.mutex.lock()
print ("test")
time.sleep(1)
self.mutex.unlock()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.mainthread = Mainthread(self)
self.mainthread.running = True
self.mainthread.start()
self.mainthread1 = Mainthread(self)
self.mainthread1.running = True
self.mainthread1.start()
app = QApplication(sys.argv)
mainwindow = MainWindow()
mainwindow.show()
app.exec_()
I have this code where I run two instances of the same MainThread class.
What I was expecting was that mainthread's message (which is "test") would print, then wait for a sec and then mainthread1's would be printed. Instead, it seems like both threads are running at the same time. Is there something I'm missing?
In your code, each thread creates its own separate mutex, so no relation is enforced between the two. Create a single mutex first, and pass it to the theads:
import time
import sys
from PyQt5.QtCore import QThread, QMutex
from PyQt5.QtWidgets import QMainWindow, QApplication
class Mainthread(QThread):
def __init__(self, mutex, parent):
super().__init__(parent)
self.parent = parent
self.running = None
self.mutex = mutex
def run(self):
while self.running:
self.mutex.lock()
print ("test")
time.sleep(1)
self.mutex.unlock()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
mutex = QMutex()
self.mainthread = Mainthread(mutex, self)
self.mainthread.running = True
self.mainthread.start()
self.mainthread1 = Mainthread(mutex, self)
self.mainthread1.running = True
self.mainthread1.start()
app = QApplication(sys.argv)
mainwindow = MainWindow()
mainwindow.show()
app.exec_()
Note: I don't have PyQt5 installed (and doing so on my architecture is tricky), but I tested this in PySide6 and as far as I know the behavior should be consistent.
when clicking a button in the PyQt5 Ui iam starting a mitmproxy. When the proxy is started, i try to change a listWidget with Data from the Proxy.
main.py
import sys
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
from MainWindow import Ui_MainWindow
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, *args, obj=None, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setupUi(self)
self.pushButton.clicked.connect(self.Start)
def Start(self):
x = threading.Thread(target=self.StartMITM)
x.start()
def StartMITM(self):
os.system("mitmweb -s mitmproxy.py -q --no-web-open-browser")
if __name__ == "__main__":
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
mitmproxy.py - this is the error part
from mitmproxy import http
from main import MainWindow
def response(flow):
MainWindow.listWidget.addItem(flow.request.pretty_url)
Can I connect to the Widgets from another File?
It has 2 independent processes: The GUI and the mitmproxy script. And communicating both processes does not imply importing modules since you would be creating another widget, also objects should not be accessed through classes (I recommend you check your basic python notes).
In this the solution is to use some Inter process communication (IPC), in this case you can use Qt Remote Objects (QtRO):
main.py
from functools import cached_property
from PyQt5 import QtCore, QtRemoteObjects, QtWidgets
class Bridge(QtCore.QObject):
messageChanged = QtCore.pyqtSignal(str)
#QtCore.pyqtSlot(str)
def add_message(self, message):
self.messageChanged.emit(message)
class MitmwebManager(QtCore.QObject):
logChanged = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super().__init__(parent)
# self.process.setProcessChannelMode(QtCore.QProcess.MergedChannels)
self.process.readyReadStandardOutput.connect(self.handle_log)
self.process.setProgram("mitmweb")
#cached_property
def process(self):
return QtCore.QProcess()
def start(self, arguments):
self.process.setArguments(arguments)
self.process.start()
def stop(self):
self.process.kill()
def handle_log(self):
data = self.process.readAllStandardOutput()
codec = QtCore.QTextCodec.codecForName("UTF-8")
message = codec.toUnicode(data)
self.logChanged.emit(message)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.button = QtWidgets.QPushButton("Start", checkable=True)
self.listwidget = QtWidgets.QListWidget()
self.logview = QtWidgets.QPlainTextEdit(readOnly=True)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QGridLayout(central_widget)
lay.addWidget(self.button, 0, 0, 1, 2)
lay.addWidget(self.listwidget, 1, 0)
lay.addWidget(self.logview, 1, 1)
self.register_node = QtRemoteObjects.QRemoteObjectRegistryHost(
QtCore.QUrl("local:registry")
)
self.source_node = QtRemoteObjects.QRemoteObjectHost(
QtCore.QUrl("local:replica"), QtCore.QUrl("local:registry")
)
self.source_node.enableRemoting(self.bridge, "bridge")
self.button.toggled.connect(self.handle_toggled)
self.mitmweb.logChanged.connect(self.handle_log_changed)
self.bridge.messageChanged.connect(self.handle_message_changed)
self.resize(640, 480)
#cached_property
def mitmweb(self):
return MitmwebManager()
#cached_property
def bridge(self):
return Bridge()
def handle_toggled(self, checked):
if checked:
self.mitmweb.start(["-s", "script.py", "--no-web-open-browser"])
self.button.setText("Stop")
else:
self.mitmweb.stop()
self.button.setText("Start")
def handle_log_changed(self, message):
self.logview.insertPlainText(message)
def closeEvent(self, event):
super().closeEvent(event)
self.mitmweb.stop()
def handle_message_changed(self, message):
item = QtWidgets.QListWidgetItem(message)
self.listwidget.addItem(item)
def main():
app = QtWidgets.QApplication([])
view = MainWindow()
view.show()
app.exec_()
if __name__ == "__main__":
main()
script.py
from mitmproxy import http
from PyQt5 import QtCore, QtRemoteObjects
class Bridge(QtCore.QObject):
def __init__(self, bridge, parent=None):
super().__init__(parent)
self._message = ""
self._bridge = bridge
self.bridge.initialized.connect(self.handle_initialized)
#property
def bridge(self):
return self._bridge
#property
def message(self):
return self._message
def send_message(self, message):
self._message = message
#QtCore.pyqtSlot()
def handle_initialized(self):
self.bridge.add_message(self._message)
QtCore.QTimer.singleShot(0, QtCore.QCoreApplication.quit)
def send_qt(message):
qt_app = QtCore.QCoreApplication([])
replica_node = QtRemoteObjects.QRemoteObjectNode(QtCore.QUrl("local:registry"))
replica_bridge = replica_node.acquireDynamic("bridge")
bridge = Bridge(replica_bridge)
bridge.send_message(message)
qt_app.exec_()
def response(flow):
send_qt(flow.request.pretty_url)
I'm having a hard time finding a way to reference class instances in a decorator function.
import json
import time
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from main_UI import Ui_ApplicationWindow
from slack import RTMClient
class WorkerThread(QThread):
finished = pyqtSignal(str)
def __init__(self):
QThread.__init__(self)
self.rtm_client = RTMClient(token="xoxp...")
def run(self):
self.rtm_client.start()
#RTMClient.run_on(event="message")
def say_hello(**payload):
data = payload['data']
if (len(data) != 0):
if "text" in data:
text = data['text']
self.finished.emit(str(text))
class ApplicationWindow(QMainWindow):
def __init__(self):
super(ApplicationWindow, self).__init__()
self.ui = Ui_ApplicationWindow()
self.ui.setupUi(self)
self.ui.pushButton.clicked.connect(self.start_rtm)
def start_rtm(self):
self.thread = WorkerThread()
self.thread.finished.connect(self.update)
self.thread.start()
#pyqtSlot(str)
def update(self, data):
self.ui.label.setText(data)
if __name__ == "__main__":
app = QApplication(sys.argv)
myWindow = ApplicationWindow()
myWindow.show()
app.exec_()
So in say_hello since it can't take self as an argument, I'm not able to use self.finished.emit(text) at the end of the function.
How can I reference a class instance/function using self in say_hello?
No, You can not. Instead of using the #RTMClient.run_on() decorator, use the RTMClient.on() function to register it.
import threading
import asyncio
from slack import RTMClient
from PyQt5 import QtCore, QtWidgets
class SlackClient(QtCore.QObject):
textChanged = QtCore.pyqtSignal(str)
def start(self):
RTMClient.on(event="message", callback=self.say_hello)
threading.Thread(target=self._start_loop, daemon=True).start()
def _start_loop(self):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
slack_token = "xoxb-...."
rtm_client = RTMClient(token=slack_token)
rtm_client.start()
def say_hello(self, **payload):
data = payload["data"]
if data:
if "text" in data:
text = data["text"]
self.textChanged.emit(text)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
client = SlackClient()
button = QtWidgets.QPushButton("Start")
textedit = QtWidgets.QPlainTextEdit()
button.clicked.connect(client.start)
client.textChanged.connect(textedit.appendPlainText)
w = QtWidgets.QWidget()
lay = QtWidgets.QVBoxLayout(w)
lay.addWidget(button)
lay.addWidget(textedit)
w.show()
sys.exit(app.exec_())
Update:
import sys
import threading
import asyncio
from slack import RTMClient
from PyQt5 import QtCore, QtWidgets
from main_UI import Ui_ApplicationWindow
class SlackClient(QtCore.QObject):
textChanged = QtCore.pyqtSignal(str)
def start(self):
RTMClient.on(event="message", callback=self.say_hello)
threading.Thread(target=self._start_loop, daemon=True).start()
def _start_loop(self):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
slack_token = "xoxb-...."
rtm_client = RTMClient(token=slack_token)
rtm_client.start()
def say_hello(self, **payload):
data = payload["data"]
if data:
if "text" in data:
text = data["text"]
self.textChanged.emit(text)
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super(ApplicationWindow, self).__init__()
self.ui = Ui_ApplicationWindow()
self.ui.setupUi(self)
self.client = SlackClient()
# connections
self.ui.pushButton.clicked.connect(self.client.start)
self.client.textChanged.connect(self.ui.label.setText)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
myWindow = ApplicationWindow()
myWindow.show()
sys.exit(app.exec_())
Actually you cannot self since that is a global variable, not a class one.
from slack import RTMClient
class WorkerThread(QThread):
finished = pyqtSignal(dict)
def __init__(self):
QThread.__init__(self)
self.rtm_client = RTMClient(token="xoxp-....")
def run(self):
self.rtm_client.start()
#RTMClient.run_on(event="message")
def say_hello(**payload):
data = payload['data']
if (len(data) != 0):
if "text" in data:
text = data['text']
WorkerThread.finished.emit(text) <--- using self impossible
I suggest that you make such variable private by appending two underscores at the beginning (__my_private_var)
I use QT to build my GUI, and decorator to record the log. When I use the decorator on a slot, the GUI will crush.
The code is:
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
def LogInfos(func):
def wrapper(*s, **gs):
print('log')
ret = func(*s, **gs)
return ret
return wrapper
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
layout = QHBoxLayout(self)
btn = QPushButton('test')
layout.addWidget(btn)
btn.clicked.connect(self.testSlot)
#LogInfos
def testSlot(self):
print('test slot')
#LogInfos
def testLog():
print('test log')
if __name__ == '__main__':
testLog()
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
I has tested that the decorator function is OK, and the GUI is ok after remove the decorator.
See #ekhumoro's explanation of why, but using the decorated method in the slot is causing your problem due to an incorrect signature.
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
def LogInfos(func):
def wrapper(*s, **gs):
print('log')
ret = func(*s, **gs)
return ret
return wrapper
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
layout = QHBoxLayout(self)
btn = QPushButton('test')
layout.addWidget(btn)
btn.clicked.connect(self.testSlot)
#LogInfos
def testSlot(self, checked=False):
print('test slot')
#LogInfos
def testLog():
print('test log')
if __name__ == '__main__':
testLog()
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
Try it: btn.clicked.connect(lambda: self.testSlot())
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
def LogInfos(func):
def wrapper(*s, **gs):
print('log')
ret = func(*s, **gs)
return ret
return wrapper
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
layout = QHBoxLayout(self)
btn = QPushButton('test')
layout.addWidget(btn)
#btn.clicked.connect(self.testSlot)
btn.clicked.connect(lambda: self.testSlot())
#LogInfos
def testSlot(self):
print('test slot')
#LogInfos
def testLog():
print('test log')
if __name__ == '__main__':
testLog()
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
I am using a QDateEdit widget with QDateEdit.setCalendarPopup(True). I am trying to connect a slot to the event when the calendar popup closes. See my example below for my attempts so far, found in MyCalendarWidget. None of my attempts so far have worked. What can I do to perform an action every time the calendar widget popup closes, not only when the date is changed?
from PyQt4 import QtGui, QtCore
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self, *args):
super(MainWindow,self).__init__(*args)
self._date = QtGui.QDateEdit()
self._date.setCalendarPopup(True)
self._date.setCalendarWidget(MyCalendarWidget())
self.setCentralWidget(self._date)
class App(QtGui.QApplication):
def __init__(self, *args):
super(App,self).__init__(*args)
self.main = MainWindow()
self.connect(self, QtCore.SIGNAL("lastWindowClosed()"), self.byebye )
self.main.show()
def byebye( self ):
self.exit(0)
class MyCalendarWidget(QtGui.QCalendarWidget):
def __init__(self, parent=None):
print("mycal initialized")
super(MyCalendarWidget, self).__init__(parent)
self.installEventFilter(self)
self._many = 2
self._many2 = 2
def focusInEvent(self, event):
print('-'*self._many + 'focus in')
if self._many == 2:
self._many = 4
else:
self._many = 2
super(MyCalendarWidget, self).focusInEvent(event)
def focusOutEvent(self, event):
print('-'*self._many2+'focus out')
if self._many2 == 2:
self._many2 = 4
else:
self._many2 = 2
super(MyCalendarWidget, self).focusOutEvent(event)
def closeEvent(self, event):
print('close')
super(MyCalendarWidget, self).closeEvent(event)
def mouseReleaseEvent(self, event):
print('mouse')
super(MyCalendarWidget, self).mouseReleaseEvent(event)
def main(args):
global app
app = App(args)
app.exec_()
if __name__ == "__main__":
main(sys.argv)
Figured it out - turns out I need to use the clicked signal in QCalendarWidget. This removes the need to sub-class QCalendarWidget as well.
from PyQt4 import QtGui, QtCore
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self, *args):
super(MainWindow,self).__init__(*args)
self._date = QtGui.QDateEdit()
self._date.setCalendarPopup(True)
calendar = self._date.calendarWidget()
calendar.clicked.connect(self._clicked)
self.setCentralWidget(self._date)
def _clicked(self, date):
print('clicked')
class App(QtGui.QApplication):
def __init__(self, *args):
super(App,self).__init__(*args)
self.main = MainWindow()
self.connect(self, QtCore.SIGNAL("lastWindowClosed()"), self.byebye )
self.main.show()
def byebye( self ):
self.exit(0)
def main(args):
global app
app = App(args)
app.exec_()
if __name__ == "__main__":
main(sys.argv)