Why do stylesheets influence QTableWidget behaviour? - python

I use python2.7 and PyQt4. I created a simple app with a button-box and a table-widget. If I edit a table cell and press the Ok button, the cell editor always disappears. But after I add the app.setStyleSheet(s) line, the cell editor does not disappear after the OK button is pressed. What is going on?
import sys
from PyQt4 import QtGui
class Widget(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
l = QtGui.QVBoxLayout(self)
table = QtGui.QTableWidget()
table.setColumnCount(3)
table.setRowCount(5)
l.addWidget(table)
l.addWidget(QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel))
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
s = "QWidget{background:red;}"
# app.setStyleSheet(s)
app.setStyleSheet(s)
mw = QtGui.QMainWindow()
w = Widget()
mw.setCentralWidget(w)
mw.show()
sys.exit(app.exec_())

This seems to be a bug in Qt4, but I can't find a specific report for it. The same code works as expected when using PyQt5, so it somehow got fixed at some point.
The bug is actually in QDialogButtonBox rather than QTableWidget. If you add a QLineEdit to the example and set focus on it, you will see that clicking on the Ok button does not switch focus. Or to be more precise, clicking on the first button does not switch focus. All the other buttons in the button-box work normally.
I thought that this might have something to do with the default and/or autoDefault properties, because they usually change the way buttons are styled (e.g. a highlighted border). But setting the properties doesn't have any effect - the bug really does seem to affect just the first button.

Related

PySide6 QDockWidget Shadow disappears while using setTitleBarWidget [duplicate]

I'm implementing a custom widget to use it as a title bar on a dockable window. My problem arises only on Windows, namely, the window border disappears when the dock window is afloat.
Seems the problem is that, on Windows only, the window flags are changed. I.e. when I do this:
print dock_window.windowFlags()
dock_window.setTitleBarWidget(title_bar)
print dock_window.windowFlags()
it prints out different setting for the flags before and after.
However, it stays the same on linux and the borders remain unchanged.
My question is, how to restore the window border?
UPDATE: Since the custom title bar overrides the flags for the border when the dock window is floating, how can I edit the dock window so it has some kind of border?
(It is crucial for the dock window to have a custom title bar when floating.)
According to this answer this is expected behavior.
From the documentation of setTitleBarWidget:
If a title bar widget is set, QDockWidget will not use native window
decorations when it is floated.
So Linux does it the wrong way then?
Anyway as a workaround for Windows I implemented the idea (unsetting the title bar widget before floating) from the answer in PySide/PyQt.
from PySide import QtGui, QtCore
class MyDockWidget(QtGui.QDockWidget):
def __init__(self, title_widget):
super().__init__()
self.title_widget = title_widget
self.toggle_title_widget(False)
self.topLevelChanged.connect(self.toggle_title_widget)
def toggle_title_widget(self, off):
if off:
self.setTitleBarWidget(None)
else:
self.setTitleBarWidget(self.title_widget)
app = QtGui.QApplication([])
w = QtGui.QMainWindow()
t = QtGui.QLabel('Title')
d = MyDockWidget(t)
w.addDockWidget(QtCore.Qt.LeftDockWidgetArea, d)
w.show()
app.exec_()
At least it keeps the standard decoration when floating.
I found this to be an unresolved bug in QT and I don't see this as expected behavior.
I found multiple cases of people stumbling on this issue eg1, eg2 and others.
Some recommend unsetting and setting the setTitleBarWidget as in Trilarion's answer.
This, however, removes the custom title bar and was not ok with me.
others recommend setting flags on topLevelChanged event: window.setWindowFlags(Qt::Window | Qt::FramelessWindowHint);. this adds the usual title bar to the dock widget, which again is not what I personally want.
the best solution I found is w->setWindowFlags(Qt::Tool|Qt::CustomizeWindowHint);. This uses Qt.CustomizeWindowHint instead of Qt.FramelessWindowHint and does not produce a huge title bar, merely a small bar.
Implementation
from PyQt5.QtCore import Qt
....
def dockfloatevent(isfloating):
if isfloating:
dock.setWindowFlags(Qt.Tool | Qt.CustomizeWindowHint)
dock.topLevelChanged.connect(dockfloatevent)
I am not using the most up to date Qt, but from what I can tell this is still an issue? If someone has a Qt account maybe post something to the above bug link? I have already wasted many hours on this and don't feel like pushing it further :|

PyQt setText not rendering text properly in MacOs

I am creating an application using PyQt5 (pythyon 3.7 , MacOs X)
When I modify the text in a textbox using the instruction
self.line_main.setText(final_text)
from a function (which is connected to a push button)
The new text does not render properly in the textbox (see screenshot) and both the old and new text are overlapping in a strange way.
An over-simplified code to illustrate the problem is this one:
import sys
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QMainWindow, QWidget, QLabel, QLineEdit
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtCore import QSize
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setMinimumSize(QSize(200, 200))
self.setWindowTitle("PyQt test")
self.line_main = QLineEdit(self)
self.line_main.move(20,20)
bt_upperCase = QPushButton('Upper Case', self)
bt_upperCase.move(20, 60)
bt_upperCase.clicked.connect(self.click_upCase)
def click_upCase(self):
final_text=self.line_main.text().upper()
self.line_main.setText(final_text)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit( app.exec_() )
screenshot of mini-app, with 're' as input as 'RE' as output
The same thing may happen for labels, although at times, only the old text is visible, and I need to select the text with the mouse or resize the main window to 'refresh' the textbox and see the new value.
The problem does not happen if I run the code in a PC, only in MacOs (tested in High Sierra and Mojave with identical results)
Some weird behaviours are:
If position the textbox at the (0,0) position in the main window, which is not very practical, then the setText functions correctly. In some other locations of the box, too, but only if it is very close to the top-left corner, and no other element is near.
If I modify the textbox value at the initialization of the main window, then it renders correctly in all cases, The problem appears when you try to do it from a function (after clicking a button, for instance), like in the code attached.
The problem does not happen if I run the code in a PC, only in MacOs (tested in High Sierra and Mojave with identical results)
If position the textbox at the (0,0) position in the main window, (which is not very practical), then the setText functions correctly. In some other locations of the box, too, but only if it is very close to the top-left corner, and no other element is near.
Does anyone have a guess why this may happen, and how could it be solved?
UPDATE.
The problem dissapears if i run the code with python 3.6 in a conda installation (PyQt version 5.9.2). Still very intrigued to know what caused the problem.

pyqt. how to add and delete widgets?

i use python 2.7 + qt4.8
how to dynamically change the number of widgets in the window? I need to remove all the widgets and create new ones in the right quantity. testarovaniya made for a simple script:
import sys
from PyQt4 import QtCore, QtGui, uic
class MainForm(QtGui.QDialog):
def __init__(self):
super(MainForm, self).__init__()
uic.loadUi("testQTwindow.ui", self)
self.connect(self.addBtn, QtCore.SIGNAL("clicked()"), self.addBtnClick)
self.connect(self.delBtn, QtCore.SIGNAL("clicked()"), self.delBtnClick)
def addBtnClick(self):
self.tempBtn = QtGui.QPushButton('button')
self.gridLayout_2.addWidget(self.tempBtn)
def delBtnClick(self):
while True:
item = self.gridLayout_2.takeAt(0)
if not item:
break
self.gridLayout_2.removeWidget(item.widget())
app = QtGui.QApplication(sys.argv)
form = MainForm()
form.show()
sys.exit(app.exec_())
and load this UI: https://yadi.sk/d/jBOmSubYhqbjm
I have two buttons. One for adding buttons to QScrollArea with gridLayout. And the second to remove all the widgets in the QScrollArea. Adding works. I can see how there are new buttons. But when you press the cleaning button does not disappear, and new ones continue to appear over the old ones. The old button can also be pressed, which suggests that they work, and not just the ghosts that are cleaned redrawing window.
I try repaint() and update() functions - but it has no effect...
This is a simple example, but even he is not working. And I do not need to add a button in the future, and whole blocks with a bunch of elements.
How to add and remove widgets dynamically?
This part of the loop should be enough:
while True:
item = self.gridLayout_2.takeAt(0)
I suspect you are attempting to delete widgets you already removed, and so prematurely ending your loop. There may have been an error message written somewhere.

Setting custom title bar widget changes the windows flags on Windows

I'm implementing a custom widget to use it as a title bar on a dockable window. My problem arises only on Windows, namely, the window border disappears when the dock window is afloat.
Seems the problem is that, on Windows only, the window flags are changed. I.e. when I do this:
print dock_window.windowFlags()
dock_window.setTitleBarWidget(title_bar)
print dock_window.windowFlags()
it prints out different setting for the flags before and after.
However, it stays the same on linux and the borders remain unchanged.
My question is, how to restore the window border?
UPDATE: Since the custom title bar overrides the flags for the border when the dock window is floating, how can I edit the dock window so it has some kind of border?
(It is crucial for the dock window to have a custom title bar when floating.)
According to this answer this is expected behavior.
From the documentation of setTitleBarWidget:
If a title bar widget is set, QDockWidget will not use native window
decorations when it is floated.
So Linux does it the wrong way then?
Anyway as a workaround for Windows I implemented the idea (unsetting the title bar widget before floating) from the answer in PySide/PyQt.
from PySide import QtGui, QtCore
class MyDockWidget(QtGui.QDockWidget):
def __init__(self, title_widget):
super().__init__()
self.title_widget = title_widget
self.toggle_title_widget(False)
self.topLevelChanged.connect(self.toggle_title_widget)
def toggle_title_widget(self, off):
if off:
self.setTitleBarWidget(None)
else:
self.setTitleBarWidget(self.title_widget)
app = QtGui.QApplication([])
w = QtGui.QMainWindow()
t = QtGui.QLabel('Title')
d = MyDockWidget(t)
w.addDockWidget(QtCore.Qt.LeftDockWidgetArea, d)
w.show()
app.exec_()
At least it keeps the standard decoration when floating.
I found this to be an unresolved bug in QT and I don't see this as expected behavior.
I found multiple cases of people stumbling on this issue eg1, eg2 and others.
Some recommend unsetting and setting the setTitleBarWidget as in Trilarion's answer.
This, however, removes the custom title bar and was not ok with me.
others recommend setting flags on topLevelChanged event: window.setWindowFlags(Qt::Window | Qt::FramelessWindowHint);. this adds the usual title bar to the dock widget, which again is not what I personally want.
the best solution I found is w->setWindowFlags(Qt::Tool|Qt::CustomizeWindowHint);. This uses Qt.CustomizeWindowHint instead of Qt.FramelessWindowHint and does not produce a huge title bar, merely a small bar.
Implementation
from PyQt5.QtCore import Qt
....
def dockfloatevent(isfloating):
if isfloating:
dock.setWindowFlags(Qt.Tool | Qt.CustomizeWindowHint)
dock.topLevelChanged.connect(dockfloatevent)
I am not using the most up to date Qt, but from what I can tell this is still an issue? If someone has a Qt account maybe post something to the above bug link? I have already wasted many hours on this and don't feel like pushing it further :|

PySide: Removing a widget from a layout

I'm trying to remove a Qt widget from a layout in a PySide application.
Here is a minimal example. It is a widget with 5 buttons in it, and the middle one is supposed to remove itself when clicked:
import sys
from PySide import QtGui
app = QtGui.QApplication(sys.argv)
widget = QtGui.QWidget()
layout = QtGui.QVBoxLayout()
buttons = [QtGui.QPushButton(str(x)) for x in xrange(5)]
def deleteButton():
b = layout.takeAt(2)
buttons.pop(2)
del b
buttons[2].clicked.connect(deleteButton)
map(layout.addWidget, buttons)
widget.setLayout(layout)
widget.show()
app.exec_()
What actually happens is this:
The button is unclickable and clearly isn't taken into consideration for the layout computations, but its image stays in place.
According to the Qt documentation, the correct way of deleting all objects from a layout is:
while ((child = layout->takeAt(0)) != 0) {
delete child;
}
Here I just want to delete the third button, so I just call takeAt(2), and then del b to call the destructor on that item. The button object is also .pop'd from the buttons list to make sure there is no leftover reference to the object. How does my code differ from the one in the Qt docs that would cause such a behavior?
Super simple fix:
def deleteButton():
b = layout.takeAt(2)
buttons.pop(2)
b.widget().deleteLater()
You first have to make sure you are addressing the actual button and not the QWidgetItem that is returned from the layout, and then call deleteLater() which will tell Qt to destroy the widget after this slot ends and control returns to the event loop.
Another example illustrates why the problem is occurring. Even though you take the layout item, the underlying widget is still parented to the original layouts widget.
def deleteButton():
b = layout.takeAt(2)
buttons.pop(2)
w = b.widget()
w.setParent(None)
This is not the preferred way, as it still leaves the cleanup of the object ambiguous. But it shows that clearing the parent allows it to leave the visual display. Use deleteLater() though. It properly cleans everything up.
The answer that 'jdi' provided is valid, although If anyone is interested, I tried implementing what is suggested in the Qt Documentation with the loop of every child Widget, and I got the following code working in Python PySide6:
def delete():
while ((child := layout.takeAt(0)) != None):
child.widget().deleteLater()

Categories

Resources