How to show Qt.Tool window with minimize/maximize windows controls? - python

I have...
class ToolWindow(QtWidgets.QMainWindow):
"""Generic window to be used as non-modal tool
Usage:
tool_win = ToolWindow()
layout = QtWidgets.QHBoxLayout()
button = QtWidgets.QPushButton('hello')
layout.addWidget(button)
tool_win.setup(layout)
button.released.connect(lambda: print('hello'))
tool_win.show()
"""
def __init__(self):
super(ToolWindow, self).__init__()
def setup(self, layout,
window_title='Untitled', object_name=None, tool=True):
"""Setup tool window"""
if tool:
self.setWindowFlags(QtCore.Qt.Tool)
self.widget = QtWidgets.QWidget()
self.widget.setLayout(layout)
self.setCentralWidget(self.widget)
self.setWindowTitle(window_title)
def closeEvent(self, event):
"""Delete object when closed"""
self.deleteLater()
However, I wish to add the typical maximize and minimize window controls to the window. I've attempted to add the following to the ToolWindow class without success (the tool window still doesn't show the maximize/minimize window controls):
self.setWindowFlags(self.windowFlags() |
QtCore.Qt.WindowSystemMenuHint |
QtCore.Qt.WindowMinMaxButtonsHint)
Is it possible to add these controls to a tool window?
Alternatively, can I create a non-modal window but which always sits atop my parent application and which shows the maximize/minimize window controls?
Please note, I don't want this tool window staying on top of ALL windows on my system. I only want it to always stay on top of my application.

You should be able to just use the QMainWindow class without any flags. As long as the tool window is a child of the primary application window, it will stay on top of it (but not windows from other applications, like it would if you set the "Window Stays On Top" flag).
You'll need to change your __init__ to accept parent arguments
def __init__(self, parent):
super(ToolWindow, self).__init__(parent)
If you have multiple Tool Windows and you want them to stay on top in a specific order, you can call my_tool_window.raise_() to bring it to the top of the z-order.
Qt ships with a window flags example. You may want to check that out to see how the different flags affect the window display and behavior.

Related

PyQt5: how to open one window in each connected screen?

I'm trying to create a screenshot utility for linux using python. Right now I'm stuck at trying to implement a function that lets the user select a region from a live screen and screenshot it. After much pondering, I reached the conclusion to create a full-screen window on each screen to get the mouse's click and drag coordinates.
How can I have my program create a full-screen window (without the toolbar icon) for each screen connected to the system?
import sys
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtGui as qtg
from PyQt5 import QtCore as qtc
class InvisWindow(qtw.QWidget):
def __init__(self, screens):
super().__init__()
self.setWindowFlags(qtc.Qt.Tool | qtc.Qt.FramelessWindowHint)
self.show()
self.showFullScreen()
self.windowHandle().setScreen(screens[0])
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
mw = InvisWindow(app.screens())
sys.exit(app.exec_())
I found this code searching for a way to do it, but no matter which screen I pass to setScreen() it always appears on a single screen, i.e. changing the argument doesn't change which screen it appears in.
There are two problems:
as the documentation explains:
If the screen is part of a virtual desktop of multiple screens, the window will not move automatically to newScreen.
on Linux, there's some amount of time and system events between the call to show and when the window is actually mapped on the screen the first time (see Initial Geometry), which can be overridden by the window manager if no geometry is explicitly set;
That said, there should be no need to use the QWindow for this, as using move is usually be enough, you only must do it before any call to show() or related functions:
class InvisWindow(qtw.QWidget):
def __init__(self, screens):
super().__init__()
self.setWindowFlags(qtc.Qt.Tool | qtc.Qt.FramelessWindowHint)
self.move(screens[0].geometry().topLeft())
self.showFullScreen()
Note that there's no use in calling show() before showFullScreen(), since it implicitly calls setVisible(True).
If what you want is to show a single window on top of everything, then you could try the following:
class InvisWindow(qtw.QWidget):
mapped = False
def __init__(self, screens):
super().__init__()
self.setWindowFlags(
qtc.Qt.WindowStaysOnTopHint |
qtc.Qt.Tool |
qtc.Qt.FramelessWindowHint
)
self.show()
def moveEvent(self, event):
if not self.mapped:
geometry = qtc.QRect()
for screen in qtw.QApplication.screens():
geometry |= screen.geometry()
if self.pos() != geometry.topLeft():
self.setGeometry(geometry)
self.mapped = True
Please consider the last lines, as they are very important, because trying to do geometry changes in a geometry change event (moveEvent and resizeEvent) can cause recursion.

QFileDialog opens in new window while adding it to QHBoxLayout

My issue is that when I am adding QFileDialog to QVBoxLayout it opens in new window. Below is the code which produces my problem.
from PyQt5.QtWidgets import QVBoxLayout, QFileDialog, QPushButton, QWidget
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowTitle("My own MainWindow")
self.fileDialog = QFileDialog()
self.confirmAction = QPushButton("Press me", self)
mainLayout = QVBoxLayout()
mainLayout.addWidget(self.fileDialog)
mainLayout.addWidget(self.confirmAction)
self.setLayout(mainLayout)
According to the docs:
Window flags are a combination of a type (e.g. Qt::Dialog) and zero or
more hints to the window system (e.g. Qt::FramelessWindowHint).
If the widget had type Qt::Widget or Qt::SubWindow and becomes a
window (Qt::Window, Qt::Dialog, etc.), it is put at position (0, 0) on
the desktop. If the widget is a window and becomes a Qt::Widget or
Qt::SubWindow, it is put at position (0, 0) relative to its parent
widget.
So these flags are used to vary the behavior of the widget, for example to convert it into a window, a dialog, a tooltip, and so on.
In the docs gives the following list:
Qt::Widget: This is the default
type for QWidget. Widgets of this type are child widgets if they have
a parent, and independent windows if they have no parent. See also
Qt::Window and Qt::SubWindow.
Qt::Window: Indicates that the
widget is a window, usually with a window system frame and a title
bar, irrespective of whether the widget has a parent or not. Note that
it is not possible to unset this flag if the widget does not have a
parent.
Qt::Dialog :Window Indicates that the widget is a
window that should be decorated as a dialog (i.e., typically no
maximize or minimize buttons in the title bar). This is the default
type for QDialog. If you want to use it as a modal dialog, it should
be launched from another window, or have a parent and used with the
QWidget::windowModality property. If you make it modal, the dialog
will prevent other top-level windows in the application from getting
any input. We refer to a top-level window that has a parent as a
secondary window.
Qt::Sheet: Window Indicates that the
window is a Macintosh sheet. Since using a sheet implies window
modality, the recommended way is to use QWidget::setWindowModality(),
or QDialog::open(), instead.
Qt::Drawer: Window Indicates
that the widget is a Macintosh drawer.
Qt::Popup : Window Indicates that the widget is a pop-up top-level window, i.e.
that it is modal, but has a window system frame appropriate for pop-up
menus.
Qt::Tool: Window Indicates that the widget is a
tool window. A tool window is often a small window with a smaller than
usual title bar and decoration, typically used for collections of tool
buttons. If there is a parent, the tool window will always be kept on
top of it. If there isn't a parent, you may consider using
Qt::WindowStaysOnTopHint as well. If the window system supports it, a
tool window can be decorated with a somewhat lighter frame. It can
also be combined with Qt::FramelessWindowHint.
On Mac OS X, tool windows correspond to the Floating class of windows.
This means that the window lives on a level above normal windows; it
impossible to put a normal window on top of it. By default, tool
windows will disappear when the application is inactive. This can be
controlled by the Qt::WA_MacAlwaysShowToolWindow attribute.
Qt::ToolTip:Window Indicates that the widget is a
tooltip. This is used internally to implement tooltips.
Qt::SplashScreen: Window Indicates that the window is a
splash screen. This is the default type for QSplashScreen.
Qt::Desktop:Window Indicates that this widget is the
desktop. This is the type for QDesktopWidget.
Qt::SubWindow: Indicates that this widget is a sub-window,
such as a QMdiSubWindow widget.
In your case we must change the behavior of Qt::Dialog to Qt::Widget, in the following code I show the code that does it:
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowTitle("My own MainWindow")
self.fileDialog = QFileDialog(self)
self.fileDialog.setOption(QFileDialog.DontUseNativeDialog)
self.fileDialog.setWindowFlags(Qt.Widget)
self.confirmAction = QPushButton("Press me", self)
mainLayout = QVBoxLayout()
mainLayout.addWidget(self.fileDialog)
mainLayout.addWidget(self.confirmAction)
self.setLayout(mainLayout)
Screenshot:
I've been looking into this myself, dissatisfied with the "just use non-native dialogs" bit. I've been hacking around in the KDE platform file dialog implementation and have gotten (stuck) pretty close to what I'd want.
The only point of attach I've found is just before the dialog will actually be shown; before that there seems to be no way to know the actual parent widget. But there we can:
find the parent QWidget (from the parent QWindow)
from that, obtain the (first) (user-side) QFileDialog instance
if the parent QWidget has a layout, replace the found QFileDialog instance with our own dialog
save the original user-side QFileDialog instance
in the dtor, either restore the original QFD in the layout, or call deleteLater() on it (and set it to NULL in case the action causes recursive calling of the dtor).
Glitches:
- dialogs may end up with 2 sets of OK/Cancel/etc. buttons
- if not, these buttons may actually close only the embedded QFD and not the enclosing dialog (seen with the python example linked above)
- resizing works but the saveSize/restoreSize mechanism doesn't
- AFAICT all signals aren't connected properly (the preview in the Scribus open-file dialog doesn't react to selecting a file as it should). File opening does work though.
Full patch here on this BKO ticket:
https://bugs.kde.org/show_bug.cgi?id=404833#c15
Evidently this is only useful for hackers and software that can ship its own platform theme plugin (which is where the KDE platform file dialog comes from). Fortunately those plugins tend to be relatively small.

PyQt - QWidget - Show Title bar only

I am opening multiple files in my application. For big files it takes few seconds to open, Hence I am displaying a QWidget with status of opened files.
The Main application passes a signal to qwidget and titlebar gets updated.
I used setWindowFlags(Qt.WindowTitleHint) to get rid of minimize/maximize buttons.
Is it the right way to do it?
Is it possible for QWidget to show title bar only?
#This is the method for reading files in the main class
#Some other method passes filenames to this method
def readfiles(self,filenames):
fopenstins=FileOpenStatusWidget()
fopenstins.show()
for i in range(len(filenames)): # Read files one by one
self.emit(SIGNAL('fopenstsig'),i+1,len(filenames))
#### More Code for reading files#####
class FileOpenStatusWidget(QtGui.QWidget):
def __init__(self):
super(FileOpenStatusWidget, self).__init__()
self.connect(main,SIGNAL('fopenstsig'),self.qwrtt)
self.layout = QtGui.QVBoxLayout()
self.status=QtGui.QLabel()
self.layout.addWidget(self.status) # This does not work -- Window remains blank
self.setWindowFlags(Qt.WindowTitleHint)
self.setLayout(self.layout)
def qwrtt(self,openedfiles,totalfiles):
self.status.setText('Opening File '+str(openedfiles)+'/'+str(totalfiles))
if openedfiles==totalfiles:
self.hide()
else:
self.setWindowTitle(self.status.text())
Use a combination of flags: Qt.CustomizeWindowHint | Qt.WindowTitleHint.
Window icon and close button are controlled by the Qt.WindowSystemMenuHint, which is added by default. Qt.CustomizeWindowHint disables all default hints.

What does the Qt.Popup window flag entail?

I'm using PyQT5 to create a GUI. Basically, what I want is a transparent window that extends the entire width and height of the screen (including the toolbar and dock on MacOS)
The code I am using to achieve this is like so:
class Gui(QWidget):
def __init__(self):
#Initialize the QApp/QWidget things
super().__init__()
#Add a default rectangle
self.rectangle = QRect(0, 0, 0, 0)
#Build the window in a method to keep the init clean
self.buildWindow()
#Build the window
def buildWindow(self):
#Make the window transparent
self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint | Qt.Popup)
self.setAttribute(Qt.WA_TranslucentBackground)
#Maximize the window
self.resize(1920, 1080)
#Enable mouse tracking
self.setMouseTracking(True)
#Render the window
self.show()
I open the GUI like so:
#Instantiate our app and Gui stuff.
app = QApplication(sys.argv)
gui = Gui()
#Make the cursor the "cross cursor" for effect
app.setOverrideCursor(QCursor(Qt.CrossCursor))
#Exit when our app exits
sys.exit(app.exec_())
The issue is that the GUI opens, renders for a second, and disappears immediately. If I remove Qt.Popup from the window flags, it will do exactly what I want it to do (but it will not extend past the dock or the toolbar on MacOS)
I've heard that this problem is causes (generally) by a widget being rendered and leaving the scope due to Python's garbage collection system, but I'm unsure if that is the problem here because it will actually render if I remove the Qt.Popup
Anyone who has experience with QT and could help would be awesome.. I've been trying to figure out this bug for a couple days.
EDIT: If you can't tell already, I am developing this on MacOS

How to pass along menu shortcuts from child widget holding focus in PyQt4?

I have a QMainWindow with a menu bar, including menu items for Save, Open and Quit with the usual shortcuts. It creates a QTableWidget that lists a bunch of different categories from which the user can choose (at his option).
If the user clicks into the QTableWidget to change categories, the widget takes the focus. That's mostly what I want, but unfortunately it also seems to steal the menu shortcuts, so that pressing Ctrl+S no longer triggers the save.
I experimented with keyPressEvent to solve this, but it seems like overkill even if I do get it working. Isn't there a way to delegate all the control/menu keys back to the QMainWindow ?
There must be an issue with how you are creating your QMenuBar. Here is an example that works just fine for me. The Save continues to function regardless of focus being in the table:
class Window(QtGui.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.resize(640,480)
menuBar = self.menuBar()
menu = menuBar.addMenu("&File")
action = menu.addAction("&Save", self.doAction)
action.setShortcuts(QtGui.QKeySequence.Save)
self.view = QtGui.QTableWidget(5,5)
self.setCentralWidget(self.view)
def doAction(self):
print "Save"

Categories