Implementing a Canvas in PyQt5 - python

I have been trying to learn to using PyQt5.
I want to implement a canvas below the "Menu Bar"
class gui(QDialog):
def __init__(self, parent=None):
super(gui, self).__init__(parent)
self.createTopLayout()
self.painter = canvas(self)
mainLayout = QGridLayout()
mainLayout.addLayout(self.topLayout, 0, 0, 1, 2)
mainLayout.addWidget(self.painter, 1, 0, 6, 2)
self.setLayout(mainLayout)
def createTopLayout(self):
self.topLayout = QHBoxLayout()
button1 = QPushButton("b1")
button2 = QPushButton("b2")
button3 = QPushButton("b3")
styleComboBox = QComboBox()
styleComboBox.addItems(QStyleFactory.keys())
styleLabel = QLabel("&Style:")
styleLabel.setBuddy(styleComboBox)
self.topLayout.addWidget(styleLabel)
self.topLayout.addWidget(styleComboBox)
self.topLayout.addStretch(1)
self.topLayout.addWidget(button1)
self.topLayout.addWidget(button2)
self.topLayout.addWidget(button3)
Where my canvas is defined as
class canvas(QMainWindow):
def __init__(self, parent=None):
super(canvas, self).__init__(parent)
self.setGeometry(100, 100, 1000, 700)
def paintEvent(self, e):
cir = circle() #circle class creates a circle with random center and radius both between 0 to 100
painter = QPainter(self)
painter.setPen(QPen(Qt.red, 1, Qt.SolidLine))
painter.drawEllipse(self, cir.center.x, cir.center.y, cir.radius, cir.radius)
but for me canvas doesnt render at all let alone the ellipse.

You should not use QMainWindow as a canvas, instead use a QWidget. On the other hand setGeometry will not work if you use layout since the latter handles the geometry, instead it establishes a fixed size and adequate margins. On the other hand it is recommended that the name of the classes begin with capital letters, considering the above the solution is:
class Canvas(QWidget):
def __init__(self, parent=None):
super(Canvas, self).__init__(parent)
self.setFixedSize(1000, 700)
self.setContentsMargins(100, 100, 100, 100)
def paintEvent(self, e):
# ...

Related

Make the whole gridLayoutWidget clickable

I have the following code which shows me 7 of the widgets in a history window of my application. Each widget has a name, icon and an address to a previously opened file. I want all widgets to be clickable and write a function which exactly knows which widget was clicked. I have no clue on how to make a whole widget act as a single clickable item.
This is my code for showing the widgets.
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
for i in range (7):
self.gridLayoutWidget = QWidget(self)
self.gridLayoutWidget.setGeometry(QRect(40, 40 + i * 60, 350, 40))
self.gridLayout = QGridLayout(self.gridLayoutWidget)
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.Label1 = QLabel(self.gridLayoutWidget)
self.Label1.setText("the name of the file")
self.Label1.setFont(QFont('Arial', 14))
self.gridLayout.addWidget(self.Label1, 0, 1, 1, 9)
self.Label2 = QLabel(self.gridLayoutWidget)
self.Label2.setText("the address of the file")
self.gridLayout.addWidget(self.Label2, 1, 1, 1, 9)
self.im = QPixmap("img.ico")
self.icon = QLabel()
self.icon.setPixmap(self.im)
self.gridLayout.addWidget(self.icon , 0, 0, 2, 1)
In this case it is better to create a class that inherits from widget and implements the logic of the complex widget to avoid repetitive code that can cause problems (for example self.Label2 what does QLabel refer to? Does it refer to the first, the second, ...? , since it refers to the latter). On the other hand, that allows us to override the mouseReleaseEvent method, causing a signal to be emitted that later allows us to identify the widget using the sender method.
class GridLayoutWidget(QWidget):
clicked = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
self.Label1 = QLabel(text="the name of the file", font=QFont("Arial", 14))
self.Label2 = QLabel(text="the address of the file")
self.icon = QLabel(pixmap=QPixmap("img.ico"))
self.gridLayout = QGridLayout(self)
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.addWidget(self.Label1, 0, 1, 1, 9)
self.gridLayout.addWidget(self.Label2, 1, 1, 1, 9)
self.gridLayout.addWidget(self.icon, 0, 0, 2, 1)
def mouseReleaseEvent(self, event):
super().mouseReleaseEvent(event)
self.clicked.emit()
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
rect = QRect(40, 40, 350, 40)
for i in range(7):
widget = GridLayoutWidget(self)
widget.setGeometry(rect.translated(0, 60 * i))
widget.clicked.connect(self.handle_clicked)
self.resize(640, 480)
#pyqtSlot()
def handle_clicked(self):
widget = self.sender()
print(widget)

How can I change size of circle in pyqt5

How can I change size of circle in first window, using slider from second window. Is there an option to send value from slider to first window and put this value in painter.drawEllipse fuction?
class ThirdWindow(QWidget):
def __init__(self):
super().__init__()
hbox = QHBoxLayout()
sld = QSlider(Qt.Horizontal, self)
sld.setRange(0, 100)
sld.setFocusPolicy(Qt.NoFocus)
sld.setPageStep(5)
sld.valueChanged.connect(self.updateLabel)
self.label = QLabel('0', self)
self.label.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
self.label.setMinimumWidth(80)
hbox.addWidget(sld)
hbox.addSpacing(15)
hbox.addWidget(self.label)
self.setLayout(hbox)
self.setGeometry(600, 60, 500, 500)
self.setWindowTitle('QSlider')
self.show()
def updateLabel(self, value):
self.label.setText(str(value))
class Window(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setWindowTitle("Dialogi")
self.w = ThirdWindow()
actionFile = self.menuBar().addMenu("Dialog")
action = actionFile.addAction("Zmień tło")
action1 = actionFile.addAction("Zmień grubość koła")
action1.triggered.connect(self.w.show)
self.setGeometry(100, 60, 300, 300)
self.setStyleSheet("background-color: Green")
def paintEvent(self, event):
painter = QPainter(self)
painter.setPen(QPen(Qt.gray, 8, Qt.SolidLine))
painter.drawEllipse(100, 100, 100, 100)
You can communicate with the parent class (in this case the class where the circle is located i.e. Window by calling self.parentWidget()
so you can call that inside your updateLabel function and pass the value. However to repaint the surface you must have a variable initiated in the main Window
Hence your code should look like-
class Window(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setWindowTitle("Dialogi")
self.w = ThirdWindow()
actionFile = self.menuBar().addMenu("Dialog")
action = actionFile.addAction("Zmień tło")
action1 = actionFile.addAction("Zmień grubość koła")
action1.triggered.connect(self.w.show)
self.setGeometry(100, 60, 300, 300)
self.setStyleSheet("background-color: Green")
self.radius = 100 #add a variable radius to keep track of the circle radius
def paintEvent(self, event):
painter = QPainter(self)
painter.setPen(QPen(Qt.gray, 8, Qt.SolidLine))
#change function to include radius
painter.drawEllipse(100, 100, self.radius, self.radius)
and in the other widget change the updateLabel function to change the radius and call repaint
class ThirdWindow(QWidget):
def updateLabel(self, value):
self.label.setText(str(value))
self.parentWidget().radius = value
self.parentWidget().repaint()

How to set width QVboxLayout

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))

Pixmap image size in Qlabel

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_())

PySide QGraphicsView size

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.

Categories

Resources