Issue
I am just trying my first prototype in 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. So I thought flashing LEDs would be fine for that. For this I try to implement a custom LED widget.
Remember that I currently try to learn python, so there might be some strange approaches in this. Anyway, here is the LED widgets class in its current state:
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. 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. Nevertheless here is the (shortened) instanciation if the widget:
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. 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. 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 !
Solution
One thing to be careful when implementing custom widgets derived from QWidget
is: sizeHint
or minimumSizeHint
for QWidget
returns invalid QSize
by default. This means, if it is added to a layout, depending on the other widgets, it will shrink to 0
. This effectively makes it 'not-visible'. Although, isVisible
would still return True
. Widget is 'visible', but it just doesn't have anything to show (0 size). So, if you're getting False
, there is definitely another issue at hand.
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. They won't break anything but they are pointless. painter.setBrush(QtCore.Qt.RadialGradientPattern)
is wrong. You can't create a brush withQtCore.Qt.RadialGradientPattern
. It is there, so thatbrush.style()
can return something. If you want a gradient pattern you should create a brush withQGradient
constructor.if self.mode is self.Mode.STATIC_OFF
: comparing withis
is wrong.is
compares identity, you want==
here. (also,FLASH_SLOW
andFLASH_MEDIUM
are both2
)assert
is for debugging and unit tests. You shouldn't use it in real code. Raise an exception if you want. Or have sensibledefault
s, where invalid values would be replaced with that.
Answered By - Avaris
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.