Prevent QMainWindow from staying on top on Windows - python

I have a problem with a QMainWindow initialized with another QMainWindow as parent.
On Debian 8 (PyQt4), it behaves as I want it to: the child QMainWindow can go underneath the other one.
On Windows (PyQt5), however, the child window wants to stay on top.
Here is piece of code to reproduce the issue:
from silx.gui import qt
print(qt.PYQT_VERSION_STR)
app = qt.QApplication([])
mw1 = qt.QMainWindow()
mw1.show()
mw1.setWindowTitle("1")
mw2 = qt.QMainWindow(mw1)
mw2.show()
mw2.setWindowTitle("2")
app.exec_()
Is there a way to tell a QMainWindow to not stay on top of its parent? A flag ?

Related

PyQt5 MainWindow with flag instantly goes out of scope

I created UI using Qt Designer. Then I converted the ui file to a .py file (pyuic -x) - it works fine if launched directly. Then I tried to subclass my ui in a separate file to implement additional logic. And this is where things start to go wrong. Inheriting from QMainWindow and my Qt Designer file works OK with no issues, as expected. However, the moment I set any WindowFlag for my QMainWindow (any flag - I tried these: StaysOnTop, FramelessWindowHint) and run the file, the window appears and instantly disappears. The program continues to run in a console window, or as a PyCharm process, but the window is gone. It looks to me like it is getting out of scope - but why setting a simple flag would make any difference to the garbage collector? Could someone explain this behaviour?
Minimum code required to reproduce this phenomenon:
from ui import Ui_MainWindow
from PyQt5 import QtCore, QtWidgets, QtGui
import sys
class Logic(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.setupUi(self)
self.show()
# self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
# self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WA_NoSystemBackground, True)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground, True)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = Logic()
sys.exit(app.exec_())
The window should appear and stay on the screen until one (or more) of the flags are uncommented. I use Python 3.8 (32-bit) with PyQt5. Run environment provided by PyCharm. Windows 10.
From the documentation of setWindowFlags():
Note: This function calls setParent() when changing the flags for a window, causing the widget to be hidden. You must call show() to make the widget visible again..
So, just move self.show() after setting the flags, or call it from outside the __init__ (after the instance is created), which is the most common and suggested way to do so, as it's considered good practice to show a widget only after it has been instanciated.

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.

Qt application blinking at startup because of drawing text in a custom paint event

I discovered a very strange behavior which causes blinking (white rectangle is shown as the main window for about half a second) at application start up in some cases. After a long time of test and trial I narrowed down the problem to the situation when a text is first drawn in paint event of my custom widget. If a text is drawn also in another widget such as QLabel, the problem is gone. But my application main window only has tool buttons with icons and a custom widget which draws text, no other widgets. The white blinking is very ugly and I would like to get rid of it, ideally with some proper solution and without nasty hacks of introducing some artificial text drawing widgets. Moreover I am not very comfortable because I really do not know what is actually going on. Why the custom widget causes blinking and QLabel not? To prove the behavior try the following code (the same problem is in C++/Qt so it is not caused by Python wrapper). Then try to uncomment the marked line and comment the next one to see that the blinking is gone.
from PyQt5.QtGui import QPainter
from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QLabel
class CustomWidget(QWidget):
def paintEvent(self, event):
p = QPainter(self)
p.drawText(20, 20, "XYZ")
app = QApplication([])
container = QWidget()
layout = QVBoxLayout(container)
# label = QLabel("ABC") # uncomment this to prevent blinking
label = QLabel() # comment this out to prevent blinking
layout.addWidget(label)
layout.addWidget(CustomWidget())
container.resize(600, 600)
container.show()
app.exec()
Any ideas what is going on there? I am using Qt 5.9.2.

Adding QFileDialog as a widget inside another QDialog

I'm attempting to create a dialog which contains two child widgets: on the left side a QFileDialog instance so users can select files, and on the right side a separate widget which will be used to show a preview of the selected file if it is of a certain type.
The problem is that the dialog opens up and I can see the "preview" widget just fine, but the QFileDialog is not showing up at all.
This short example demonstrates my problem:
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
app = QApplication([])
main_dialog = QDialog()
main_dialog.setWindowTitle('My Dialog')
layout = QHBoxLayout(main_dialog)
file_dialog = QFileDialog(main_dialog, Qt.Widget)
file_dialog.setOption(QFileDialog.DontUseNativeDialog)
layout.addWidget(file_dialog)
preview = QLabel('Preview', main_dialog)
layout.addWidget(preview)
main_dialog.show()
app.exec_()
Some things that I've tried:
Add file_dialog.show() before/after main_dialog.show(): this shows up the QFileDialog, but in a different window; I want the file dialog to appear inside main_dialog, not as a separate window;
Do not pass Qt.Widget to the QFileDialog constructor, to no effect;
Do not pass main_dialog as parent to QFileDialog, again no effect;
Change main_dialog to a QWidget just to see if it changed anything, it did not;
I've searched the docs but did not find a suitable solution.
Any hints? Also, suggestions on how to accomplish the task of allowing the user to select a file and display a preview of the file in the same window are welcome.
Context: this is a port of an old application written for Qt3. Qt3's QFileSystem dialog had this "preview" functionality built-in; I'm trying to reproduce the same functionality in Qt5.
Versions
Python 2.7
PyQt 5.5.1
I've also tried with Python 3.6 (from conda-forge) but obtained the same behavior.
You need to turn off the Qt.Dialog flag in the file dialog's windowFlags...
file_dialog.setWindowFlags(file_dialog.windowFlags() & ~Qt.Dialog)
Otherwise the QFileDialog will always be created as a top level window. Works for me anyway.

How to make a QWidget based window have a transparent background?

Example code is here and it runs like that, the image looks like:
.
The main strategy is to use the QBrush to load a backgroud image that has a number and other stuff, the picture originally has the transparent background.
But how to make the QWidget window have a transparent backgroud has stucked me.
In PyQt, you need to add the flag to all your existing window flags using the bitwise OR operator |, to make it work:
window.setWindowFlags(window.windowFlags() | QtCore.Qt.FramelessWindowHint)
And then do
window.setAttribute(QtCore.Qt.WA_TranslucentBackground)
Remember to call the show() method after setting flags. Quoting the docs here:
Note: This function calls setParent() when changing the flags for a window, causing the widget to be hidden. You must call show() to make the widget visible again..
On the bitwise operator: https://wiki.python.org/moin/BitwiseOperators
Hope that was useful.
Edit: Removed some incorrect information, Thanks to #ekhumoro's comments from below.
If you need to have a window (based QWidget) with transparent background you can to my knowledge only achieve this if you also make the window frameless.
The following example does that. Important are setting window flag FramelessWindowHint and setting attribute WA_TranslucentBackground.
from PySide import QtCore, QtGui
app = QtGui.QApplication([])
window = QtGui.QWidget()
window.setWindowFlags(QtCore.Qt.FramelessWindowHint)
window.setAttribute(QtCore.Qt.WA_TranslucentBackground)
window.show()
layout = QtGui.QVBoxLayout(window)
button = QtGui.QPushButton('Exit')
button.clicked.connect(app.quit)
layout.addWidget(button)
app.exec_()
Which only shows a freestanding button.

Categories

Resources