简体   繁体   English

pyside(python / Qt)中的自定义小部件不会显示

[英]Custom widget in pyside (python/Qt) won't show up

I am just trying my first prototype in pyside (python/Qt). 我只是在pyside(python / Qt)中尝试我的第一个原型。 The application itself starts up fine, creates a window with widgets according to my layout. 应用程序本身启动正常,根据我的布局创建一个带小部件的窗口。 Threads are started and execute, all fine. 线程启动并执行,一切正常。 Except... 除了...

I want to enhance the GUI by adding some custom widget indicating the execution state of the threads. 我想通过添加一些指示线程执行状态的自定义窗口小部件来增强GUI。 So I thought flashing LEDs would be fine for that. 所以我认为闪烁的LED会很好。 For this I try to implement a custom LED widget. 为此,我尝试实现自定义LED小部件。

Remember that I currently try to learn python, so there might be some strange approaches in this. 请记住,我目前正在尝试学习 python,因此可能会有一些奇怪的方法。 Anyway, here is the LED widgets class in its current state: 无论如何,这是目前状态下的LED小部件类:

from PySide import QtCore, QtGui

class LED(QtGui.QWidget):

    class Mode:
        STATIC_OFF   = 0
        STATIC_ON    = 1
        FLASH_SLOW   = 2
        FLASH_MEDIUM = 2
        FLASH_FAST   = 3

    class Color:
        BLACK  = '#000000'
        GREEN  = '#00FF00'
        RED    = '#FF0000'
        BLUE   = '#0000FF'
        YELLOW = '#FFFF00' 
        WHITE  = '#FFFFFF'

    mode   = Mode.STATIC_ON
    color  = Color.BLACK
    radius = 10
    status = False
    timer  = None

    outdated = QtCore.Signal()

    def __init__(self, mode, color, radius, parent=None):
        super(LED, self).__init__(parent)
        self.outdated.connect(self.update)
        self.setMode(mode,False)
        self.setColor(color,False)
        self.setRadius(radius,False)
        self.timer = QtCore.QTimer(self)
        self.timer.timeout.connect(self.adjustAppearance)
        self.adjustAppearance()

    def getCenter(self):
        return QtCore.QPoint(self.radius, self.radius)

    def getBox(self):
        return QtCore.QRect(self.radius, self.radius)

    def setColor(self, color, update=True):
        assert color in (self.Color.GREEN,self.Color.RED,self.Color.BLUE,self.Color.YELLOW,self.Color.WHITE), "invalid color"
        self.color = color
        if update:
            self.adjustAppearance()

    def setMode(self, mode, update=True):
        assert mode in (self.Mode.STATIC_OFF,self.Mode.STATIC_ON,self.Mode.FLASH_SLOW,self.Mode.FLASH_MEDIUM,self.Mode.FLASH_FAST),"invalid mode"
        self.mode = mode
        if update:
            self.adjustAppearance()

    def setRadius(self, radius, update=True):
        assert isinstance(radius, int), "invalid radius type (integer)"
        assert 10<=radius<=100, "invalid radius value (0-100)"
        self.radius = radius
        if update:
            self.adjustAppearance()

    def switchOn(self):
        self.status = True
        self.adjustAppearance()

    def switchOff(self):
        self.status = False
        self.adjustAppearance()

    def adjustAppearance(self):
        if self.mode is self.Mode.STATIC_OFF:
            self.status = False
            self.timer.stop()
        elif self.mode is self.Mode.STATIC_ON:
            self.status = True
            self.timer.stop()
        elif self.mode is self.Mode.FLASH_SLOW:
            self.status = not self.status
            self.timer.start(200)
        elif self.mode is self.Mode.FLASH_SLOW:
            self.status = not self.status
            self.timer.start(500)
        elif self.mode is self.Mode.FLASH_SLOW:
            self.status = not self.status
            self.timer.start(1000)
            self.outdated.emit()

    def paintEvent(self, event):
        painter = QtGui.QPainter()
        painter.begin(self)
        self.drawWidget(event, painter)
        painter.end()

    def drawWidget(self, event, painter):
        if self.status:
            shade = QtGui.QColor(self.color).darker
        else:
            shade = QtGui.QColor(self.color).lighter
        #painter.setPen(QtGui.QColor('black'), 1, QtCore.Qt.SolidLine)
        painter.setPen(QtGui.QColor('black'))
        painter.setBrush(QtCore.Qt.RadialGradientPattern)
        painter.drawEllipse(self.getCenter(), self.radius, self.radius)

My problem is that the widget simply does not show when I add it to the windows layout. 我的问题是,当我将它添加到Windows布局时,小部件根本不会显示。 Other widgets (non-custome, plain Qt widgets) do show, so I gues it is not a question of creating the widget, not a question of how I use the widget. 其他小部件(非客户,普通Qt小部件)确实显示,所以我猜这不是创建小部件的问题,而不是我如何使用小部件的问题。 Nevertheless here is the (shortened) instanciation if the widget: 然而,如果小部件,这里是(缩短的)instanciation:

class View(QtGui.QMainWindow):

    ui = None

    def __init__(self, config, parent=None):
        log.debug("window setup")
        self.config = config
        super(View, self).__init__(parent)
        try:
            self.ui = self.Ui(self)
            self.setObjectName("appView")
            self.setWindowTitle("AvaTalk")
            self.show()
        except RuntimeError as e:
            log.error(e.message)

    class Ui(QtCore.QObject):

        # [...]
        iconDetector = None
        buttonDetector = None
        # [...]

        def __init__(self, window, parent=None):
            log.debug("ui setup")
            super(View.Ui, self).__init__(parent)
            self.window = window

            # central widget
            log.debug("widget setup")
            self.centralWidget = QtGui.QWidget()
            self.widgetLayout = QtGui.QVBoxLayout(self.centralWidget)

            # create toolbars
            #self.createMenubar()
            #self.createCanvas()
            self.createToolbar()
            #self.createStatusbar()

            # visualize widget
            self.window.setCentralWidget(self.centralWidget)

            # actions
            log.debug("actions setup")
            self.actionQuit = QtGui.QAction(self.window)
            self.actionQuit.setObjectName("actionQuit")
            self.menuFile.addAction(self.actionQuit)
            self.menubar.addAction(self.menuFile.menuAction())

            log.debug("connections setup")
            QtCore.QObject.connect(self.actionQuit, QtCore.SIGNAL("activated()"), self.window.close)
            QtCore.QMetaObject.connectSlotsByName(self.window)

        def createToolbar(self):
            log.debug("toolbar setup")
            self.toolbar = QtGui.QHBoxLayout()
            self.toolbar.setObjectName("toolbar")
            self.toolbar.addStretch(1)
            # camera
            # detector
            self.iconDetector = LED(LED.Mode.STATIC_OFF,LED.Color.GREEN,10,self.window)
            self.buttonDetector = IconButton("Detector", "detector",self.window)
            self.toolbar.addWidget(self.iconDetector)
            self.toolbar.addWidget(self.buttonDetector)
            self.toolbar.addStretch(1)
            # analyzer
            # extractor
            # layout
            self.widgetLayout.addLayout(self.toolbar)

It might well be that the actual painting using QPainter is still nonsense. 很可能是使用QPainter的实际绘画仍然是无稽之谈。 I did not yet come to test that: actually when testing I find that isVisible() returns False on the widget after the setup has completed. 我还没有测试过:实际上在测试时我发现isVisible()在设置完成后在窗口小部件上返回False So I assume I miss a central point. 所以我想我错过了一个中心点。 Unfortunately I am unable to find out what I miss... 不幸的是我无法找到我想念的东西......

Maybe someone can spot my issue? 也许有人可以发现我的问题? Thanks ! 谢谢 !

One thing to be careful when implementing custom widgets derived from QWidget is: sizeHint or minimumSizeHint for QWidget returns invalid QSize by default. 有一两件事要实现源自自定义部件时要小心QWidget是: sizeHintminimumSizeHintQWidget返回无效QSize默认。 This means, if it is added to a layout, depending on the other widgets, it will shrink to 0 . 这意味着,如果将其添加到布局中,则根据其他窗口小部件,它将缩小为0 This effectively makes it 'not-visible'. 这实际上使它“不可见”。 Although, isVisible would still return True . 虽然, isVisible仍会返回True Widget is 'visible', but it just doesn't have anything to show (0 size). 窗口小部件是“可见的”,但它没有任何要显示的内容(0大小)。 So, if you're getting False , there is definitely another issue at hand. 所以,如果你得到False ,那肯定还有另一个问题。

So it is necessary to define these two methods with sensible sizes: 因此有必要使用合理的大小来定义这两种方法:

class LED(QtGui.QWidget):
    # ...

    def sizeHint(self):
        size = 2 * self.radius + 2
        return QtCore.QSize(size, size)

    def minimumSizeHint(self):
        size = 2 * self.radius + 2
        return QtCore.QSize(size, size)

Note: There are other issues: 注意:还有其他问题:

  • Like defining mode , color , etc as class attributes and then overriding them with instance attributes. 像定义modecolor等作为类属性,然后用实例属性覆盖它们。 They won't break anything but they are pointless. 它们不会破坏任何东西,但它们毫无意义。
  • painter.setBrush(QtCore.Qt.RadialGradientPattern) is wrong. painter.setBrush(QtCore.Qt.RadialGradientPattern)是错误的。 You can't create a brush with QtCore.Qt.RadialGradientPattern . 您无法使用QtCore.Qt.RadialGradientPattern创建画笔。 It is there, so that brush.style() can return something. 它就在那里,所以brush.style()可以返回一些东西。 If you want a gradient pattern you should create a brush with QGradient constructor . 如果你想要一个渐变图案,你应该用QGradient构造函数创建一个画笔。
  • if self.mode is self.Mode.STATIC_OFF : comparing with is is wrong. if self.mode is self.Mode.STATIC_OFF :与is比较是错误的。 is compares identity, you want == here. is比较认同,你要==这里。 (also, FLASH_SLOW and FLASH_MEDIUM are both 2 ) (另外, FLASH_SLOWFLASH_MEDIUM都是2
  • assert is for debugging and unit tests. assert用于调试和单元测试。 You shouldn't use it in real code. 你不应该在实际代码中使用它。 Raise an exception if you want. 如果你愿意,可以提出异常。 Or have sensible default s, where invalid values would be replaced with that. 或者有合理的default值,其中无效值将被替换。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM