Painting FocusRect with QStylePainter - qt

I am trying to make my own widget that uses QStyle options in order to give it a "native" look and feel.
Step 1 would be drawing a simple FocusRect, which I tried to accomplish like that:
import sys
from PyQt5 import QtWidgets, QtGui
class MyWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
def paintEvent(self, event):
painter = QtWidgets.QStylePainter(self)
option = QtWidgets.QStyleOptionFocusRect()
option.initFrom(self)
option.backgroundColor = self.palette().color(QtGui.QPalette.Background)
painter.drawPrimitive(QtWidgets.QStyle.PE_FrameFocusRect, option)
if __name__ == '__main__':
qApp = QtWidgets.QApplication(sys.argv)
qApp.setStyle('fusion')
window = QtWidgets.QMainWindow()
window.widget = MyWidget(window)
window.widget.resize(100, 100)
window.widget.move(50, 50)
window.setFixedSize(200, 200)
window.show()
sys.exit(qApp.exec_())
Unfortunately this only gives me an empty window. What am I missing?

Turns out that the FrameFocusRect is only painted, when it actually has the focus.
Adding option.state |= QtWidgets.QStyle.State_KeyboardFocusChange in the paintEvent method solves the problem.

Related

I can't see QLabel and QLineEdit widgets in my QMainWindow in Python2

I wrote this code and I don't understand why widgets QLabel and QLineEdit don't show up? Do I have to put them in another class? It's Python2.7 and PySide.
This is how a window looks like when I run the code:
#!/usr/bin/env python
# coding: utf-8
import sys
import crypt
from PySide import QtGui
class MyApp(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MyApp, self).__init__(parent)
self.initui()
def initui(self):
# main window size, title and icon
self.setMinimumSize(500, 350)
self.setWindowTitle("Calculate a password hash in Linux")
# lines for entering data
self.saltLabel = QtGui.QLabel("Salt:")
self.saltLine = QtGui.QLineEdit()
self.saltLine.setPlaceholderText("e.g. $6$xxxxxxxx")
# set layout
grid = QtGui.QGridLayout()
grid.addWidget(self.saltLabel, 0, 0)
grid.addWidget(self.saltLine, 1, 0)
self.setLayout(grid)
# show a widget
self.show()
def main():
app = QtGui.QApplication(sys.argv)
instance = MyApp()
instance.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
How about using a QWidget as centralWidget
widget = QWidget()
widget.setLayout(grid)
#add your widgets and...
self.setCentralWidget(widget)
and you don't need to call show() since you do in your __main__
It's up to the owner but i would recommend sublassing a QWidget and leave your QMainWindow instance as concise as possible. An implementation could be:
class MyWidget(QtGui.QWidget):
def __init__(self, *args):
QtGui.QWidget.__init__(self, *args)
grid = QtGui.QGridLayout()
#and so on...
and use this as widget in your QMainWindow instance. This increases readability and maintainability and reusability a lot :)

Why does clicking a QToolButton generate a leaveEvent? Is there a workaround?

For some reason, whenever the menu-button part of the QToolButton gets clicked, it generates a momentary leaveEvent (at least when it is in a toolbar). I even tested underMouse() in the leaveEvent, and it returns false. Why is this? Is there a way to fix this?
Sample for testing (Py3.3, change super() for py2.7):
from PyQt4.QtGui import *
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
toolbar = QToolBar(self)
toolbar.addWidget(ToolButton())
class ToolButton(QToolButton):
def __init__(self, parent=None):
super().__init__(parent)
self.setText('test')
self.setPopupMode(QToolButton.MenuButtonPopup)
self.setMenu(QMenu())
self.menu().addAction('Stub')
def enterEvent(self, event):
print('entered')
super().enterEvent(event)
def leaveEvent(self, event):
print('left')
super().leaveEvent(event)
if __name__ == '__main__':
import sys
application = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(application.exec_())
The following can be used to double check; unlike leaveEvent, it always returns the correct information:
def leaveEvent(self, event):
if not QApplication.widgetAt(QCursor().pos()) is self:
#do stuff
super().leaveEvent(event)

make the QMessageBox or QmainWindow in front of any overlapping sibling widgets?

If the active window belongs to some other process,how to make the QMessageBox or QmainWindow of this example in front of any overlapping sibling widgets when timeout ?
I tried raise_() and activateWindow() ,but both don’t work on WinXP
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class MainWindow(QWidget):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.resize(800,600)
self.lcdNumber = QLCDNumber()
self.lcdNumber.setNumDigits(8)
layout = QVBoxLayout(self)
layout.addWidget(self.lcdNumber)
self.currentTime = QTime(0,0,0)
self.lcdNumber.display(self.currentTime.toString('hh:mm:ss'))
self.timer = QTimer(self)
self.timer.timeout.connect(self.updateLcdNumberContent)
self.timer.start(1000)
def updateLcdNumberContent(self):
self.currentTime = self.currentTime.addSecs(1)
self.lcdNumber.display(self.currentTime.toString('hh:mm:ss'))
if self.currentTime == QTime(0,0,4) :
msgBox = QMessageBox(self)
msgBox.setWindowTitle('iTimer')
msgBox.setIcon (QMessageBox.Information)
msgBox.setText("Time Out !!")
stopButton = msgBox.addButton("Stop", QMessageBox.ActionRole)
ignoreButton = msgBox.addButton(QMessageBox.Ignore)
stopButton.clicked.connect(self.timer.stop)
msgBox.show()
# self.raise_()
# self.activateWindow()
if __name__ == '__main__':
app =QApplication(sys.argv)
frame = MainWindow()
frame.show()
sys.exit(app.exec_())
Try to modify window flags by using QWidget::setWindowFlags() method of your QMessageBox or QMainWindow. You should use Qt::WindowStaysOnTopHint flag for your purpose.
It will be something like window->setWindowFlags(window->windowFlags() | Qt::WindowStaysOnTopHint).
If you will not succeed with just setWindowFlags(window->windowFlags() | Qt::WindowStaysOnTopHint), you will need to use Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint with another flags. Experiment with it and you'll succeed.

Python code to change displayed image with pyqt4 on request

I have the following code to display am image using pyQt:
app = QtGui.QApplication(sys.argv)
window = QtGui.QMainWindow()
window.setGeometry(opts.posx, opts.posy, opts.width, opts.height)
pic = QtGui.QLabel(window)
pic.setGeometry(5, 5, opts.width-10, opts.height-10)
pixmap = QtGui.QPixmap(opts.filename)
pixmap = pixmap.scaledToHeight(opts.height)
pic.setPixmap(pixmap)
window.show()
sys.exit(app.exec_())
I would like to wrap up this code possibly in the form of a class, and be able to set a different image during runtime, using signals, socket, threads I really do not know. I would imagine something like:
class MyImage(object):
def __init(self, args):
some setup code
self.pic = whatever
def set_image(self, filename):
pixmap = QtGui.QPixmap(opts.filename)
pixmap = pixmap.scaledToHeight(opts.height)
pic.setPixmap(pixmap)
With the original code I just call sys.exit(app.exec_()) which makes the code 'freeze'. But I want to send a signal (and a filename) from a different running python code. Any suggestion how this can be handled easily and straightforward? Maybe overwriting the app.exec_ method?
Something like this should work for you:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import sip
sip.setapi('QString', 2)
sip.setapi('QVariant', 2)
from PyQt4 import QtGui, QtCore
class ImageChanger(QtGui.QWidget):
def __init__(self, images, parent=None):
super(ImageChanger, self).__init__(parent)
self.comboBox = QtGui.QComboBox(self)
self.comboBox.addItems(images)
self.layout = QtGui.QVBoxLayout(self)
self.layout.addWidget(self.comboBox)
class MyWindow(QtGui.QWidget):
def __init__(self, images, parent=None):
super(MyWindow, self).__init__(parent)
self.label = QtGui.QLabel(self)
self.imageChanger = ImageChanger(images)
self.imageChanger.move(self.imageChanger.pos().y(), self.imageChanger.pos().x() + 100)
self.imageChanger.show()
self.imageChanger.comboBox.currentIndexChanged[str].connect(self.changeImage)
self.layout = QtGui.QVBoxLayout(self)
self.layout.addWidget(self.label)
#QtCore.pyqtSlot(str)
def changeImage(self, pathToImage):
pixmap = QtGui.QPixmap(pathToImage)
self.label.setPixmap(pixmap)
if __name__ == "__main__":
import sys
images = [ "/path/to/image/1",
"/path/to/image/2",
"/path/to/image/3",
]
app = QtGui.QApplication(sys.argv)
app.setApplicationName('MyWindow')
main = MyWindow(images)
main.show()
sys.exit(app.exec_())

Disable sorting of child items in QTreeView

I am using pyQt. How can I disable child items sorting in QTreeView/StandardItemModel?
You could use a QSortFilterProxyModel and reimplement its lessThan method.
Alternatively, create a subclass of QStandardItem and reimplement its less than operator.
Here's a simple example that demonstrates the latter approach:
from random import sample
from PyQt4 import QtGui, QtCore
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.view = QtGui.QTreeView(self)
self.view.setHeaderHidden(True)
self.model = QtGui.QStandardItemModel(self.view)
self.view.setModel(self.model)
parent = self.model.invisibleRootItem()
keys = range(65, 91)
for key in sample(keys, 10):
item = StandardItem('Item %s' % chr(key), False)
parent.appendRow(item)
for key in sample(keys, 10):
item.appendRow(StandardItem('Child %s' % chr(key)))
self.view.sortByColumn(0, QtCore.Qt.AscendingOrder)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.view)
class StandardItem(QtGui.QStandardItem):
def __init__(self, text, sortable=True):
QtGui.QStandardItem.__init__(self, text)
self.sortable = sortable
def __lt__(self, other):
if getattr(self.parent(), 'sortable', True):
return QtGui.QStandardItem.__lt__(self, other)
return False
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
Call setSortingEnabled(bool) on your QTreeView instance. Here is the corresponding docu for c++ and here is the link to pyqt api docu for this function

Resources