Why, when I toggle the QMainWindow's SetWindowFlags to WindowStaysOnTopHint, does my window disappear, and more importantly not stay on top? I'm using PySide and a QMainWindow.
import sys
from PySide import QtGui, QtCore
class Browser(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Browser, self).__init__(parent)
self.resize(200, 150)
self.setWindowTitle('Assets')
self.initUI()
def initUI(self):
self.mi_stay_on_top = QtGui.QAction('Stay On Top', self)
self.mi_stay_on_top.setShortcut('Ctrl+T')
self.mi_stay_on_top.setCheckable(True)
self.mi_stay_on_top.triggered.connect(self.toggle_stay_on_top)
menubar = self.menuBar()
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(self.mi_stay_on_top)
grid = QtGui.QVBoxLayout()
grid.setContentsMargins(10,10,10,10)
self.setLayout(grid)
def toggle_stay_on_top(self):
if self.mi_stay_on_top.isChecked():
# enabled
self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
else:
# disable
self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowStaysOnTopHint)
def main():
app = QtGui.QApplication(sys.argv)
ex = Browser()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The setWindowFlags method resets the parent, which hides the window.
So you need to do this:
def toggle_stay_on_top(self):
if self.mi_stay_on_top.isChecked():
# enabled
self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
else:
# disable
self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowStaysOnTopHint)
# re-show the window after changing flags
self.show()
Related
I found some code on here that shows an example of how you can get the window to resize when the widget is hidden, and it works for me. Here is the code:
from PyQt4 import QtCore, QtGui
import sys
class MainWindow(QtGui.QWidget):
def __init__(self):
self.app = QtGui.QApplication(sys.argv)
super(MainWindow, self).__init__()
self.button = QtGui.QPushButton('Show/Hide')
self.button.setCheckable(True)
self.frame = QtGui.QFrame()
self.frame.setFixedHeight(100)
self.layout = layout = QtGui.QVBoxLayout()
layout2 = QtGui.QVBoxLayout()
self.setLayout(layout)
self.frame.setLayout(layout2)
layout.addWidget(self.button)
layout.addWidget(self.frame)
layout.addStretch(1)
layout2.addWidget(QtGui.QLabel('Yoyoyo'))
self.button.toggled.connect(self.clickAction)
def startup(self):
self.show()
sys.exit(self.app.exec_())
def clickAction(self):
checked = self.button.isChecked()
if checked:
self.frame.show()
else:
self.frame.hide()
QtCore.QTimer.singleShot(0, self.resizeMe)
def resizeMe(self):
self.resize(self.minimumSizeHint())
if __name__ == "__main__":
myApp = MainWindow()
myApp.startup()
I then tried to modify this to match my existing code by separating the mainWindow class and the widget class. Here is the code that does that.
from PySide import QtGui,QtCore
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.w = testW(self)
self.setCentralWidget(self.w)
self.show()
class testW(QtGui.QWidget):
def __init__(self,parent):
super(testW,self).__init__()
self.parent = parent
self.button = QtGui.QPushButton('Show/Hide')
self.button.setCheckable(True)
self.button.setChecked(True);
self.frame = QtGui.QFrame()
self.frame.setFixedHeight(100)
self.layout = layout = QtGui.QVBoxLayout()
layout2 = QtGui.QVBoxLayout()
self.setLayout(layout)
self.frame.setLayout(layout2)
layout.addWidget(self.button)
layout.addWidget(self.frame)
layout.addStretch(1)
layout2.addWidget(QtGui.QLabel('Yoyoyo'))
self.button.toggled.connect(self.clickAction)
def clickAction(self):
checked = self.button.isChecked()
if checked:
self.frame.show()
else:
self.frame.hide()
QtCore.QTimer.singleShot(0, self.resizeMe)
def resizeMe(self):
self.resize(self.minimumSizeHint())
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myApp = MainWindow()
sys.exit(app.exec_())
#time.sleep(1)
Running the first code does what I want it to. After I hide the widget, the window resizes to the correct size. The second implementation of the code does not shrink and expand the window when I hide and show the widget. Is this because the MainWindow is in a separate class?
Use size policies for your widgets. For your example you can change UI creation code as follows:
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.w = testW(self)
self.w.setSizePolicy(
QtWidgets.QSizePolicy.MinimumExpanding,
QtWidgets.QSizePolicy.MinimumExpanding
)
self.setCentralWidget(self.w)
self.show()
Please note new setSizePolicy call which say Qt layout engine how to change the size of your widget according to its content.
Unfortunately QMainWindow does not respect sizeHint automatically, but it is calculated properly, so you can adjustSize manually:
def clickAction(self):
checked = self.button.isChecked()
if checked:
self.frame.show()
else:
self.frame.hide()
QtCore.QTimer.singleShot(0, self.parent.adjustSize)
You do not need to resize your widget itself, because it will be resized according to the policy. Even sizeHint will be calculated automatically so you need only to call adjustSize of QMainWindow.
PS: I used PySide2 instead of PySide so the imports are different a little bit:
from PySide2 import QtWidgets, QtCore
The project occurs loging in and signing in.
Im trying a transition from registerwindow to mainwindow and when we submit the window is automatically transit to mainwindow. There is only way to do this (at least for me) i have to import two python doc which named mainwindow.py and register.py, they are in same doc by the way.
This is the mainmenu.py
from PyQt5 import QtCore,QtGui,QtWidgets
from window.register import Ui_Form
class Ui_MainWindow(object):
def login(self):
self.window = QtWidgets.QWidget()
self.ui = Ui_Form()
self.ui.setupUi(self.window)
self.window.show()
MainWindow.hide()
and this is register.py
from PyQt5 import QtCore, QtGui, QtWidgets
from window.mainmenu import Ui_MainWindow
import sqlite3
class Ui_Form(object):
def submit(self):
sorgu2 = "Select * From users where nickname = ?"
sorgu = "INSERT INTO users values(?,?)"
self.cursor.execute(sorgu,(self.lineEdit.text(),self.lineEdit.text()))
self.connect.commit()
Form.hide()
self.window2 = QtWidgets.QMainWindow()
self.ui2 = Ui_MainWindow()
self.ui2.setupUi(self.window2)
self.window2.show()
Its supposed to be when i clicked to the button the register window will be hidden and mainmenu window will be show. Same thing for the mainmenu but the direct opposite
I know i am doing circular dependent imports but there is no other way but importing them to each other
If second window will be QDialog then you can hide main window, use exec() for QDialog and main window will wait till you close QDialog, and when it returns to main window then you can show it again.
from PyQt5 import QtWidgets
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.button = QtWidgets.QPushButton("Show Second Window", self)
self.button.clicked.connect(self.show_second_window)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.button)
self.show()
def show_second_window(self):
self.hide() # hide main window
self.second = SecondWindow()
self.second.exec() # will wait till you close second window
self.show() # show main window again
class SecondWindow(QtWidgets.QDialog): # it has to be dialog
def __init__(self):
super().__init__()
self.button = QtWidgets.QPushButton("Close It", self)
self.button.clicked.connect(self.show_second_window)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.button)
self.show()
def show_second_window(self):
self.close() # go back to main window
app = QtWidgets.QApplication([])
main = MainWindow()
app.exec()
The other popular method is to create two widgets with all contents and replace widgets in one window.
from PyQt5 import QtWidgets
class MainWidget(QtWidgets.QWidget):
def __init__(self, parent):
super().__init__()
self.parent = parent
self.button = QtWidgets.QPushButton("Show Second Window", self)
self.button.clicked.connect(self.show_second_window)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.button)
self.show()
def show_second_window(self):
self.close()
self.parent.set_content("Second")
class SecondWidget(QtWidgets.QWidget):
def __init__(self, parent):
super().__init__()
self.parent = parent
self.button = QtWidgets.QPushButton("Close It", self)
self.button.clicked.connect(self.show_second_window)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.button)
self.show()
def show_second_window(self):
self.close()
self.parent.set_content("Main")
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.layout = QtWidgets.QVBoxLayout(self)
self.set_content("Main")
self.show()
def set_content(self, new_content):
if new_content == "Main":
self.content = MainWidget(self)
self.layout.addWidget(self.content)
elif new_content == "Second":
self.content = SecondWidget(self)
self.layout.addWidget(self.content)
app = QtWidgets.QApplication([])
main = MainWindow()
app.exec()
EDIT: Change window's content using QStackedLayout
from PyQt5 import QtWidgets
class FirstWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
layout = QtWidgets.QVBoxLayout(self)
self.button = QtWidgets.QPushButton("Show Second Stack", self)
self.button.clicked.connect(self.change_stack)
layout.addWidget(self.button)
def change_stack(self):
self.parent().stack.setCurrentIndex(1)
class SecondWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
layout = QtWidgets.QVBoxLayout(self)
self.button = QtWidgets.QPushButton("Show First Stack", self)
self.button.clicked.connect(self.change_stack)
layout.addWidget(self.button)
def change_stack(self):
self.parent().stack.setCurrentIndex(0)
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.stack = QtWidgets.QStackedLayout(self)
self.stack1 = FirstWidget(self)
self.stack2 = SecondWidget(self)
self.stack.addWidget(self.stack1)
self.stack.addWidget(self.stack2)
self.show()
app = QtWidgets.QApplication([])
main = MainWindow()
app.exec()
is there a way to get a context menu on a tables column head.
Find nothing about that in PyQt5's tuts.
the table's context menu is simple but the column heads don't affect.
# dlg is a QDialog object
self.tbl = QtWidgets.QTableWidget(dlg)
self.tbl.setContextMenuPolicy( Qt.CustomContextMenu )
You have to use the QHeaderView of the QTableWidget:
from PyQt5 import QtCore, QtWidgets
class Dialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.tbl = QtWidgets.QTableWidget(10, 10, self)
for w in (self.tbl.horizontalHeader(), self.tbl.verticalHeader(), self.tbl):
w.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
w.customContextMenuRequested.connect(self.on_customContextMenuRequested)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.tbl)
#QtCore.pyqtSlot(QtCore.QPoint)
def on_customContextMenuRequested(self, pos):
widget = self.sender()
if isinstance(widget, QtWidgets.QAbstractItemView):
widget = widget.viewport()
menu = QtWidgets.QMenu()
menu.addAction("Foo Action")
menu.exec_(widget.mapToGlobal(pos))
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Dialog()
w.show()
sys.exit(app.exec_())
Update:
class Dialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
self.tbl = QtWidgets.QTableWidget(10, 10, self)
self.tbl.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.tbl.customContextMenuRequested.connect(self.on_customContextMenuRequested_tw)
self.tbl.verticalHeader().setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.tbl.verticalHeader().customContextMenuRequested.connect(self.on_customContextMenuRequested_vh)
self.tbl.horizontalHeader().setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.tbl.horizontalHeader().customContextMenuRequested.connect(self.on_customContextMenuRequested_hh)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.tbl)
#QtCore.pyqtSlot(QtCore.QPoint)
def on_customContextMenuRequested_tw(self, pos):
menu = QtWidgets.QMenu()
menu.addAction("Foo Action TW")
menu.exec_(self.tbl.viewport().mapToGlobal(pos))
#QtCore.pyqtSlot(QtCore.QPoint)
def on_customContextMenuRequested_vh(self, pos):
menu = QtWidgets.QMenu()
menu.addAction("Foo Action VH")
menu.exec_(self.tbl.verticalHeader().mapToGlobal(pos))
#QtCore.pyqtSlot(QtCore.QPoint)
def on_customContextMenuRequested_hh(self, pos):
menu = QtWidgets.QMenu()
menu.addAction("Foo Action HH")
menu.exec_(self.tbl.horizontalHeader().mapToGlobal(pos))
You need to set the context menu policy on the header itself (if I've understood correctly), so...
self.tbl = QtWidgets.QTableWidget(dlg)
self.tbl.horizontalHeader().setContextMenuPolicy(Qt.CustomContextMenu)
and connect to the `QHeaderView::customContextMenuRequested signal...
self.tbl.horizontalHeader().customContextMenuRequested.connect(self.handle_context_menu_request)
I'm trying to make a button which would place window on top of others. Using recommends from other questions, I put in my class setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) to set and setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowStaysOnTopHint) to delete flag. It sets the flag, but when i change the button state, it still has that flag enabled. Here is code example:
from PyQt5 import QtWidgets, QtCore
import sys
class widget(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.resize(500, 500)
box = QtWidgets.QVBoxLayout()
self.setLayout(box)
self.btn = QtWidgets.QPushButton("pin")
box.addWidget(self.btn)
self.btn.setCheckable(True)
self.btn.toggled.connect(self.setOnTop)
def setOnTop(self):
if self.btn.isChecked():
self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
print("checked")
else:
self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowStaysOnTopHint)
print("unchecked")
self.show()
def main(self):
app = QtWidgets.QApplication(sys.argv)
ex = widget()
ex.show()
sys.exit(app.exec_())
main()
Clicking Dialog_01's button hides its window and opens Dialog_02. Clicking Dialog_02's button should close its windows and unhide Dialog_01. How to achieve it?
import sys, os
from PyQt4 import QtCore, QtGui
class Dialog_02(QtGui.QMainWindow):
def __init__(self):
super(Dialog_02, self).__init__()
myQWidget = QtGui.QWidget()
myBoxLayout = QtGui.QVBoxLayout()
Button_02 = QtGui.QPushButton("Close THIS and Unhide Dialog 01")
Button_02.clicked.connect(self.closeAndReturn)
myBoxLayout.addWidget(Button_02)
myQWidget.setLayout(myBoxLayout)
self.setCentralWidget(myQWidget)
self.setWindowTitle('Dialog 02')
def closeAndReturn(self):
self.close()
class Dialog_01(QtGui.QMainWindow):
def __init__(self):
super(Dialog_01, self).__init__()
myQWidget = QtGui.QWidget()
myBoxLayout = QtGui.QVBoxLayout()
Button_01 = QtGui.QPushButton("Hide THIS and Open Dialog 02")
Button_01.clicked.connect(self.callAnotherQMainWindow)
myBoxLayout.addWidget(Button_01)
myQWidget.setLayout(myBoxLayout)
self.setCentralWidget(myQWidget)
self.setWindowTitle('Dialog 01')
def callAnotherQMainWindow(self):
self.hide()
self.dialog_02 = Dialog_02()
self.dialog_02.show()
self.dialog_02.raise_()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
dialog_1 = Dialog_01()
dialog_1.show()
sys.exit(app.exec_())
Make the first window a parent of the second window:
class Dialog_02(QtGui.QMainWindow):
def __init__(self, parent):
super(Dialog_02, self).__init__(parent)
# ensure this window gets garbage-collected when closed
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
...
def closeAndReturn(self):
self.close()
self.parent().show()
class Dialog_01(QtGui.QMainWindow):
...
def callAnotherQMainWindow(self):
self.hide()
self.dialog_02 = Dialog_02(self)
self.dialog_02.show()
If you want the same dialog to be shown each time, do something like:
def callAnotherQMainWindow(self):
self.hide()
if not hassattr(self, 'dialog_02'):
self.dialog_02 = Dialog_02(self)
self.dialog_02.show()
and hide() the child window, rather than closing it.