No Qt.UserRole in PyQt6 - qt

I'm trying to implement a basic progress bar using a delegate within a TreeView widget. I've double-checked the official documentation, and confirmed that the UserRole is supposed to be contained in PyQt6.QtCore.Qt. However, I always get the error upon trying to launch the code
AttributeError: type object 'Qt' has no attribute 'DisplayRole'
How can I solve this problem?
import sys
from PyQt6 import QtWidgets, QtGui
from PyQt6.QtCore import Qt
print(Qt.UserRole)
class ProgressBarDelegate(QtWidgets.QStyledItemDelegate):
def paint(self, painter, option, index):
# Get the data for the item
progress = index.data(Qt.UserRole)
# Draw the progress bar
painter.save()
rect = option.rect
rect.setWidth(int(rect.width() * progress))
painter.fillRect(rect, QtGui.QColor("#00c0ff"))
painter.restore()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
# Set up the tree view
self.tree_view = QtWidgets.QTreeView(self)
self.setCentralWidget(self.tree_view)
# Set up the model
self.model = QtGui.QStandardItemModel(self)
self.tree_view.setModel(self.model)
# Add some items to the model
for i in range(10):
item = QtGui.QStandardItem("Item {}".format(i))
progress = i / 10
item.setData(progress, Qt.UserRole)
self.model.appendRow(item)
# Set the delegate for the progress bar column
self.tree_view.setItemDelegateForColumn(1, ProgressBarDelegate(self))
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
Update:
I've tried Qt.ItemDataRole.UserRole, it didn't give the error above, but the progress bar didn't show up.

Related

How do I prevent double valueChanged events when I press the arrows in a QSpinbox?

Could be a either a Qt bug, or a bug in my app, I am hoping someone can help me diagnose this.
I am building a PyQt5 application with an interface built using qtdesigner and pyuic5.
I have to QSpinBox widgets in my window, which I connect to two methods like this:
self.img1IndexBox.valueChanged.connect(self.changeImage1)
self.img2IndexBox.valueChanged.connect(self.changeImage2)
Everything seems to work fine in the application, except for one thing: If I scroll the mouse over the spinbox, I can make it increment the value. If I change the value with text, all works fine. If I use keyboard arrows on the spinbox, it works fine. But if I click on either the up or down arrows from the spinbox, I get get two changeValue events, a double increment. Like I clicked twice. In fact, it even looks from the animation that it is creating an event for the downpress, and another when the button goes back up.
Could this be just a library bug, or what could be causing this in my program? How could I debug this?
You might be able to prevent that double thing by setting spinbox enable to false.
then make it enable to true after processing large data.
Upon clicking the arrow up/down
on_valuechanged
ui->spinbox->setEnabled(false);
then set to true before the function on_valuechanged ends.
ui->spinbox->setEnabled(true);
Apparently the problem is the event is triggering a very long routine, this delays the "button release" event, and it is enough time to make the system think the user is actually holding the button, generating more events... But I would still be interested in learning what would be a good walk-around. Would there be a nice pyqt-onic way to start a thread for that method?
http://www.qtcentre.org/archive/index.php/t-43078.html
Instead of using valueChanged.connect use editingFinished.connect it will make sure the function is called only after value is provided.
PyQt
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QDoubleSpinBox, QApplication, QMainWindow, QWidget, QVBoxLayout, QLabel, QSpinBox
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
# def valueChanged(self, value):
# print(value)
def valueChanged(self):
print(f"Value changed new value is : {self.spinBox.value()}")
def initUI(self):
self.setGeometry(0, 0, 200, 100)
self.layout = QVBoxLayout()
self.spinBox = QSpinBox()
self.spinBox.setAlignment(Qt.AlignCenter)
self.spinBox.setRange(0, 1000)
# self.spinBox.valueChanged.connect(self.valueChanged)
self.spinBox.editingFinished.connect(self.valueChanged)
self.layout.addWidget(self.spinBox)
self.setLayout(self.layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec())
PySide
import sys
from PySide6.QtWidgets import QDoubleSpinBox, QApplication, QMainWindow, QWidget, QVBoxLayout, QLabel, QSpinBox
from PySide6.QtGui import Qt
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
# def valueChanged(self, value):
# print(value)
def valueChanged(self):
print(f"Value changed new value is : {self.spinBox.value()}")
def initUI(self):
self.setGeometry(0, 0, 200, 100)
self.layout = QVBoxLayout()
self.spinBox = QSpinBox()
self.spinBox.setAlignment(Qt.AlignCenter)
self.spinBox.setRange(0, 1000)
# self.spinBox.valueChanged.connect(self.valueChanged)
self.spinBox.editingFinished.connect(self.valueChanged)
self.layout.addWidget(self.spinBox)
self.setLayout(self.layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec())

Restrict item in PyQt4‏ using itemChange()

I tried using the Qt documentation example to restrict the rectangle to the area of the scene but it still fails, someone has an alternative to do this?
My code, the QGraphicsView instance was created in Qt Desginer:
# -*- coding: utf-8 -*-
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
from screen import *
class MovableItem(QGraphicsRectItem):
def __init__(self, rectang, *args, **kwargs):
QGraphicsRectItem.__init__(self, rectang, *args, **kwargs)
self.setFlags(QGraphicsItem.ItemIsMovable |
QGraphicsItem.ItemSendsGeometryChanges)
self.pen = QPen(Qt.darkMagenta)
self.pen.setWidth(4)
self.setPen(self.pen)
def itemChange(self, change, value):
if change == QGraphicsItem.ItemPositionChange and self.scene():
# value is the new position.
self.newPos = value.toPointF()
self.rect = self.scene().sceneRect()
if not(self.rect.contains(self.newPos)):
# Keep the item inside the scene rect.
self.newPos.setX(min(self.rect.right(), max(self.newPos.x(), self.rect.left())))
self.newPos.setY(min(self.rect.bottom(), max(self.newPos.y(), self.rect.top())))
return self.newPos
return QGraphicsRectItem.itemChange(self, change, value)
class Main(QWidget, Ui_Form):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.setupUi(self)
self.scene = QGraphicsScene()
self.cena.setScene(self.scene)
self.scene.addPixmap(QPixmap("01.png"))
self. graph = MovableItem(2, 2, 300, 150)
self.scene.addItem(self.graph)
def showEvent(self, event):
self.cena.fitInView(self.scene.sceneRect(), Qt.IgnoreAspectRatio)
app = QApplication(sys.argv)
window = Main()
window.show()
sys.exit(app.exec_())
First:
Use setSceneRect() in your main Main(), to set the size of the scene.
Second:
Actually the example of the documentation is wrong, therefore, to adjust the rectangle to the scene, delete this if and subtract, in min, the parameters right and bottom by the rectangle dimensions right and bottom in setX and setY. Replace this part of your code:
if not(self.rect.contains(self.newPos)):
# Keep the item inside the scene rect.
self.newPos.setX(min(self.rect.right(), max(self.newPos.x(), self.rect.left())))
self.newPos.setY(min(self.rect.bottom(), max(self.newPos.y(), self.rect.top())))
return self.newPos
For:
self.newPos.setX(min(self.rect.right()-self.boundingRect().right(), max(self.newPos.x(), self.rect.left())))
self.newPos.setY(min(self.rect.bottom()-self.boundingRect().bottom(), max(self.newPos.y(), self.rect.top())))
return self.newPos

How to show clickable QFrame without loosing focus from main window?

Finally I am able to create a chrome like tab in Qt/PyQt QMainWindow. After unsuccessfully trying to port this Qt C++ non client area painting code, I revise my thinking to be this way : trick visually by displaying a free floating QFrame that get resized and moved together with main window. Surely this is not a perfect solution (for example this code still don't solve when to disable topmost hint where the another application is on top of the main application window, but I think that's quite easy to solve)
What I want to ask in this page, is how to keep the click action on this QFrame window button from stealing focus from main window? At the moment I simply reactivate the main window when click action does occur. But it creates flashing effect on the mainwindow titlebar. I believe this SO page gives the answer, but I haven't been able to create a successful result from this C++ code:
HWND winHandle = (HWND)winId();
ShowWindow(winHandle, SW_HIDE);
SetWindowLong(winHandle, GWL_EXSTYLE, GetWindowLong(winHandle, GWL_EXSTYLE)
| WS_EX_NOACTIVATE | WS_EX_APPWINDOW);
ShowWindow(winHandle, SW_SHOW);
Into this PyQt code:
def no_focus(self):
import ctypes, win32con, win32gui
dc = win32gui.GetWindowDC(self.winId())
user32 = ctypes.windll.user32
user32.SetWindowLongW(dc, win32con.GWL_EXSTYLE, user32.GetWindowLongW(dc, win32con.GWL_EXSTYLE) | win32con.WS_EX_NOACTIVATE | win32con.WS_EX_APPWINDOW)
Would love to let you see and test the fully functional code below:
__author__ = 'Eko Wibowo'
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class FSTabHeader(QFrame):
def __init__(self, parent):
super(FSTabHeader, self).__init__(None)
self.mainwindow = parent
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.SplashScreen)
self.setFocusPolicy(Qt.NoFocus)
self.setAttribute(Qt.WA_ShowWithoutActivating)
layout = QHBoxLayout(self)
layout.setSpacing(0)
layout.setContentsMargins(0, 0, 0, 0)
tab_text = 'Click me'
self.btn_tab = QPushButton(self)
self.btn_tab.setStyleSheet('border:1px')
self.btn_tab.setContentsMargins(0,0,0,0)
self.btn_tab.setText(tab_text)
self.btn_tab.setMinimumHeight(25 + 1)
self.btn_tab.setMaximumHeight(25 + 1)
self.btn_tab.setMinimumWidth(60)
self.btn_tab.setMaximumWidth(60)
self.btn_tab.setCursor(Qt.PointingHandCursor)
self.btn_tab.clicked.connect(self.dummy)
layout.addWidget(self.btn_tab)
self.setLayout(layout)
self.show()
def dummy(self):
print 'it create flashes effect on mainwindow titlebar'
self.mainwindow.activateWindow()
def no_focus(self):
import ctypes, win32con, win32gui
dc = win32gui.GetWindowDC(self.winId())
user32 = ctypes.windll.user32
user32.SetWindowLongW(dc, win32con.GWL_EXSTYLE, user32.GetWindowLongW(dc, win32con.GWL_EXSTYLE) | win32con.WS_EX_NOACTIVATE | win32con.WS_EX_APPWINDOW)
def adjust_position(self):
top_left = self.mainwindow.mapToGlobal(self.mainwindow.rect().topLeft())
self.move(top_left.x() + 20 + 5, top_left.y() - self.height() + 1)
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.tab_header = FSTabHeader(self)
self.tab_header.no_focus()
def resizeEvent(self, *args, **kwargs):
self.tab_header.adjust_position()
def moveEvent(self, *args, **kwargs):
self.tab_header.adjust_position()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
mainWindow = MainWindow(None)
mainWindow.show()
app.exec_()
Any suggestions?

PyQt: proper way to connect QTabWidget.tabCloseRequested to a slot

I've got a simple sample PyQt application with a QTabWidget. I can't connect QTabWidget's tabCloseRequested signal to a slot, so that the tab is closed properly:
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Application(object):
def __init__(self):
app = QApplication(sys.argv)
self.window = QMainWindow()
self.notebook = QTabWidget()
self.notebook.tabBar().setTabsClosable(True)
self.notebook.tabBar().setMovable(True)
self.notebook.tabCloseRequested.connect(self.close_handler)
self.window.setCentralWidget(self.notebook)
page1 = QWidget()
self.notebook.addTab(page1, "page1")
page2 = QWidget()
self.notebook.addTab(page2, "page2")
self.window.show()
sys.exit(app.exec_())
def close_handler(self, index):
print "close_handler called, index = %s" % index
self.notebook.removeTab(index)
if __name__ == "__main__":
app = Application()
When I click on the close button, nothing happens. Not even the print, which should be invoked! What am I doing wrong?
You need to call setTabsClosable(True) on the tab-widget, rather than its tab-bar:
self.notebook.setTabsClosable(True)
(PS: the close_handler method is also missing a self argument).

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.

Resources