I'm trying to get an image to fit my label entirely without using setScaledContents(True) since I'd like my ImageGrab to have the exact same dimensions as the QLabel space.
I use a PIL ImageGrab with a bbox. if I chance the parameters to a wider width and higher height it will crash the program without any errors. I have attached a picture where the Image in the Label is localed left center. I'd like it to the top left. So that it can expand down and to the right if I increased the size.
But I am curious as to why I am unable to increase the size of the bbox. (0, 0, 400, 220) works fine but (0, 0, 420, 220) will not upload the Image and closes down the GUI without any errors.
I'd like to have a ImageGrab with bbox (0, 0, 800, 700) and a QLabel with size(800, 700) so it can fit it perfectly.
class Main(QMainWindow):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setGeometry(200, 200, 1000, 700)
self.setWindowTitle('threads')
self.mainFrame = QFrame(self)
self.mainFrame.resize(1000, 650)
self.mainFrame.move(0, 50)
self.mainFrame.setStyleSheet("background-color: rbg(50, 50, 50)")
self.testButton = QPushButton("Click", self)
self.testButton.resize(500,30)
self.connect(self.testButton, SIGNAL("clicked()"), self.Capture)
self.label_ = QLabel(self.mainFrame)
self.label_.move(10, 10)
self.label_.resize(980, 630)
self.label_.setStyleSheet("background-color: rbg(150, 150, 150)")
#pyqtSlot(QImage)
def ChangeFrame(self, image):
pixmap = QPixmap.fromImage(image)
self.label_.setPixmap(pixmap)
def Capture(self):
self.thread_ = CaptureScreen()
self.connect(self.thread_, SIGNAL("ChangeFrame(QImage)"), self.ChangeFrame, Qt.QueuedConnection)
self.thread_.start()
class CaptureScreen(QThread):
pixmap = pyqtSignal(QImage)
def __init__(self, parent = None):
QThread.__init__(self)
def __del__(self):
print("?????")
self.exiting = True
self.wait()
def run(self):
while(True):
time.sleep(1/60)
img = ImageGrab.grab(bbox=(0, 0, 420, 220))
frame = ImageQt(img)
frame = QImage(frame)
self.emit( SIGNAL("ChangeFrame(QImage)"), frame)
The solution is to use layouts, and set the alignment in QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft.
I also recommend using the new connection syntax, on the other hand if you are going to inherit from QMainWindow in the constructor you must call it. And finally when you use QMainWindow you must set a central widget.
import time
from PyQt4 import QtCore, QtGui
from PIL import ImageGrab, ImageQt
class Main(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.setGeometry(200, 200, 1000, 700)
self.setWindowTitle('threads')
main_widget = QtGui.QWidget()
self.setCentralWidget(main_widget)
lay = QtGui.QVBoxLayout(main_widget)
self.testButton = QtGui.QPushButton("Click")
self.testButton.setFixedHeight(30)
self.testButton.clicked.connect(self.capture)
mainFrame = QtGui.QFrame()
mainFrame.setStyleSheet("background-color: rbg(50, 50, 50)")
_lay = QtGui.QVBoxLayout(mainFrame)
_lay.setContentsMargins(0, 0, 0, 0)
self.label_ = QtGui.QLabel()
_lay.addWidget(self.label_, 0, QtCore.Qt.AlignTop|QtCore.Qt.AlignLeft)
lay.addWidget(self.testButton)
lay.addWidget(mainFrame)
#QtCore.pyqtSlot(QtGui.QImage)
def changeFrame(self, image):
pixmap = QtGui.QPixmap.fromImage(image)
self.label_.setPixmap(pixmap)
#QtCore.pyqtSlot()
def capture(self):
self.thread_ = CaptureScreen()
self.thread_.changedFrame.connect(self.changeFrame, QtCore.Qt.QueuedConnection)
self.thread_.start()
self.testButton.setDisabled(True)
class CaptureScreen(QtCore.QThread):
changedFrame = QtCore.pyqtSignal(QtGui.QImage)
def __del__(self):
print("?????")
self.exiting = True
self.quit()
self.wait()
def run(self):
while True:
time.sleep(1/60)
w, h = 420, 220
img = ImageGrab.grab(bbox=(0, 0, w, h))
frame = ImageQt.toqimage(img)
self.changedFrame.emit(QtGui.QImage(frame))
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = Main()
w.show()
sys.exit(app.exec_())
Related
Here is the code for a camera surveillance system. In the configuration_page.py file I'm getting the size of the window as 100x30 despite using self.showMaximized() and resizeEvent(). The widgets present in it are also showing the size as 640x480. I'm particularly interested in retrieving the size of a widget named self.mid_frame in CameraDisplay class present in config.py file.
Earlier I was facing the same issue in dashboard.py file but I found a workaround by explicitly passing width and height in that case. But this time I'm stuck because I need to get the size of the widget named self.mid_frame. I don't understand why even resizeEvent is showing the wrong size.
main.py
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import Qt, QRect
from threading import Thread, RLock, Lock
from collections import deque
from datetime import datetime
import time
import sys
import cv2
import imutils
from dashboard import *
from configuration_page import *
from global_widgets import *
class CameraWidget(QtGui.QWidget):
"""Independent camera feed
Uses threading to grab IP camera frames in the background
#param width - Width of the video frame
#param height - Height of the video frame
#param stream_link - IP/RTSP/Webcam link
#param aspect_ratio - Whether to maintain frame aspect ratio or force into fraame
"""
def __init__(self, stream_link=0, stacked_widget=None, width=0, height=0, btn_text=None, idx=None, aspect_ratio=False, parent=None, deque_size=1):
super(CameraWidget, self).__init__(parent)
# Initialize deque used to store frames read from the stream
self.deque = deque(maxlen=deque_size)
self.maintain_aspect_ratio = aspect_ratio
self.camera_stream_link = stream_link
self.stacked_widget = stacked_widget
self.idx = idx
# Flag to check if camera is valid/working
self.online = False
self.capture = None
self.video_frame = QtGui.QLabel()
self.video_frame_1 = QtGui.QLabel()
self.load_network_stream()
# Start background frame grabbing
self.get_frame_thread = Thread(target=self.get_frame, args=())
self.get_frame_thread.daemon = True
self.get_frame_thread.start()
# Periodically set video frame to display
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.set_frame)
self.timer.start(.5)
print('Started camera: {}'.format(self.camera_stream_link))
def load_network_stream(self):
"""Verifies stream link and open new stream if valid"""
def load_network_stream_thread():
if self.verify_network_stream(self.camera_stream_link):
self.capture = cv2.VideoCapture(self.camera_stream_link)
self.online = True
self.load_stream_thread = Thread(target=load_network_stream_thread, args=())
self.load_stream_thread.daemon = True
self.load_stream_thread.start()
def verify_network_stream(self, link):
"""Attempts to receive a frame from given link"""
cap = cv2.VideoCapture(link)
if not cap.isOpened():
return False
cap.release()
return True
def get_frame(self):
"""Reads frame, resizes, and converts image to pixmap"""
while True:
try:
if self.capture.isOpened() and self.online:
# Read next frame from stream and insert into deque
status, frame = self.capture.read()
if status:
self.deque.append(frame)
else:
self.capture.release()
self.online = False
else:
# Attempt to reconnect
print('attempting to reconnect', self.camera_stream_link)
self.load_network_stream()
self.spin(2)
self.spin(.001)
except AttributeError:
pass
def spin(self, seconds):
"""Pause for set amount of seconds, replaces time.sleep so program doesnt stall"""
time_end = time.time() + seconds
while time.time() < time_end:
QtGui.QApplication.processEvents()
def set_frame(self):
"""Sets pixmap image to video frame"""
if not self.online:
self.spin(1)
return
if self.deque and self.online:
# Grab latest frame
frame = self.deque[-1]
frame_1 = self.deque[-1]
# Display frames on dashboard and configuration pages
# Frame for dashboard
# Keep frame aspect ratio
if self.maintain_aspect_ratio:
self.frame = imutils.resize(frame, width=self.screen_width)
# Force resize
else:
self.frame = cv2.resize(frame, (self.screen_width, self.screen_height))
self.frame = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)
h, w, ch = self.frame.shape
bytesPerLine = ch * w
# Frame for configuration page-----------------------------------------------------------------------------------------
# print('2: ', self.screen_width_1, self.screen_height_1)
self.frame_1 = cv2.resize(frame_1, (self.screen_width_1, self.screen_height_1))
self.frame_1 = cv2.cvtColor(self.frame_1, cv2.COLOR_BGR2RGB)
h_1, w_1, ch_1 = self.frame_1.shape
bytesPerLine_1 = ch_1 * w_1
# Convert to pixmap and set to video frame
self.img = QtGui.QImage(self.frame, w, h, bytesPerLine, QtGui.QImage.Format_RGB888)
self.pix = QtGui.QPixmap.fromImage(self.img)
self.video_frame.setPixmap(self.pix)
self.img_1 = QtGui.QImage(self.frame_1, w_1, h_1, bytesPerLine_1, QtGui.QImage.Format_RGB888)
self.pix_1 = QtGui.QPixmap.fromImage(self.img_1)
self.video_frame_1.setPixmap(self.pix_1)
def set_frame_params(self, width, height, btn_text=None, idx=0):
self.screen_width = width
self.screen_height = height
self.btn_text = btn_text
self.idx = idx
def set_frame_params_1(self, width, height):
self.screen_width_1 = width
self.screen_height_1 = height
def get_video_display_frame(self):
self.video_display_frame = QtGui.QFrame()
self.video_layout = QtGui.QVBoxLayout()
self.video_btn = QtGui.QPushButton(self.btn_text)
self.video_btn.setStyleSheet("background-color: rgb(128, 159, 255);")
self.video_btn.clicked.connect(self.on_clicked)
# self.video_frame = QtGui.QLabel()
self.video_frame.setScaledContents(True)
self.video_layout.addWidget(self.video_btn)
self.video_layout.addWidget(self.video_frame)
self.video_layout.setContentsMargins(0,0,0,0)
self.video_layout.setSpacing(0)
self.video_display_frame.setLayout(self.video_layout)
return self.video_display_frame
def get_video_frame(self):
self.video_frame_1.setScaledContents(True)
return self.video_frame_1
#QtCore.pyqtSlot()
def on_clicked(self):
GlobalObject().dispatchEvent("hello", args=(self.idx,))
self.stacked_widget.setCurrentIndex(1)
class Window(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.stacked_widget = QtGui.QStackedWidget()
layout = QtGui.QVBoxLayout()
layout.setContentsMargins(0,0,0,0)
layout.addWidget(self.stacked_widget)
widget = QtGui.QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
self.cam1 = CameraWidget(camera1, self.stacked_widget, idx=1)
widget_1 = DashBoard(self.cam1, self.stacked_widget)
widget_2 = ZoneConfig(self.cam1, self.stacked_widget)
self.stacked_widget.addWidget(widget_1)
self.stacked_widget.addWidget(widget_2)
self.showMaximized()
camera1 = '../streams/Fog.avi'
if __name__ == '__main__':
app = QtGui.QApplication([])
app.setStyle(QtGui.QStyleFactory.create("plastique"))
# app.setStyle(QtGui.QStyleFactory.create("Cleanlooks"))
window = Window()
window.show()
sys.exit(app.exec_())
dashboard.py
from PyQt4 import QtGui, QtCore
from global_widgets import *
class DashBoard(QtGui.QWidget):
def __init__(self, cam1, stacked_widget, parent=None):
super(DashBoard, self).__init__(parent)
self.showMaximized()
self.screen_width = self.width()
self.screen_height = self.height()
self.stacked_widget = stacked_widget
# Layouts and frames
layout = QtGui.QVBoxLayout()
top_frame = QtGui.QFrame()
top_frame.setStyleSheet("background-color: rgb(208, 208, 225)")
mid_frame = QtGui.QFrame()
mid_frame.setStyleSheet("background-color: rgb(153, 187, 255)")
layout.addWidget(top_frame, 1)
layout.addWidget(QHLine())
layout.addWidget(mid_frame, 20)
layout.setContentsMargins(0,0,0,0)
layout.setSpacing(0)
self.setLayout(layout)
# Top frame
label = QtGui.QLabel('Dashboard')
label.setFont(QtGui.QFont('Times New Roman', 20))
top_layout = QtGui.QHBoxLayout()
top_layout.addWidget(label, alignment=QtCore.Qt.AlignCenter)
top_layout.setContentsMargins(5,5,5,5)
top_frame.setLayout(top_layout)
# Middle frame
self.mid_layout = QtGui.QStackedLayout()
# Create camera widgets
print('Creating Camera Widgets...')
cam_widget = Cam1(cam1, self.screen_width, self.screen_height, self)
self.mid_layout.addWidget(cam_widget)
self.mid_layout.setCurrentWidget(cam_widget)
mid_frame.setLayout(self.mid_layout)
class Cam1(QtGui.QWidget):
def __init__(self, cam1, screen_width, screen_height, parent=None):
super(Cam1, self).__init__(parent)
cam1.set_frame_params(screen_width, screen_height, 'VIDS 01', 1)
# Add widgets to layout
print('Adding widgets to layout...')
layout = QtGui.QGridLayout()
layout.addWidget(cam1.get_video_display_frame(),0,0,1,1)
layout.setContentsMargins(5,5,5,5)
self.setLayout(layout)
configuration_page.py
from PyQt4 import QtGui, QtCore
from global_widgets import *
from ui_main import *
class ZoneConfig(QtGui.QWidget):
def __init__(self, cam1, stacked_widget, parent=None):
super(ZoneConfig, self).__init__(parent)
GlobalObject().addEventListener("hello", self.set_cam)
self.layout = QtGui.QVBoxLayout()
self.frame = QtGui.QFrame()
self.frame_layout = QtGui.QStackedLayout()
# self.setLayout(self.frame_layout)
self.screen_width = self.width()
self.screen_height = self.height()
cam_widget_1 = CameraDisplay(cam1, 1, self.frame_layout, stacked_widget, self)
self.frame_layout.addWidget(cam_widget_1)
self.frame.setLayout(self.frame_layout)
self.layout.addWidget(self.frame)
self.layout.setContentsMargins(0,0,0,0)
self.setLayout(self.layout)
#QtCore.pyqtSlot()
def set_cam(self, index):
if index == 1:
self.frame_layout.setCurrentIndex(0)
class CameraDisplay(QtGui.QWidget):
def __init__(self, cam, idx, frame_layout, stacked_widget, parent=None):
super(CameraDisplay, self).__init__(parent)
self.showMaximized()
self.screen_width = self.width()
self.screen_height = self.height()
self.frame_layout = frame_layout
self.stacked_widget = stacked_widget
print('size: ', self.screen_width, self.screen_height)
# Layouts and frames
layout = QtGui.QVBoxLayout()
top_frame = QtGui.QFrame()
top_frame.setStyleSheet("background-color: rgb(208, 208, 225)")
self.mid_frame = QtGui.QFrame() ## Size of this frame is needed
self.mid_frame.setStyleSheet("background-color: rgb(153, 187, 255)")
btm_frame = QtGui.QFrame()
btm_frame.setStyleSheet("background-color: rgb(208, 208, 225)")
# Top frame
label = QtGui.QLabel('Configuration')
label.setFont(QtGui.QFont('Times New Roman', 20))
top_layout = QtGui.QHBoxLayout()
top_layout.addWidget(label, alignment=QtCore.Qt.AlignCenter)
top_layout.setContentsMargins(5,5,5,5)
top_frame.setLayout(top_layout)
# Middle frame
mid_layout = QtGui.QHBoxLayout()
# Create camera widgets
print('Creating Camera Widgets...')
mid_layout = QtGui.QHBoxLayout()
self.video_frame = cam.get_video_frame()
mid_layout.addWidget(self.video_frame)
mid_layout.setContentsMargins(5,5,5,5)
self.mid_frame.setLayout(mid_layout)
# Bottom frame
btn = QtGui.QPushButton('Dashboard')
btn.clicked.connect(self.goMainWindow)
btm_layout = QtGui.QHBoxLayout()
btm_layout.addStretch()
btm_layout.addWidget(btn)
btm_layout.setContentsMargins(5,5,5,5)
btm_frame.setLayout(btm_layout)
layout.addWidget(top_frame, 1)
layout.addWidget(QHLine())
layout.addWidget(self.mid_frame, 50)
layout.addWidget(QHLine())
layout.addWidget(btm_frame, 1)
layout.setContentsMargins(0,0,0,0)
layout.setSpacing(0)
self.setLayout(layout)
# self.showMaximized()
cam.set_frame_params_1(self.mid_frame.width(), self.mid_frame.height()-10) ## here I want the size
def resizeEvent(self, event):
QtGui.QWidget.resizeEvent(self, event)
def goMainWindow(self):
self.stacked_widget.setCurrentIndex(0)
I think the problem is only in configuration_page.py file but I'm pasting entire code in case anyone wants to go throught the entire logic.
Add the following to your code.
def event(self, e):
if e.type() in (QEvent.Show, QEvent.Resize):
widget_w = self.mid_frame.frameGeometry().width()
widget_h = self.mid_frame.frameGeometry().height()
screen_w = self.width()
screen_h = self.height()
# For "debugging" purposes:
print("Widget - width: %s height: %s" % (widget_w, widget_h)
print("screen - width: %s height: %s" % (screen_w, screen_h)
return ((widget_w, widget_h), (screen_w, screen_h))
#return {"widget": {"width": widget_w, "height": widget_h}, "screen": {"width": screen_w, "height": screen_h}}
import sys
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import QApplication,QDialog,QPushButton,QVBoxLayout,QWidget
class Main(QDialog):
def __init__(self):
super(Main, self).__init__()
self.ui()
# Group Of Drage Event
def mousePressEvent(self,event):
self.offset = event.pos()
def mouseMoveEvent(self, e):
x = e.globalX()
y = e.globalY()
x_w = self.offset.x()
y_w = self.offset.y()
self.move(x - x_w, y - y_w)
def ui(self):
# TitleBar
self.setWindowFlags(Qt.FramelessWindowHint)
# Window Size
self.setGeometry(600,300,400,500)
# Window Background Color
self.BackGroundColor = QPalette()
self.BackGroundColor.setColor(QPalette.Background, QColor(255,255,255))
self.setPalette(self.BackGroundColor)
# NavBar Button
self.btn = QPushButton('Test',self)
self.btn1 = QPushButton("Test1",self)
# NavBar Layout
self.layout = QVBoxLayout(self)
self.layout.addWidget(self.btn)
self.layout.addWidget(self.btn1)
self.layout.set
self.setLayout(self.layout)
# Close img
self.closeBtn = QPushButton(self)
self.closeBtn.setGeometry(368,0,32,32)
self.closeBtn.setFlat(True)
self.closeBtn.setStyleSheet('QPushButton{background-color: rgba(0,0,0,0.0)}')
self.closeBtn.setIcon(QIcon('img/close.png'))
self.closeBtn.setIconSize(QSize(10,10))
self.closeBtn.clicked.connect(QCoreApplication.instance().quit)
# Maximize icon
self.maxBtn = QPushButton(self)
self.maxBtn.setGeometry(self,336,0,32,32)
self.maxBtn.setFlat(True)
self.maxBtn.setStyleSheet('QPushButton{background-color: rgba(0,0,0,0.0)}')
self.maxBtn.setIcon(QIcon('img/max.png'))
self.maxBtn.setIconSize(QSize(14,14))
# Minimize Incon
self.minBtn = QPushButton(self)
self.minBtn.setGeometry(304,0,32,32)
self.minBtn.setFlat(True)
self.minBtn.setStyleSheet('QPushButton{background-color: rgba(0,0,0,0.0)}')
self.minBtn.setIcon(QIcon('img/min.png'))
self.minBtn.setIconSize(QSize(10,10))
def main():
app = QApplication()
win = Main()
win.show()
app.exec_()
if __name__ == "__main__":
main()
I want to fixed navbar on the left. So, I create instance of QVBoxLayout and add widget to my Layout. and I had searched google, stackoverflow. i Don't get any information about my problem
but I don't know how to set layout widget. please teach me. Thank you.
if you don't understand my text please tell me i will describe that
Version:
PySide 5.14.2.1
Python 3.7.7
The layouts are not visual elements, so they do not have any geometric element associated with them, such as the width size, the layout is a handle of size and positions.
In this case the solution is to establish a container with a fixed size and in that container place the buttons with the help of a layout:
class Main(QDialog):
def __init__(self):
super(Main, self).__init__()
self.ui()
# Group Of Drage Event
def mousePressEvent(self, event):
self.offset = event.pos()
def mouseMoveEvent(self, e):
x = e.globalX()
y = e.globalY()
x_w = self.offset.x()
y_w = self.offset.y()
self.move(x - x_w, y - y_w)
def ui(self):
# TitleBar
self.setWindowFlags(Qt.FramelessWindowHint)
# Window Size
self.setGeometry(600, 300, 400, 500)
# Window Background Color
self.BackGroundColor = QPalette()
self.BackGroundColor.setColor(QPalette.Background, QColor(255, 255, 255))
self.setPalette(self.BackGroundColor)
# NavBar Button
self.btn = QPushButton("Test")
self.btn1 = QPushButton("Test1")
left_container = QWidget(self)
left_container.setFixedWidth(100)
# NavBar layout
self.layout = QVBoxLayout(left_container)
self.layout.addWidget(self.btn)
self.layout.addWidget(self.btn1)
hlay = QHBoxLayout(self)
hlay.addWidget(left_container)
hlay.addStretch()
# Close img
self.closeBtn = QPushButton(self)
self.closeBtn.setGeometry(368, 0, 32, 32)
self.closeBtn.setFlat(True)
self.closeBtn.setStyleSheet("QPushButton{background-color: rgba(0,0,0,0.0)}")
self.closeBtn.setIcon(QIcon("img/close.png"))
self.closeBtn.setIconSize(QSize(10, 10))
self.closeBtn.clicked.connect(QCoreApplication.instance().quit)
# Maximize icon
self.maxBtn = QPushButton(self)
self.maxBtn.setGeometry(336, 0, 32, 32)
self.maxBtn.setFlat(True)
self.maxBtn.setStyleSheet("QPushButton{background-color: rgba(0,0,0,0.0)}")
self.maxBtn.setIcon(QIcon("img/max.png"))
self.maxBtn.setIconSize(QSize(14, 14))
# Minimize Incon
self.minBtn = QPushButton(self)
self.minBtn.setGeometry(304, 0, 32, 32)
self.minBtn.setFlat(True)
self.minBtn.setStyleSheet("QPushButton{background-color: rgba(0,0,0,0.0)}")
self.minBtn.setIcon(QIcon("img/min.png"))
self.minBtn.setIconSize(QSize(10, 10))
I want to draw a circle that displays over a video at the cursor location when pressing the mouse. The video is playing in a QLabel object that is in a MainWindow. I’m using OpenCV to read frames from the webcam at 10 fps. I’m converting the frames to QPixmap and displaying them in the QLabel object (self.vidWindow).
In the code below, the circle is painted immediately when the MainWindow is launched (not what I want) and is then covered up by the video stream. Text displays in the mask Qlabel object and a message is printed in the MainWindow when the mouse button is pressed.
Can I draw a circle in a QLabel object? If so, should I use the mask QLabel object or is there a way to overlay directly over the video in the self.vidWindow?
In the minimalized version of the code, the video displays, but an error is triggered when I try to draw the ellipse.
import sys, cv2
from PyQt5.QtWidgets import QMainWindow, QApplication, QLabel
from PyQt5.QtGui import QImage, QPixmap, QPainter, QPen, QFont
from PyQt5.QtCore import QTimer, Qt, QCoreApplication
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.statusBar().showMessage('Ready')
self.setGeometry(50, 50, 800, 600)
self.setWindowTitle('Statusbar')
self.vidWindow = QLabel(self)
self.vidWindow.setGeometry(20, 20, 640, 480)
self.maskWindow = QLabel(self)
self.maskWindow.setGeometry(20, 20, 640, 480)
self.maskWindow.setStyleSheet('background-color: rgba(0,0,0,0%)')
font = QFont()
font.setPointSize(18)
font.setBold(True)
font.setWeight(75)
self.maskWindow.setFont(font)
self.maskWindow.setText('Message is on the mask Qlabel object')
self.msgLabel = QLabel(self)
self.msgLabel.setGeometry(675, 300, 100, 20)
self.cap = cv2.VideoCapture(0)
self.pix = QImage()
self.timer = QTimer()
self.frame_rate = 5
self.show()
self.start()
def nextFrameSlot(self):
ret, frame = self.cap.read()
if ret == True:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
img = QImage(frame,frame.shape[1], frame.shape[0], QImage.Format_RGB888)
img = img.scaled(640, 480, Qt.KeepAspectRatio)
self.pix = QPixmap.fromImage(img)
self.vidWindow.setPixmap(self.pix)
def mousePressEvent(self, QMouseEvent):
self.msgLabel.setText('Mouse Clicked!')
def paintEvent(self, QMouseEvent):
e = QMouseEvent
painter = QPainter(self)
painter.setPen(QPen(Qt.green, 4, Qt.SolidLine))
painter.drawEllipse(e.x(), e.y(), 100)
def start(self):
rate = int(1000.0 / self.frame_rate)
self.timer.setTimerType(Qt.PreciseTimer)
self.timer.timeout.connect(self.nextFrameSlot)
self.timer.start(rate)
def closeEvent(self, event):
if self.cap.isOpened():
self.cap.release()
self.vidWindow.clear()
QCoreApplication.quit()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MainWindow()
sys.exit(app.exec_())
Although QPainter is used to paint a widget it will not work for this case since it paints the "MainWindow" that is below its children as the QLabels. There are at least 2 possible solutions:
Create a custom QLabel and detect the click and paint the circle,
Create a QLabel that shows a QPixmap that has the circle and move it based on the mouse information.
In this case I will implement the second method:
import sys, cv2
from PyQt5.QtWidgets import QMainWindow, QApplication, QLabel
from PyQt5.QtGui import QImage, QPixmap, QPainter, QPen, QFont
from PyQt5.QtCore import QTimer, Qt
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.statusBar().showMessage("Ready")
self.setGeometry(50, 50, 800, 600)
self.setWindowTitle("Statusbar")
self.vidWindow = QLabel(self)
self.vidWindow.setGeometry(20, 20, 640, 480)
self.maskWindow = QLabel(self)
self.maskWindow.setGeometry(20, 20, 640, 480)
self.maskWindow.setStyleSheet("background-color: rgba(0,0,0,0%)")
font = QFont()
font.setPointSize(18)
font.setBold(True)
font.setWeight(75)
self.maskWindow.setFont(font)
self.maskWindow.setText("Message is on the mask Qlabel object")
self.msgLabel = QLabel(self)
self.msgLabel.setGeometry(675, 300, 100, 20)
self.marker_label = QLabel(self)
pixmap = QPixmap(100, 100)
pixmap.fill(Qt.transparent)
painter = QPainter(pixmap)
painter.setPen(QPen(Qt.green, 4, Qt.SolidLine))
painter.drawEllipse(pixmap.rect().adjusted(4, 4, -4, -4))
painter.end()
self.marker_label.setPixmap(pixmap)
self.marker_label.adjustSize()
self.marker_label.hide()
self.marker_label.raise_()
self.cap = cv2.VideoCapture(0)
self.timer = QTimer()
self.frame_rate = 5
self.show()
self.start()
def nextFrameSlot(self):
ret, frame = self.cap.read()
if ret == True:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
img = QImage(frame, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
img = img.scaled(640, 480, Qt.KeepAspectRatio)
pix = QPixmap.fromImage(img)
self.vidWindow.setPixmap(pix)
def mousePressEvent(self, event):
self.msgLabel.setText("Mouse Clicked!")
if self.vidWindow.rect().contains(event.pos()):
self.marker_label.move(event.pos() - self.marker_label.rect().center())
self.marker_label.show()
super().mousePressEvent(event)
def start(self):
rate = int(1000.0 / self.frame_rate)
self.timer.setTimerType(Qt.PreciseTimer)
self.timer.timeout.connect(self.nextFrameSlot)
self.timer.start(rate)
def closeEvent(self, event):
if self.cap.isOpened():
self.cap.release()
super().closeEvent(event)
if __name__ == "__main__":
app = QApplication(sys.argv)
ex = MainWindow()
sys.exit(app.exec_())
I am trying to achieve something like this in PySide: https://codepen.io/imprakash/pen/GgNMXO
What I want to do is create a child window frameless with a black overlay below.
I didn't succeed to create a child window frameless and the overlay...
This is a base code to replicate the HTML:
from PySide import QtCore, QtGui
import sys
class MainWindow(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.resize(800, 500)
self.button = QtGui.QPushButton("Click Me")
self.setLayout(QtGui.QVBoxLayout())
self.layout().addWidget(self.button)
# Connections
self.button.clicked.connect(self.displayOverlay)
def displayOverlay(self):
popup = QtGui.QDialog(self)
popup.setWindowFlags(QtCore.Qt.FramelessWindowHint)
popup.setLayout(QtGui.QHBoxLayout())
popup.layout().addWidget(QtGui.QLabel("HI"))
popup.show()
print "clicked"
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
If you comment the line with the FramelessWindowHint, the window comes, else nothing happen...
I really hope that someone could help me. Thank you for the time you spent to read my question.
I'll be using PyQt5 for this explanation. It might have some differences to PySide (which I'm not sure if its still maintained) and PyQt4, but it shouldn't be too hard to convert.
The following example has a parent widget which a few buttons. One of them (the obvious one) calls for the popup. I've prepared the example to deal with the parent resize but have not made any code regarding mouse events of dragging the popup (see mouseMoveEvent and mouseReleaseEvent for that).
So here is the code:
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
class TranslucentWidgetSignals(QtCore.QObject):
# SIGNALS
CLOSE = QtCore.pyqtSignal()
class TranslucentWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(TranslucentWidget, self).__init__(parent)
# make the window frameless
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
self.fillColor = QtGui.QColor(30, 30, 30, 120)
self.penColor = QtGui.QColor("#333333")
self.popup_fillColor = QtGui.QColor(240, 240, 240, 255)
self.popup_penColor = QtGui.QColor(200, 200, 200, 255)
self.close_btn = QtWidgets.QPushButton(self)
self.close_btn.setText("x")
font = QtGui.QFont()
font.setPixelSize(18)
font.setBold(True)
self.close_btn.setFont(font)
self.close_btn.setStyleSheet("background-color: rgb(0, 0, 0, 0)")
self.close_btn.setFixedSize(30, 30)
self.close_btn.clicked.connect(self._onclose)
self.SIGNALS = TranslucentWidgetSignals()
def resizeEvent(self, event):
s = self.size()
popup_width = 300
popup_height = 120
ow = int(s.width() / 2 - popup_width / 2)
oh = int(s.height() / 2 - popup_height / 2)
self.close_btn.move(ow + 265, oh + 5)
def paintEvent(self, event):
# This method is, in practice, drawing the contents of
# your window.
# get current window size
s = self.size()
qp = QtGui.QPainter()
qp.begin(self)
qp.setRenderHint(QtGui.QPainter.Antialiasing, True)
qp.setPen(self.penColor)
qp.setBrush(self.fillColor)
qp.drawRect(0, 0, s.width(), s.height())
# drawpopup
qp.setPen(self.popup_penColor)
qp.setBrush(self.popup_fillColor)
popup_width = 300
popup_height = 120
ow = int(s.width()/2-popup_width/2)
oh = int(s.height()/2-popup_height/2)
qp.drawRoundedRect(ow, oh, popup_width, popup_height, 5, 5)
font = QtGui.QFont()
font.setPixelSize(18)
font.setBold(True)
qp.setFont(font)
qp.setPen(QtGui.QColor(70, 70, 70))
tolw, tolh = 80, -5
qp.drawText(ow + int(popup_width/2) - tolw, oh + int(popup_height/2) - tolh, "Yep, I'm a pop up.")
qp.end()
def _onclose(self):
print("Close")
self.SIGNALS.CLOSE.emit()
class ParentWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(ParentWidget, self).__init__(parent)
self._popup = QtWidgets.QPushButton("Gimme Popup!!!")
self._popup.setFixedSize(150, 40)
self._popup.clicked.connect(self._onpopup)
self._other1 = QtWidgets.QPushButton("A button")
self._other2 = QtWidgets.QPushButton("A button")
self._other3 = QtWidgets.QPushButton("A button")
self._other4 = QtWidgets.QPushButton("A button")
hbox = QtWidgets.QHBoxLayout()
hbox.addWidget(self._popup)
hbox.addWidget(self._other1)
hbox.addWidget(self._other2)
hbox.addWidget(self._other3)
hbox.addWidget(self._other4)
self.setLayout(hbox)
self._popframe = None
self._popflag = False
def resizeEvent(self, event):
if self._popflag:
self._popframe.move(0, 0)
self._popframe.resize(self.width(), self.height())
def _onpopup(self):
self._popframe = TranslucentWidget(self)
self._popframe.move(0, 0)
self._popframe.resize(self.width(), self.height())
self._popframe.SIGNALS.CLOSE.connect(self._closepopup)
self._popflag = True
self._popframe.show()
def _closepopup(self):
self._popframe.close()
self._popflag = False
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
main = ParentWidget()
main.resize(500, 500)
main.show()
sys.exit(app.exec_())
Which results in the following:
The logic is the following. You create an empty Widget and manually draw the background and popup (paintEvent). You add a button for closing the popup. For this you build a Signal and let the parent widget do the closing. This is important because you need to make the parent widget control some important elements of the popup (such as closing, resizng, etc.). You can add far more complexity but hopefully the example will suffice for starters.
Thanks to armatita, I succeed to get what I wanted. For now, there are some issues but it works and I get the result that I wanted.
I give you the code to the next who will be looking for the same thing.
from PySide import QtCore, QtGui
import sys
class CtmWidget(QtGui.QWidget):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self, parent)
self.button = QtGui.QPushButton("Close Overlay")
self.setLayout(QtGui.QHBoxLayout())
self.layout().addWidget(self.button)
self.button.clicked.connect(self.hideOverlay)
def paintEvent(self, event):
painter = QtGui.QPainter()
painter.begin(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
path = QtGui.QPainterPath()
path.addRoundedRect(QtCore.QRectF(self.rect()), 10, 10)
mask = QtGui.QRegion(path.toFillPolygon().toPolygon())
pen = QtGui.QPen(QtCore.Qt.white, 1)
painter.setPen(pen)
painter.fillPath(path, QtCore.Qt.white)
painter.drawPath(path)
painter.end()
def hideOverlay(self):
self.parent().hide()
class Overlay(QtGui.QWidget):
def __init__(self, parent, widget):
QtGui.QWidget.__init__(self, parent)
palette = QtGui.QPalette(self.palette())
palette.setColor(palette.Background, QtCore.Qt.transparent)
self.setPalette(palette)
self.widget = widget
self.widget.setParent(self)
def paintEvent(self, event):
painter = QtGui.QPainter()
painter.begin(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.fillRect(event.rect(), QtGui.QBrush(QtGui.QColor(0, 0, 0, 127)))
painter.end()
def resizeEvent(self, event):
position_x = (self.frameGeometry().width()-self.widget.frameGeometry().width())/2
position_y = (self.frameGeometry().height()-self.widget.frameGeometry().height())/2
self.widget.move(position_x, position_y)
event.accept()
class MainWindow(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.resize(800, 500)
self.button = QtGui.QPushButton("Click Me")
self.setLayout(QtGui.QVBoxLayout())
self.layout().addWidget(self.button)
self.popup = Overlay(self, CtmWidget())
self.popup.hide()
# Connections
self.button.clicked.connect(self.displayOverlay)
def displayOverlay(self):
self.popup.show()
print "clicked"
def resizeEvent(self, event):
self.popup.resize(event.size())
event.accept()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
Once again thank you both of you(ymmx and armatita) to spend time on my issue.
did you try replacing popup.show() by popup.exec_()? and remove self as a parameter of the Qdialog? I change QDialog to QmessageBox to be able to quit the subwindow but it still work with the QDialog.
popup = QMessageBox()
popup.setWindowFlags( Qt.FramelessWindowHint)
popup.setLayout( QHBoxLayout())
popup.layout().addWidget( QLabel("HI"))
popup.exec_()
update
class Popup(QDialog ):
def __init__(self):
super().__init__()
self.setWindowFlags( Qt.CustomizeWindowHint)
self.setLayout( QHBoxLayout())
Button_close = QPushButton('close')
self.layout().addWidget( QLabel("HI"))
self.layout().addWidget( Button_close)
Button_close.clicked.connect( self.close )
self.exec_()
print("clicked")
def mousePressEvent(self, event):
self.oldPos = event.globalPos()
def mouseMoveEvent(self, event):
delta = QPoint (event.globalPos() - self.oldPos)
#print(delta)
self.move(self.x() + delta.x(), self.y() + delta.y())
self.oldPos = event.globalPos()
class MainWindow( QWidget):
def __init__(self):
QWidget.__init__(self)
self.resize(800, 500)
self.button = QPushButton("Click Me")
self.setLayout( QVBoxLayout())
self.layout().addWidget(self.button)
# Connections
self.button.clicked.connect(self.displayOverlay)
def displayOverlay(self):
Popup( )
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
I have 2 issues with QGraphicsView.
I can't get the size of the QGraphicsView object. All methods I'm using is giving me values I wouldn't expect.
If I print out the mouse's position on the area's lower-right corner (scrollbars included), I get a random 400 value. After setting sceneRect to 500 I was expecting to get that back.
from PySide import QtGui, QtCore
class View(QtGui.QGraphicsView):
def __init__(self, parent = None):
super(View, self).__init__(parent)
self.setScene( QtGui.QGraphicsScene(self) )
self.setSceneRect( 0, 0, 500, 500 )
print self.viewport().width() # outputs 96
print self.width() # outputs 100
print self.rect() # outputs QRect(0, 0, 100, 30)
print self.size() # outputs QSize(100, 30)
def mouseMoveEvent(self, event):
print event.pos().toTuple() # prints (413, 423) at lower-right corner
class MainWindow(QtGui.QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.resize(500, 500)
self.view = View(self)
hLayout = QtGui.QHBoxLayout()
hLayout.addWidget(self.view)
buttonsLayout = QtGui.QVBoxLayout()
buttonsLayout.setSpacing(0)
for i in range(10):
newButton = QtGui.QPushButton()
buttonsLayout.addWidget(newButton)
hLayout.addLayout(buttonsLayout)
self.tempButton = QtGui.QPushButton()
mainLayout = QtGui.QVBoxLayout()
mainLayout.addLayout(hLayout)
mainLayout.addWidget(self.tempButton)
self.setLayout(mainLayout)
def run(self):
self.show()
win = MainWindow()
win.run()
Thank you!
Regarding your first issue, I believe you are not getting the sizes you are expecting for two reasons:
You are not explicitly setting the size of the QGraphicsView widget to 500, but the QGraphicsScene instead.
You are fetching the sizes too early in the construction of your application, before the layout of the MainWindow has been properly painted.
Regarding your second issue, depending of what is desired, it is possible to use the method mapFromScene to get the position of the mouse event in regard to the QGraphicsScene instead of the QGraphicsView widget.
More specifically, this can be achieve in your code by:
Setting the size of the QGraphicsView widget with setFixedSize ;
Moving the "size-fetching" calls in the run method, after the MainWindow has been painted ;
Adding a mapToScene transformation on the mouseMoveEvent coordinate.
Below is the code that was modified accordingly to the points listed above:
from PySide import QtGui, QtCore
import sys
class View(QtGui.QGraphicsView):
def __init__(self, parent = None):
super(View, self).__init__(parent)
self.setScene(QtGui.QGraphicsScene(self) )
self.setSceneRect( 0, 0, 1000, 1000 )
self.setFixedSize(500, 500)
def mouseMoveEvent(self, event):
print
print self.mapToScene(event.pos()).toTuple()
# prints (1000, 1000) at lower-right corner
print event.pos().toTuple()
# prints (500, 500) at lower-right corner
class MainWindow(QtGui.QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.view = View(self)
hLayout = QtGui.QHBoxLayout()
hLayout.addWidget(self.view)
buttonsLayout = QtGui.QVBoxLayout()
buttonsLayout.setSpacing(0)
for i in range(10):
newButton = QtGui.QPushButton()
buttonsLayout.addWidget(newButton)
hLayout.addLayout(buttonsLayout)
self.tempButton = QtGui.QPushButton()
mainLayout = QtGui.QVBoxLayout()
mainLayout.addLayout(hLayout)
mainLayout.addWidget(self.tempButton)
self.setLayout(mainLayout)
def run(self):
self.show()
print
print self.view.viewport().width() # outputs 485
print self.view.width() # outputs 500
print self.view.rect() # outputs QRect(0, 0, 500, 500)
print self.view.size() # outputs QSize(500, 500)
print self.view.sceneRect() #outputs QRect(0, 0, 1000, 1000)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
win = MainWindow()
win.run()
sys.exit(app.exec_())
With the code above, the value returned for the size of QGraphicView is 500x500, while it is 1000x1000 for the QGraphicsScene, as expected.