John McCallen
John McCallen

Reputation: 13

python pyqt QPainter widgets don't show up in layout

I am new to python3 pyqt GUI development. I am trying to make a simple GUI which has various widgets spaced nicely in a compound layout structure of a QVBoxLayout which contains many QHBoxLayouts. When building the layouts, I use the addStretch() method to space out the widgets but the circles which are painted do not show up in the GUI display. However if I comment out all of the addStretch() function calls then the GUI displays all the desired widgets (however the spacing is not good looking). Where are the circles going?

Please help!

Code where circle widgets do not show up.

#!/usr/bin/python3

import sys

from PyQt4 import QtCore, QtGui

class DrawCircle(QtGui.QWidget):
    def __init__(self, color, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.color = color

    def setColor(self,color):
        self.color = color

    def paintEvent(self,event):
        self.paint = QtGui.QPainter()
        self.paint.begin(self)
        self.paint.setRenderHint(QtGui.QPainter.Antialiasing)
        radx = 30
        rady = radx
        if self.color == 'red':
            self.paint.setPen(QtCore.Qt.red)
            self.paint.setBrush(QtCore.Qt.red)
        elif self.color == 'green':
            self.paint.setPen(QtCore.Qt.green)
            self.paint.setBrush(QtCore.Qt.green)
        k = int(radx * 1.5)
        center = QtCore.QPoint(k,k)
        self.paint.drawEllipse(center,radx,rady)
        self.paint.end()


class Window(QtGui.QMainWindow, QtGui.QGraphicsView):
    def __init__(self):
        super(Window,self).__init__()

        # GUI Setup
        self.myWid = QtGui.QWidget()

        # Establish a Vertical Box Layout
        # The layout will be a vertical box layout comprised of horizontal
        # box layouts
        self.vbox_fullDisplay = QtGui.QVBoxLayout()


        # Add a Zero-width spacer-item before the area where the
        # text label will appear
        self.vbox_fullDisplay.addStretch(1)

        ###############################
        # Build horizontal box layout #
        ###############################
        self.hbox_top = QtGui.QHBoxLayout()
        # The text should be centered on the screen so zero-width
        # spacer items will be added to the left and right of the label
        self.hbox_top.addStretch(1)

        self.top_text_label = QtGui.QLabel(self)
        self.top_text_label.setFont(QtGui.QFont("Times",32,QtGui.QFont.Bold))
        self.top_text_label.setText("Top of window")
        self.top_text_label.adjustSize()
        self.hbox_top.addWidget(self.top_text_label)

        self.hbox_top.addStretch(1)

        # Add the horizontal box layout to the vertical box layout
        self.vbox_fullDisplay.addLayout(self.hbox_top)

        # Add space between the text and the row of circles
        self.vbox_fullDisplay.addStretch(1)

        ############################
        # Build the row of buttons #
        ############################
        self.hbox_buttons = QtGui.QHBoxLayout()
        # The text should be centered on the screen so zero-width
        # spacer items will be added to the left and right of the label
        self.hbox_buttons.addStretch(1)

        # These buttons will be used to toggle the color of the circles
        btn1 = QtGui.QPushButton('Circle 1')
        btn1.clicked.connect(self.circle1Meth)

        btn2 = QtGui.QPushButton('Circle 2')
        btn2.clicked.connect(self.circle2Meth)

        self.hbox_buttons.addWidget(btn1)
        self.hbox_buttons.addStretch(1)
        self.hbox_buttons.addWidget(btn2)
        self.hbox_buttons.addStretch(1)

        # Add the horizontal box layout to the vertical box layout
        self.vbox_fullDisplay.addLayout(self.hbox_buttons)

        # Add space between the text and the row of circles
        self.vbox_fullDisplay.addStretch(1)

        ############################
        # Build the row of circles #
        ############################
        self.hbox_circles = QtGui.QHBoxLayout()
        self.hbox_circles.addStretch(1)
        self.__circleColors = ['red','green']
        self.__circle1Color = 0;
        self.circle1 = DrawCircle(self.__circleColors[self.__circle1Color])
        print("const draw circ2")
        self.__circle2Color = 1
        self.circle2 = DrawCircle(self.__circleColors[self.__circle2Color])

        self.hbox_circles.addWidget(self.circle1)
        self.hbox_circles.addStretch(1)
        self.hbox_circles.addWidget(self.circle2)
        self.hbox_circles.addStretch(1)

        # Add the row of circles to the vertical box layout
        self.vbox_fullDisplay.addLayout(self.hbox_circles)

        # Add space between the circles and the slot machine pictures
        self.vbox_fullDisplay.addStretch(1)

        self.bottomLabel = QtGui.QLabel(self)
        self.bottomLabel.setText("Bottom Of GUI")
        self.bottomLabel.adjustSize()
        self.vbox_fullDisplay.addWidget(self.bottomLabel)

        self.myWid.setLayout(self.vbox_fullDisplay)
        self.setCentralWidget(self.myWid)

        self.setGeometry(100,200,600,500)
        self.setWindowTitle('Does this work')

    def circle1Meth(self):
        print("Circ1 change")
        if self.__circle1Color == 1:
            self.__circle1Color = 0
        else:
            self.__circle1Color = 1

        # Set the color for circle 1
        self.circle1.setColor(self.__circleColors[self.__circle1Color])

        # force a repaint by updating the widget Using the repaint() method
        # could cause infinite recursion
        self.update()

    def circle2Meth(self):
        print("Circ2 change")
        if self.__circle2Color == 1:
            self.__circle2Color = 0
        else:
            self.__circle2Color = 1

        # Set the color for circle 1
        self.circle2.setColor(self.__circleColors[self.__circle2Color])

        # force a repaint by updating the widget Using the repaint() method
        # could cause infinite recursion
        self.update()

###
# Run GUI
###
app = QtGui.QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())

Code where circle widgets show up (all addStretch() calls commented out)

#!/usr/bin/python3

import sys

from PyQt4 import QtCore, QtGui

class DrawCircle(QtGui.QWidget):
    def __init__(self, color, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.color = color

    def setColor(self,color):
        self.color = color

    def paintEvent(self,event):
        self.paint = QtGui.QPainter()
        self.paint.begin(self)
        self.paint.setRenderHint(QtGui.QPainter.Antialiasing)
        radx = 30
        rady = radx
        if self.color == 'red':
            self.paint.setPen(QtCore.Qt.red)
            self.paint.setBrush(QtCore.Qt.red)
        elif self.color == 'green':
            self.paint.setPen(QtCore.Qt.green)
            self.paint.setBrush(QtCore.Qt.green)
        k = int(radx * 1.5)
        center = QtCore.QPoint(k,k)
        self.paint.drawEllipse(center,radx,rady)
        self.paint.end()


class Window(QtGui.QMainWindow, QtGui.QGraphicsView):
    def __init__(self):
        super(Window,self).__init__()

        # GUI Setup
        self.myWid = QtGui.QWidget()

        # Establish a Vertical Box Layout
        # The layout will be a vertical box layout comprised of horizontal
        # box layouts
        self.vbox_fullDisplay = QtGui.QVBoxLayout()


        # Add a Zero-width spacer-item before the area where the
        # text label will appear
        #self.vbox_fullDisplay.addStretch(1)

        ###############################
        # Build horizontal box layout #
        ###############################
        self.hbox_top = QtGui.QHBoxLayout()
        # The text should be centered on the screen so zero-width
        # spacer items will be added to the left and right of the label
        #self.hbox_top.addStretch(1)

        self.top_text_label = QtGui.QLabel(self)
        self.top_text_label.setFont(QtGui.QFont("Times",32,QtGui.QFont.Bold))
        self.top_text_label.setText("Top of window")
        self.top_text_label.adjustSize()
        self.hbox_top.addWidget(self.top_text_label)

        #self.hbox_top.addStretch(1)

        # Add the horizontal box layout to the vertical box layout
        self.vbox_fullDisplay.addLayout(self.hbox_top)

        # Add space between the text and the row of circles
        #self.vbox_fullDisplay.addStretch(1)

        ############################
        # Build the row of buttons #
        ############################
        self.hbox_buttons = QtGui.QHBoxLayout()
        # The text should be centered on the screen so zero-width
        # spacer items will be added to the left and right of the label
        #self.hbox_buttons.addStretch(1)

        # These buttons will be used to toggle the color of the circles
        btn1 = QtGui.QPushButton('Circle 1')
        btn1.clicked.connect(self.circle1Meth)

        btn2 = QtGui.QPushButton('Circle 2')
        btn2.clicked.connect(self.circle2Meth)

        self.hbox_buttons.addWidget(btn1)
        #self.hbox_buttons.addStretch(1)
        self.hbox_buttons.addWidget(btn2)
        #self.hbox_buttons.addStretch(1)

        # Add the horizontal box layout to the vertical box layout
        self.vbox_fullDisplay.addLayout(self.hbox_buttons)

        # Add space between the text and the row of circles
        #self.vbox_fullDisplay.addStretch(1)

        ############################
        # Build the row of circles #
        ############################
        self.hbox_circles = QtGui.QHBoxLayout()
        #self.hbox_circles.addStretch(1)
        self.__circleColors = ['red','green']
        self.__circle1Color = 0;
        self.circle1 = DrawCircle(self.__circleColors[self.__circle1Color])
        print("const draw circ2")
        self.__circle2Color = 1
        self.circle2 = DrawCircle(self.__circleColors[self.__circle2Color])

        self.hbox_circles.addWidget(self.circle1)
        #self.hbox_circles.addStretch(1)
        self.hbox_circles.addWidget(self.circle2)
        #self.hbox_circles.addStretch(1)

        # Add the row of circles to the vertical box layout
        self.vbox_fullDisplay.addLayout(self.hbox_circles)

        # Add space between the circles and the slot machine pictures
        #self.vbox_fullDisplay.addStretch(1)

        self.bottomLabel = QtGui.QLabel(self)
        self.bottomLabel.setText("Bottom Of GUI")
        self.bottomLabel.adjustSize()
        self.vbox_fullDisplay.addWidget(self.bottomLabel)

        self.myWid.setLayout(self.vbox_fullDisplay)
        self.setCentralWidget(self.myWid)

        self.setGeometry(100,200,600,500)
        self.setWindowTitle('Does this work')

    def circle1Meth(self):
        print("Circ1 change")
        if self.__circle1Color == 1:
            self.__circle1Color = 0
        else:
            self.__circle1Color = 1

        # Set the color for circle 1
        self.circle1.setColor(self.__circleColors[self.__circle1Color])

        # force a repaint by updating the widget Using the repaint() method
        # could cause infinite recursion
        self.update()

    def circle2Meth(self):
        print("Circ2 change")
        if self.__circle2Color == 1:
            self.__circle2Color = 0
        else:
            self.__circle2Color = 1

        # Set the color for circle 1
        self.circle2.setColor(self.__circleColors[self.__circle2Color])

        # force a repaint by updating the widget Using the repaint() method
        # could cause infinite recursion
        self.update()

###
# Run GUI
###
app = QtGui.QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())

Upvotes: 1

Views: 1887

Answers (1)

eyllanesc
eyllanesc

Reputation: 243945

When you use addStrech() the layouts will use sizeHint() as the size, since the size is not set so it is not visible, the solution is to set that property. Another suggestion is that you do not make the QPainter a member of the class since it consumes memory in an unavoidable way, just make it a local variable. Another recommendation is that you call update() inside setColor() method and it does not save you code lines and you have a cleaner code.

class DrawCircle(QtGui.QWidget):
    def __init__(self, color, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.color = color

    def setColor(self,color):
        self.color = color
        self.update()

    def paintEvent(self,event):
        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        radx = 5
        rady = radx
        if self.color in ('red', 'green'):
            col = QtGui.QColor(self.color)
            painter.setPen(col)
            painter.setBrush(col)
        k = int(radx * 1.5)
        center = QtCore.QPoint(k,k)
        painter.drawEllipse(center,radx,rady)

    def sizeHint(self):
        return QtCore.QSize(15, 15)

Upvotes: 1

Related Questions