I use PyQt5 and Python2.7.
I have four Classes. App, UIWidget, PlayStreaming and Thread.
App is parent of UIWidget.
UIWidget is parent of PlayStreaming.
PlayStreaming is parent of Thread.
I like to pass Statusbar message from Thread Class to App Class, so that I can update status.
I use self.parent().set_status_message('') from children classes subsequently to display message with self.statusBar().showMessage('') in App class.
But I have error in UIWidget as
AttributeError: 'QWidget' object has no attribute 'set_status_message'
Aborted (core dumped)
How can I update to Status bar in MainWindow from Child class?
My code is as follow.
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
import cv2
import time
import face_recognition.api as face_recognition
class Thread(QtCore.QThread):
changePixmap = QtCore.pyqtSignal(QtGui.QImage)
scaled_size = QtCore.QSize(640, 480)
curScale=1.0
def run(self):
cap = cv2.VideoCapture(-1)
cap.set(3,1280);
cap.set(4,1024);
time.sleep(2)
self.maxHeight=cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
self.maxScale=self.maxHeight/480.0
while True:
ret, frame = cap.read()
if ret:
r=1
face_locations=[]
rescaleSize=int(480*self.curScale)
if(frame.shape[0] > 480 and frame.shape[1] > 640):
r = rescaleSize / float(frame.shape[0])
dim = (int(frame.shape[1] * r), rescaleSize)
face_locations = face_recognition.face_locations(cv2.resize(frame, dim, fx=0.0, fy=0.0))
else:
face_locations = face_recognition.face_locations(frame)
for face_location in face_locations:
top, right, bottom, left = face_location
cv2.rectangle(frame,(int(right/r),int(top/r)),(int(left/r),int(bottom/r)),(0,255,0),2)
rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
convertToQtFormat = QtGui.QImage(rgbImage.data, rgbImage.shape[1], rgbImage.shape[0], QtGui.QImage.Format_RGB888)
p = convertToQtFormat.scaled(self.scaled_size, QtCore.Qt.KeepAspectRatio)
self.changePixmap.emit(p)
#QtCore.pyqtSlot(QtCore.QSize)
def scaled(self, scaled_size):
self.scaled_size = scaled_size
#QtCore.pyqtSlot()
def scaleup(self):
self.curScale = self.curScale + 0.1
if self.curScale > self.maxScale:
self.curScale = self.maxScale
self.parent().set_status_message('Cur scale:'+str(self.curScale))
#QtCore.pyqtSlot()
def scaledown(self):
self.curScale = self.curScale - 0.1
if self.curScale < 1.0:
self.curScale = 1.0
self.parent().set_status_message('Cur scale:'+str(self.curScale))
class PlayStreaming(QtWidgets.QLabel):
reSize = QtCore.pyqtSignal(QtCore.QSize)
scaleupSignal = QtCore.pyqtSignal()
scaledownSignal = QtCore.pyqtSignal()
def __init__(self):
super(PlayStreaming, self).__init__()
self.initUI()
#QtCore.pyqtSlot(QtGui.QImage)
def setImage(self, image):
self.label.setPixmap(QtGui.QPixmap.fromImage(image))
def initUI(self):
self.setWindowTitle("Image")
# create a label
self.label = QtWidgets.QLabel(self)
th = Thread(self)
th.changePixmap.connect(self.setImage)
self.scaleupSignal.connect(th.scaleup)
self.scaledownSignal.connect(th.scaledown)
self.reSize.connect(th.scaled)
th.start()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.label, alignment=QtCore.Qt.AlignCenter)
def resizeEvent(self, event):
self.reSize.emit(self.size())
def set_status_message(self, message):
return self.parent().set_status_message(message)
class UIWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(UIWidget, self).__init__(parent)
# Initialize tab screen
self.tabs = QtWidgets.QTabWidget()
self.tab1 = QtWidgets.QWidget()
self.tab2 = QtWidgets.QWidget()
self.tab3 = QtWidgets.QWidget()
# Add tabs
self.tabs.addTab(self.tab1, "Face")
self.tabs.addTab(self.tab2, "Human")
self.tabs.addTab(self.tab3, "Vehicle")
self.display = PlayStreaming()
# Create first tab
self.createGridLayout()
self.tab1.layout = QtWidgets.QVBoxLayout()
self.tab1.layout.addWidget(self.display, stretch=1)
self.tab1.layout.addWidget(self.horizontalGroupBox)
self.tab1.setLayout(self.tab1.layout)
# Add tabs to widget
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.tabs)
def createGridLayout(self):
self.horizontalGroupBox = QtWidgets.QGroupBox("")
self.horizontalGroupBox.setStyleSheet("QGroupBox{ background-color: red; border: none;}")
hlay1 = QtWidgets.QHBoxLayout()
self.TestButton=QtWidgets.QPushButton('Test')
hlay1.addWidget(self.TestButton)
self.RunButton=QtWidgets.QPushButton('Run')
hlay1.addWidget(self.RunButton)
self.ScaleUpButton=QtWidgets.QPushButton('ScaleUp')
self.ScaleUpButton.clicked.connect(self.display.scaleupSignal)
hlay1.addWidget(self.ScaleUpButton)
self.ScaleDownButton=QtWidgets.QPushButton('ScaleDown')
self.ScaleDownButton.clicked.connect(self.display.scaledownSignal)
hlay1.addWidget(self.ScaleDownButton)
hlay2 = QtWidgets.QHBoxLayout()
hlay2.addWidget(QtWidgets.QPushButton('Set Faces'))
hlay2.addWidget(QtWidgets.QPushButton('FacePose'))
hlay2.addWidget(QtWidgets.QPushButton('Gender'))
hlay2.addWidget(QtWidgets.QPushButton('Age'))
hlay2.addWidget(QtWidgets.QPushButton('Recognize'))
layout = QtWidgets.QVBoxLayout()
layout.addLayout(hlay1)
layout.addLayout(hlay2)
self.horizontalGroupBox.setLayout(layout)
def set_status_message(self, message):
return self.statusBar().set_status_message(message)
class App(QMainWindow):
def __init__(self):
super(App,self).__init__()
self.title = 'FaceHumanVehicle'
self.left = 10
self.top = 10
self.width = 1000
self.height = 800
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.form_widget = UIWidget(self)
self.statusBar().showMessage('')
self.setCentralWidget(self.form_widget)
self.show()
def set_status_message(self, message):
return self.statusBar().showMessage(message)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
Whenever you add a widget to a layout, it will be automatically re-parented to the widget that contains the layout. So this code:
self.display = PlayStreaming()
# Create first tab
self.createGridLayout()
self.tab1.layout = QtWidgets.QVBoxLayout()
self.tab1.layout.addWidget(self.display, stretch=1)
will mean that self.display.parent() returns self.tab1, which obviously does not have a set_status_message method (since it's just a plain QWidget).
However, it's bad practice to try to directly access gui methods from a thread, so a better solution is to emit a custom signal that sends the status-bar message:
class Thread(QtCore.QThread):
statusMessage = QtCore.pyqtSignal(str)
...
def scaleup(self):
...
self.statusMessage.emit('Cur scale:'+str(self.curScale))
You can then connect this up within your PlayStreaming class and use its window() method to access the top-level main-window:
class PlayStreaming(QtWidgets.QLabel):
...
def initUI(self):
...
th = Thread(self)
th.statusMessage.connect(self.handle_status_message)
def handle_status_message(self, message):
self.window().set_status_message(message)
Related
I have implemented QUndoStack with QGraphicScene and everything is working like charm with individual QGraphicItem movement/rotation, but when it comes to the multi-items movement, I couldn't find any way to do so, since I didn't find any flag/signal that will be emitted after all selected item position has been changed [ NOT DURING].
Here is a sketch code that will represent my goals.
import sys
from typing import Dict
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtGui import QPen, QColor, QBrush
from PyQt5.QtWidgets import QApplication, QGraphicsItem, QMainWindow, QGraphicsScene, QGraphicsView, \
QGraphicsEllipseItem, QUndoStack, QUndoCommand
import random
class CustomUndoRedoStack(QUndoStack):
instance = None
def __init__(self):
if not CustomUndoRedoStack.instance:
CustomUndoRedoStack.instance = self
super(CustomUndoRedoStack, self).__init__()
#staticmethod
def get_instance():
return CustomUndoRedoStack.instance
class MovingMultiItemsCommand(QUndoCommand):
def __init__(self, new_items_pos: Dict[QGraphicsItem], old_items_pos: Dict[QGraphicsItem]):
super(MovingMultiItemsCommand, self).__init__()
self.new_items_pos = new_items_pos
self.old_items_pos = old_items_pos
CustomUndoRedoStack.get_instance().push(self)
def undo(self) -> None:
for item in self.old_items_pos:
item.setPos(self.old_items_pos[item])
super(MovingMultiItemsCommand, self).undo()
def redo(self) -> None:
for item in self.new_items_pos:
item.setPos(self.new_items_pos[item])
super(MovingMultiItemsCommand, self).redo()
class CustomQGraphicsEllipseItem(QGraphicsEllipseItem):
def __init__(self, x, y):
super(CustomQGraphicsEllipseItem, self).__init__(x, y, 20, 20)
self.setFlag(QGraphicsItem.ItemIsSelectable)
self.setFlag(QGraphicsItem.ItemIsMovable)
self.setPen(QPen(QColor(255, 128, 0), 0.5, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
self.setBrush(QBrush(QColor(255, 128, 20, 128)))
class MyGraphicsView(QGraphicsView):
def __init__(self):
super(MyGraphicsView, self).__init__()
self.setDragMode(QGraphicsView.RubberBandDrag)
self._isPanning = False
self._mousePressed = False
self.setCacheMode(QGraphicsView.CacheBackground)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
class MyGraphicsScene(QGraphicsScene):
def __init__(self):
super(MyGraphicsScene, self).__init__()
self.setBackgroundBrush(QBrush(QColor(50, 50, 50)))
self.old_items_pos = {}
self.new_items_pos = {}
# ------- I want something like the following -----------------------------
def selected_items_before_change(self):
for item in self.selectedItems():
self.old_items_pos[item] = item.pos()
def selected_items_after_change(self):
for item in self.selectedItems():
self.new_items_pos[item] = item.pos()
def selected_item_position_changed(self):
"""
This method must be triggered only and only once after selected items position has been changed (NOT WHILE)
"""
MovingMultiItemsCommand(self.new_items_pos, self.old_items_pos)
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
class MyMainWindow(QMainWindow):
def __init__(self):
super(MyMainWindow, self).__init__()
self.setWindowTitle("Test")
self.resize(800, 600)
self.gv = MyGraphicsView()
self.gv.setScene(MyGraphicsScene())
self.setCentralWidget(self.gv)
self.populate()
def populate(self):
scene = self.gv.scene()
for i in range(500):
x = random.randint(0, 1000)
y = random.randint(0, 1000)
rect = CustomQGraphicsEllipseItem(x, y)
scene.addItem(rect)
def main():
app = QApplication(sys.argv)
ex = MyMainWindow()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
C++ answers will be good as well, but Python is preferable :)
The mouse movement of selectable items is handled by the graphics scene, starting with its mousePressEvent() and ending with the corresponding mouseReleaseEvent().
This means that you can create a QUndoCommand based on the selected items (if any) after the default implementation of mouse button press/release is called. This will be valid for any movement, including when just one item has been moved.
class ItemMovedUndoCommand(QUndoCommand):
def __init__(self, oldPositions, newPositions):
if len(oldPositions) == 1:
pos = oldPositions.values()[0]
text = 'Item moved to {}x{}'.format(
pos.x(), pos.y())
else:
text = '{} items moved'.format(len(oldPositions))
super().__init__(text)
self.oldPositions = oldPositions
self.newPositions = newPositions
def redo(self):
for item, pos in self.newPositions.items():
item.setPos(pos)
def undo(self):
for item, pos in self.oldPositions.items():
item.setPos(pos)
class CustomQGraphicsEllipseItem(QGraphicsEllipseItem):
def __init__(self, x, y):
super(CustomQGraphicsEllipseItem, self).__init__(-10, -10, 20, 20)
self.setPos(x, y)
self.setFlag(QGraphicsItem.ItemIsSelectable)
self.setFlag(QGraphicsItem.ItemIsMovable)
self.setPen(QPen(QColor(255, 128, 0),
0.5, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
self.setBrush(QBrush(QColor(255, 128, 20, 128)))
class MyGraphicsView(QGraphicsView):
def __init__(self):
super(MyGraphicsView, self).__init__()
self.setDragMode(QGraphicsView.RubberBandDrag)
self._isPanning = False
self._mousePressed = False
self.setCacheMode(QGraphicsView.CacheBackground)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
class MyGraphicsScene(QGraphicsScene):
itemsMoved = pyqtSignal(object, object)
def __init__(self):
super(MyGraphicsScene, self).__init__()
self.setBackgroundBrush(QBrush(QColor(50, 50, 50)))
self.oldPositions = {}
def mousePressEvent(self, event):
super().mousePressEvent(event)
if event.button() == Qt.LeftButton:
self.oldPositions = {i:i.pos() for i in self.selectedItems()}
def mouseReleaseEvent(self, event):
super().mouseReleaseEvent(event)
if event.button() == Qt.LeftButton and self.oldPositions:
self.itemsMoved.emit(self.oldPositions,
{i:i.pos() for i in self.oldPositions.keys()})
self.oldPositions = {}
class MyMainWindow(QMainWindow):
def __init__(self):
super(MyMainWindow, self).__init__()
self.setWindowTitle("Test")
self.resize(800, 600)
self.gv = MyGraphicsView()
self.scene = MyGraphicsScene()
self.gv.setScene(self.scene)
self.setCentralWidget(self.gv)
toolBar = QToolBar()
self.addToolBar(Qt.TopToolBarArea, toolBar)
self.undoStack = QUndoStack()
toolBar.addAction(self.undoStack.createUndoAction(self.scene))
toolBar.addAction(self.undoStack.createRedoAction(self.scene))
self.populate()
self.scene.itemsMoved.connect(self.itemsMoved)
def itemsMoved(self, oldPositions, newPositions):
self.undoStack.push(ItemMovedUndoCommand(oldPositions, newPositions))
def populate(self):
scene = self.gv.scene()
for i in range(500):
x = random.randint(0, 1000)
y = random.randint(0, 1000)
rect = CustomQGraphicsEllipseItem(x, y)
scene.addItem(rect)
Here is my sample code. When I click the index item in list view, I am getting the selection item,it's working fine.But I want to get the selected item using up and down arrows. Can anyone please help me. Thank you in advance.
Given below is my code:
import sys
from PyQt4 import QtCore,QtGui
class mtable(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.auto_search1 = QtGui.QWidget()
self.auto_search_vbox1 = QtGui.QVBoxLayout(self.auto_search1)
self.auto_search_vbox1.setAlignment(QtCore.Qt.AlignLeft)
hbox1=QtGui.QHBoxLayout()
self.le_search1 = QtGui.QLineEdit()
self.se_btn1 = QtGui.QPushButton("Search")
self.searchBtn = QtGui.QPushButton("Close")
self.searchBtn.clicked.connect(self.auto_search1.close)
self.se_btn1.clicked.connect(self.filterClicked1)
hbox1.addWidget(self.le_search1)
hbox1.addWidget(self.se_btn1)
hbox1.addWidget(self.searchBtn)
self.auto_search_vbox1.addLayout(hbox1)
self.total_list1 =[]
self.list1 = QtGui.QListView()
self.list1.clicked.connect(self.on_treeView_clicked)
self.model1 = QtGui.QStandardItemModel(self.list1)
self.y =['one','two', 'three']
for i in self.y:
self.total_list1.append(i)
for code in self.total_list1:
item1 = QtGui.QStandardItem(code)
self.model1.appendRow(item1)
self.list1.setModel(self.model1)
self.auto_search_vbox1.addWidget(self.list1)
self.auto_search1.show()
self.auto_search1.resize(1000,500)
#QtCore.pyqtSlot(QtCore.QModelIndex)
def on_treeView_clicked(self, index):
itms = self.list1.selectedIndexes()
for data in itms:
print index.data().toString()
self.le_search1.setText(index.data().toString())
self.filterClicked1()
def filterClicked1(self):
print "searching logic"
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
tb = mtable()
sys.exit(app.exec_())
Here I need to select the green highlighted item using arrow keys without clicking the item
You have to use the currentChanged signal of the selectionModel() of the QListView:
import sys
from PyQt4 import QtCore,QtGui
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.lineedit = QtGui.QLineEdit()
self.search_button = QtGui.QPushButton("Search")
self.close_button = QtGui.QPushButton("Close")
self.listview = QtGui.QListView()
model = QtGui.QStandardItemModel(self.listview)
for e in ('one', 'two', 'three'):
model.appendRow(QtGui.QStandardItem(e))
self.listview.setModel(model)
# signals connections
self.listview.selectionModel().currentChanged.connect(self.on_currentChanged)
QtCore.QTimer.singleShot(0, self.selectFirstItem)
# layout
central_widget = QtGui.QWidget()
self.setCentralWidget(central_widget)
vlay = QtGui.QVBoxLayout(central_widget)
hlay = QtGui.QHBoxLayout()
hlay.addWidget(self.lineedit)
hlay.addWidget(self.search_button)
hlay.addWidget(self.close_button)
vlay.addLayout(hlay)
vlay.addWidget(self.listview)
self.resize(640, 480)
def selectFirstItem(self):
self.listview.setFocus()
ix = self.listview.model().index(0, 0)
self.listview.selectionModel().setCurrentIndex(ix, QtGui.QItemSelectionModel.Select)
#QtCore.pyqtSlot(QtCore.QModelIndex)
def on_currentChanged(self, current):
self.lineedit.setText(current.data())
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I use PyQt5 and Python2.7
I have UIWidget class, PlayStreaming class and Thread class.
Once a button from UIWidget is pressed and then dictionary object from PlayStreaming is sent to Thread class.
If I remove 'QVariantMap', I can receive Button click signal, but I can't send data.
How can I solve the problem?
My whole code is as follow.
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow, QInputDialog
import cv2
import time
import face_recognition.api as face_recognition
class Thread(QtCore.QThread):
changePixmap = QtCore.pyqtSignal(QtGui.QImage)
updateStatus = QtCore.pyqtSignal(str)
scaled_size = QtCore.QSize(640, 480)
curScale=1.0
facearray=[]
dim=(640,480)
processedImage=[]
def run(self):
cap = cv2.VideoCapture(-1)
cap.set(3,1280);
cap.set(4,1024);
time.sleep(2)
self.maxHeight=cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
self.maxScale=self.maxHeight/480.0
while True:
ret, frame = cap.read()
if ret:
r=1
rescaleSize=int(480*self.curScale)
if(frame.shape[0] > 480 and frame.shape[1] > 640):
r = rescaleSize / float(frame.shape[0])
self.dim = (int(frame.shape[1] * r), rescaleSize)
processedImage=cv2.resize(frame, self.dim, fx=0.0, fy=0.0)
face_locations = face_recognition.face_locations(processedImage)
if(len(face_locations) > 0):
encodefaces(facelocs)
else:
processedImage=frame.copy()
face_locations = face_recognition.face_locations(processedImage)
if(len(face_locations) > 0):
encodefaces(facelocs)
for face_location in face_locations:
top, right, bottom, left = face_location
cv2.rectangle(frame,(int(right/r),int(top/r)),(int(left/r),int(bottom/r)),(0,255,0),2)
rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
convertToQtFormat = QtGui.QImage(rgbImage.data, rgbImage.shape[1], rgbImage.shape[0], QtGui.QImage.Format_RGB888)
p = convertToQtFormat.scaled(self.scaled_size, QtCore.Qt.KeepAspectRatio)
self.changePixmap.emit(p)
#QtCore.pyqtSlot(QtCore.QSize)
def scaled(self, scaled_size):
self.scaled_size = scaled_size
#QtCore.pyqtSlot()
def scaleup(self):
self.curScale = self.curScale + 0.1
if self.curScale > self.maxScale:
self.curScale = self.maxScale
self.updateStatus.emit('Cur scale:'+str(self.dim))
#QtCore.pyqtSlot()
def scaledown(self):
self.curScale = self.curScale - 0.1
if self.curScale < 1.0:
self.curScale = 1.0
self.updateStatus.emit('Cur scale:'+str(self.dim))
#QtCore.pyqtSlot('QVariantMap')
def getfacestorecognize(self, clickedInfos):
facearray.append(clickedInfos)
print(clickedInfos['x']+' '+clickedInfos['y']+' '+clickedInfos['name'])
def encodefaces(self, facelocs):
if(len(self.facearray) > 0):
for face in facearray:
r=(self.scaled_size[0]/self.dim[0])
x=int(face['x'])*r
y=int(face['y'])*r
#for loc in facelocs:
class PlayStreaming(QtWidgets.QLabel):
reSize = QtCore.pyqtSignal(QtCore.QSize)
scaleupSignal = QtCore.pyqtSignal()
scaledownSignal = QtCore.pyqtSignal()
transferFaceInfosSignal = QtCore.pyqtSignal()#'QVariantMap'
def __init__(self):
super(PlayStreaming, self).__init__()
self.initUI()
self.mousePressEvent = self.showDialog
#QtCore.pyqtSlot(QtGui.QImage)
def setImage(self, image):
self.label.setPixmap(QtGui.QPixmap.fromImage(image))
def initUI(self):
# create a label
self.label = QtWidgets.QLabel(self)
th = Thread(self)
th.changePixmap.connect(self.setImage)
th.updateStatus.connect(self.handle_status_message)
self.scaleupSignal.connect(th.scaleup)
self.scaledownSignal.connect(th.scaledown)
self.transferFaceInfosSignal.connect(th.getfacestorecognize)
self.reSize.connect(th.scaled)
th.start()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.label, alignment=QtCore.Qt.AlignCenter)
def resizeEvent(self, event):
self.reSize.emit(self.size())
def showDialog(self, event):
x = event.pos().x()
y = event.pos().y()
facedata={"x": str(x), "y": str(y), "name": ''}
text, ok = QInputDialog.getText(self, 'Name input dialog',
'Enter name:')
if (ok and str(text)!=''):
facedata['name']=str(text)
self.transferFaceInfosSignal.emit(facedata)
def handle_status_message(self, message):
self.window().set_status_message(message)
class UIWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(UIWidget, self).__init__(parent)
# Initialize tab screen
self.tabs = QtWidgets.QTabWidget()
self.tab1 = QtWidgets.QWidget()
self.tab2 = QtWidgets.QWidget()
self.tab3 = QtWidgets.QWidget()
# Add tabs
self.tabs.addTab(self.tab1, "Face")
self.tabs.addTab(self.tab2, "Human")
self.tabs.addTab(self.tab3, "Vehicle")
self.display = PlayStreaming()
# Create first tab
self.createGridLayout()
self.tab1.layout = QtWidgets.QVBoxLayout()
self.tab1.layout.addWidget(self.display, stretch=1)
self.tab1.layout.addWidget(self.horizontalGroupBox)
self.tab1.setLayout(self.tab1.layout)
# Add tabs to widget
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.tabs)
def createGridLayout(self):
self.horizontalGroupBox = QtWidgets.QGroupBox("")
self.horizontalGroupBox.setStyleSheet("QGroupBox{ background-color: red; border: none;}")
hlay1 = QtWidgets.QHBoxLayout()
self.TestButton=QtWidgets.QPushButton('Test')
hlay1.addWidget(self.TestButton)
self.RunButton=QtWidgets.QPushButton('Run')
hlay1.addWidget(self.RunButton)
self.ScaleUpButton=QtWidgets.QPushButton('ScaleUp')
self.ScaleUpButton.clicked.connect(self.display.scaleupSignal)
hlay1.addWidget(self.ScaleUpButton)
self.ScaleDownButton=QtWidgets.QPushButton('ScaleDown')
self.ScaleDownButton.clicked.connect(self.display.scaledownSignal)
hlay1.addWidget(self.ScaleDownButton)
hlay1.addWidget(QtWidgets.QPushButton('Reset'))
hlay2 = QtWidgets.QHBoxLayout()
hlay2.addWidget(QtWidgets.QPushButton('Set Faces'))
hlay2.addWidget(QtWidgets.QPushButton('FacePose'))
hlay2.addWidget(QtWidgets.QPushButton('Gender'))
hlay2.addWidget(QtWidgets.QPushButton('Age'))
self.RecognizeButton=QtWidgets.QPushButton('Recognize')
self.RecognizeButton.clicked.connect(self.display.transferFaceInfosSignal)
hlay2.addWidget(self.RecognizeButton)
layout = QtWidgets.QVBoxLayout()
layout.addLayout(hlay1)
layout.addLayout(hlay2)
self.horizontalGroupBox.setLayout(layout)
class App(QMainWindow):
def __init__(self):
super(App,self).__init__()
self.title = 'FaceHumanVehicle'
self.left = 10
self.top = 10
self.width = 1000
self.height = 800
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.form_widget = UIWidget(self)
self.statusBar().showMessage('')
self.setCentralWidget(self.form_widget)
self.show()
def set_status_message(self, message):
return self.statusBar().showMessage(message)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
From what I understand you want to send the data when you press the button, so the slot connecting to the button should emit the signal, not connect to the signal. For this the data that I see is filled in mousePressEvent must be a member of the class, in this case the logic is to save the position when the image is pressed, and when the button is pressed a dialogue will be opened, a name will be established and the data will be sent.
class PlayStreaming(QtWidgets.QLabel):
reSize = QtCore.pyqtSignal(QtCore.QSize)
scaleupSignal = QtCore.pyqtSignal()
scaledownSignal = QtCore.pyqtSignal()
transferFaceInfosSignal = QtCore.pyqtSignal('QVariantMap') # <--- +++
def __init__(self):
super(PlayStreaming, self).__init__()
self.facedata = {"x": "", "y": "", "name": ""} # <--- +++
self.initUI()
# self.mousePressEvent = self.showDialog <--- ---
# ...
def mousePressEvent(self, event):
self.facedata["x"] = str(event.pos().x())
self.facedata["y"] = str(event.pos().y())
self.showDialog()
super(PlayStreaming, self).mousePressEvent(event)
def showDialog(self):
text, ok = QtWidgets.QInputDialog.getText(self, 'Name input dialog', 'Enter name:')
if ok and text:
self.facedata['name']= text
#QtCore.pyqtSlot()
def send_signal(self)
if self.facedata["name"]:
self.transferFaceInfosSignal.emit(self.facedata)
class UIWidget(QtWidgets.QWidget):
# ...
def createGridLayout(self):
# ...
self.RecognizeButton.clicked.connect(self.display.send_signal)
# ...
On the other hand the signals and slot support all native python types like dict, so you can replace 'QVariantMap' with dict.
I use PyQt5 in Python2.7. I have three Classes. Thread, PlayStreaming and UIWidget.
class Thread(QtCore.QThread):
changePixmap = QtCore.pyqtSignal(QtGui.QImage)
scaled_size = QtCore.QSize(640, 480)
curScale=1.0
maxHeight=0
maxScale=0
def run(self):
cap = cv2.VideoCapture(1)
cap.set(3,1280);
cap.set(4,1024);
time.sleep(2)
maxHeight=cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
maxScale=maxHeight/480.0
while True:
ret, frame = cap.read()
if ret:
r=1
face_locations=[]
rescaleSize=int(480*curScale)
if(frame.shape[0] > 480 and frame.shape[1] > 640):
r = rescaleSize / float(frame.shape[0])
dim = (int(frame.shape[1] * r), rescaleSize)
face_locations = face_recognition.face_locations(cv2.resize(frame, dim, fx=0.0, fy=0.0))
else:
face_locations = face_recognition.face_locations(frame)
for face_location in face_locations:
top, right, bottom, left = face_location
cv2.rectangle(frame,(int(right/r),int(top/r)),(int(left/r),int(bottom/r)),(0,255,0),2)
rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
convertToQtFormat = QtGui.QImage(rgbImage.data, rgbImage.shape[1], rgbImage.shape[0], QtGui.QImage.Format_RGB888)
p = convertToQtFormat.scaled(self.scaled_size, QtCore.Qt.KeepAspectRatio)
self.changePixmap.emit(p)
def scaled(self, scaled_size):
self.scaled_size = scaled_size
def scaleup(self):
self.curScale = self.curScale + 0.1
if( self.curScale > maxScale):
self.curScale=maxScale
print(self.curScale)
class PlayStreaming(QtWidgets.QLabel):
reSize = QtCore.pyqtSignal(QtCore.QSize)
def __init__(self):
super(PlayStreaming, self).__init__()
self.initUI()
#QtCore.pyqtSlot(QtGui.QImage)
def setImage(self, image):
self.label.setPixmap(QtGui.QPixmap.fromImage(image))
def initUI(self):
self.setWindowTitle("Image")
# create a label
self.label = QtWidgets.QLabel(self)
##############instance of Thread############
th = Thread(self)
############################################
th.changePixmap.connect(self.setImage)
self.reSize.connect(th.scaled)
th.start()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.label, alignment=QtCore.Qt.AlignCenter)
def resizeEvent(self, event):
self.reSize.emit(self.size())
class UIWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(UIWidget, self).__init__(parent)
# Initialize tab screen
self.tabs = QtWidgets.QTabWidget()
self.tab1 = QtWidgets.QWidget()
self.tab2 = QtWidgets.QWidget()
self.tab3 = QtWidgets.QWidget()
# Add tabs
self.tabs.addTab(self.tab1, "Face")
self.tabs.addTab(self.tab2, "Human")
self.tabs.addTab(self.tab3, "Vehicle")
# Create first tab
self.createGridLayout()
self.tab1.layout = QtWidgets.QVBoxLayout()
##############instance of PlayStreaming############
self.display = PlayStreaming()
###################################################
self.tab1.layout.addWidget(self.display, stretch=1)
self.tab1.layout.addWidget(self.horizontalGroupBox)
self.tab1.setLayout(self.tab1.layout)
# Add tabs to widget
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.tabs)
def createGridLayout(self):
self.horizontalGroupBox = QtWidgets.QGroupBox("")
self.horizontalGroupBox.setStyleSheet("QGroupBox{ background-color: red; border: none;}")
hlay1 = QtWidgets.QHBoxLayout()
self.TestButton=QtWidgets.QPushButton('Test')
hlay1.addWidget(self.TestButton)
self.RunButton=QtWidgets.QPushButton('Run')
hlay1.addWidget(self.RunButton)
self.ScaleUpButton=QtWidgets.QPushButton('ScaleUp')
###################This button clicked signal to Thread class#########
#############How to send??????????????????????########################
self.ScaleUpButton.clicked.connect(scaleup)
hlay1.addWidget(self.ScaleUpButton)
self.ScaleDownButton=QtWidgets.QPushButton('ScaleDown')
hlay1.addWidget(self.ScaleDownButton)
hlay2 = QtWidgets.QHBoxLayout()
hlay2.addWidget(QtWidgets.QPushButton('Set Faces'))
hlay2.addWidget(QtWidgets.QPushButton('FacePose'))
hlay2.addWidget(QtWidgets.QPushButton('Gender'))
hlay2.addWidget(QtWidgets.QPushButton('Age'))
hlay2.addWidget(QtWidgets.QPushButton('Recognize'))
layout = QtWidgets.QVBoxLayout()
layout.addLayout(hlay1)
layout.addLayout(hlay2)
self.horizontalGroupBox.setLayout(layout)
PlayStreaming has instance of Thread and UIWidget has instance of PlayStreaming ( Not sure I am using the right terminology for Python as instance)
I like to send self.ScaleUpButton's click signal from UIWidget to scaleup slot in Thread class.
How can I do that?
You have to use the signals and the slots, the idea is that each widget is like a black box so PlayStreaming must have a signal that must call a slot of the thread:
class Thread(QtCore.QThread):
changePixmap = QtCore.pyqtSignal(QtGui.QImage)
def __init__(self, parent=None):
super(Thread, self).__init__(parent)
self.scaled_size = QtCore.QSize(640, 480)
self.curScale=1.0
self.maxScale = 0
def run(self):
cap = cv2.VideoCapture(1)
cap.set(3, 1280);
cap.set(4, 1024);
QtCore.QThread.sleep(2)
maxHeight = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
self.maxScale = maxHeight/480.0
while True:
ret, frame = cap.read()
if ret:
r=1
face_locations=[]
rescaleSize=int(480*self.curScale)
if(frame.shape[0] > 480 and frame.shape[1] > 640):
r = rescaleSize / float(frame.shape[0])
dim = (int(frame.shape[1] * r), rescaleSize)
face_locations = face_recognition.face_locations(cv2.resize(frame, dim, fx=0.0, fy=0.0))
else:
face_locations = face_recognition.face_locations(frame)
for face_location in face_locations:
top, right, bottom, left = face_location
cv2.rectangle(frame,(int(right/r),int(top/r)),(int(left/r),int(bottom/r)),(0,255,0),2)
rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
convertToQtFormat = QtGui.QImage(rgbImage.data, rgbImage.shape[1], rgbImage.shape[0], QtGui.QImage.Format_RGB888)
p = convertToQtFormat.scaled(self.scaled_size, QtCore.Qt.KeepAspectRatio)
self.changePixmap.emit(p)
#QtCore.pyqtSlot(QtCore.QSize)
def scaled(self, scaled_size):
self.scaled_size = scaled_size
#QtCore.pyqtSlot()
def scaleup(self):
self.curScale = self.curScale + 0.1
if self.curScale > self.maxScale:
self.curScale = self.maxScale
print(self.curScale)
class PlayStreaming(QtWidgets.QLabel):
reSize = QtCore.pyqtSignal(QtCore.QSize)
scaleupSignal = QtCore.pyqtSignal()
def __init__(self):
super(PlayStreaming, self).__init__()
self.initUI()
#QtCore.pyqtSlot(QtGui.QImage)
def setImage(self, image):
self.label.setPixmap(QtGui.QPixmap.fromImage(image))
def initUI(self):
self.setWindowTitle("Image")
self.label = QtWidgets.QLabel(self)
th = Thread(self)
th.changePixmap.connect(self.setImage)
self.scaleupSignal.connect(th.scaleup)
self.reSize.connect(th.scaled)
th.start()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.label, alignment=QtCore.Qt.AlignCenter)
def resizeEvent(self, event):
self.reSize.emit(self.size())
class UIWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(UIWidget, self).__init__(parent)
# Initialize tab screen
self.tabs = QtWidgets.QTabWidget()
self.tab1 = QtWidgets.QWidget()
self.tab2 = QtWidgets.QWidget()
self.tab3 = QtWidgets.QWidget()
# Add tabs
self.tabs.addTab(self.tab1, "Face")
self.tabs.addTab(self.tab2, "Human")
self.tabs.addTab(self.tab3, "Vehicle")
self.display = PlayStreaming()
# Create first tab
self.createGridLayout()
self.tab1.layout = QtWidgets.QVBoxLayout()
self.tab1.layout.addWidget(self.display, stretch=1)
self.tab1.layout.addWidget(self.horizontalGroupBox)
self.tab1.setLayout(self.tab1.layout)
# Add tabs to widget
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.tabs)
def createGridLayout(self):
self.horizontalGroupBox = QtWidgets.QGroupBox("")
self.horizontalGroupBox.setStyleSheet("QGroupBox{ background-color: red; border: none;}")
hlay1 = QtWidgets.QHBoxLayout()
self.TestButton=QtWidgets.QPushButton('Test')
hlay1.addWidget(self.TestButton)
self.RunButton=QtWidgets.QPushButton('Run')
hlay1.addWidget(self.RunButton)
self.ScaleUpButton=QtWidgets.QPushButton('ScaleUp')
self.ScaleUpButton.clicked.connect(self.display.scaleupSignal)
hlay1.addWidget(self.ScaleUpButton)
self.ScaleDownButton=QtWidgets.QPushButton('ScaleDown')
hlay1.addWidget(self.ScaleDownButton)
hlay2 = QtWidgets.QHBoxLayout()
hlay2.addWidget(QtWidgets.QPushButton('Set Faces'))
hlay2.addWidget(QtWidgets.QPushButton('FacePose'))
hlay2.addWidget(QtWidgets.QPushButton('Gender'))
hlay2.addWidget(QtWidgets.QPushButton('Age'))
hlay2.addWidget(QtWidgets.QPushButton('Recognize'))
layout = QtWidgets.QVBoxLayout()
layout.addLayout(hlay1)
layout.addLayout(hlay2)
self.horizontalGroupBox.setLayout(layout)
I want to set value into sheet using thread.
import time
import wx
from wx.lib import sheet
from threading import Thread
EVT_RESULT_ID = wx.NewId()
def EVT_RESULT(win, func):
win.Connect(-1, -1, EVT_RESULT_ID, func)
class ResultEvent(wx.PyEvent):
def __init__(self, data):
wx.PyEvent.__init__(self)
self.SetEventType(EVT_RESULT_ID)
self.data = data
class TestThread(Thread):
def __init__(self, wxObject,sheet):
Thread.__init__(self)
self.wxObject = wxObject
self.sheet=sheet
self.start()
def run(self):
self.sheet.sheetload()
wx.PostEvent(self.wxObject, ResultEvent(self.sheet))
class MySheet(sheet.CSheet):
def __init__(self, parent):
sheet.CSheet.__init__(self, parent)
self.SetNumberRows(100)
self.SetNumberCols(30)
def sheetload(self):
self.SetNumberRows(200)
self.SetNumberCols(30)
self.EnableEditing(False)
for i in range(200):
for j in range(30):
self.SetCellValue(i,j,str(i))
class Newt(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None,-1, "Tutorial")
self.box = wx.BoxSizer(wx.VERTICAL)
toolbar2 = wx.ToolBar(self, wx.TB_HORIZONTAL | wx.TB_TEXT,size = (1000, 40))
self.show_btn =wx.Button(toolbar2, label="show")
self.show_btn.Enable(True)
self.Bind(wx.EVT_BUTTON, self.onShow,self.show_btn)
toolbar2.AddControl(self.show_btn)
self.box.Add((5,5) , 0)
self.box.Add(toolbar2)
self.box.Add((5,10) , 0)
toolbar2.Realize()
self.notebook = wx.Notebook(self, -1, style=wx.RIGHT)
self.box.Add(self.notebook, 1, wx.EXPAND)
self.Maximize(True)
self.SetSizer(self.box)
EVT_RESULT(self, self.updateDisplay)
def onShow(self, event):
sheet=MySheet(self.notebook)
TestThread(self,sheet)
def updateDisplay(self, msg):
t = msg.data
self.notebook.AddPage(t,"Logs")
app = wx.PySimpleApp()
frame = Newt().Show()
app.MainLoop()
Here I am using sheetload function defined in Mysheet to execute in TestThread. So i can set value into sheet in background without blocking main gui.
But i am getting this error, and my gui is crashing.
(python2.7:10775): GLib-CRITICAL **: Source ID 559 was not found when attempting to remove it
can you help me, what is wrong with this code.