A dialog's position changed after closing a QMessageBox in PyQt - python

I wrote a QMainWindow, two QDialog, and one QMessageBox. The code is as follows:
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QPushButton, QMessageBox, QApplication, QAction, QMainWindow
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
action = QAction("Open DocumentDialog", self)
action.triggered.connect(self.show_main_dialog)
menu_bar = self.menuBar()
file = menu_bar.addMenu("File")
file.addAction(action)
self.setWindowTitle("MainWindow")
def show_main_dialog(self):
main_dialog = DocumentDialog(self)
main_dialog.show()
class DocumentDialog(QDialog):
def __init__(self, parent):
super().__init__(parent)
vbox = QVBoxLayout()
btn = QPushButton("Open DocumentDetailDialog")
btn.clicked.connect(self.btn_clicked)
vbox.addWidget(btn)
self.setLayout(vbox)
self.setWindowTitle("DocumentDialog")
def btn_clicked(self):
detail_dialog = DetailDialog(self)
detail_dialog.show()
class DetailDialog(QDialog):
def __init__(self, parent: QDialog):
super().__init__(parent)
vbox = QVBoxLayout()
btn = QPushButton("Open delete message box")
btn.clicked.connect(self.btn_clicked)
vbox.addWidget(btn)
self.setLayout(vbox)
self.setWindowTitle("DetailDialog")
def btn_clicked(self):
msg_box = QMessageBox(self)
msg_box.setIcon(QMessageBox.Warning)
msg_box.setText("Are you sure?")
msg_box.setWindowTitle("Delete")
msg_box.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
msg_box.buttonClicked.connect(self.delete_handler)
msg_box.show()
def delete_handler(self, btn):
self.close()
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
mw = MainWindow()
mw.show()
sys.exit(app.exec())
The process is as follows:
Start the app, then MainWindow shows itself.
Click on the menu item to show DocumentDialog.
Click on the button on DocumentDialog to show DetailDialog.
Click on the button on DetailDialog to show a QMessageBox.
Click on the button on QMessageBox to close it along with DetailDialog.
The QMessageBox is closed, as expected. the weird thing is, DocumentDialog is over MainWindow when it shows up. But after QMessageBox is closed, DocumentDialog hides behind MainWindow, why? How not to let DocumentDialog change it position?
I record a GIF to illustrate the process:

You should set Qt.Popup Flag for your Dialog :
This makes your Dialog always on top of others.
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QPushButton, QMessageBox, QApplication, QAction, QMainWindow
from PyQt5.QtCore import *
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
action = QAction("Open DocumentDialog", self)
action.triggered.connect(self.show_main_dialog)
menu_bar = self.menuBar()
file = menu_bar.addMenu("File")
file.addAction(action)
self.setWindowTitle("MainWindow")
def show_main_dialog(self):
main_dialog = DocumentDialog(self)
main_dialog.show()
class DocumentDialog(QDialog):
def __init__(self, parent):
super().__init__(parent)
vbox = QVBoxLayout()
btn = QPushButton("Open DocumentDetailDialog")
btn.clicked.connect(self.btn_clicked)
vbox.addWidget(btn)
self.setLayout(vbox)
self.setWindowTitle("DocumentDialog")
def btn_clicked(self):
detail_dialog = DetailDialog(self)
detail_dialog.show()
class DetailDialog(QDialog):
def __init__(self, parent: QDialog):
super().__init__(parent)
vbox = QVBoxLayout()
btn = QPushButton("Open delete message box")
btn.clicked.connect(self.btn_clicked)
vbox.addWidget(btn)
self.setLayout(vbox)
self.setWindowTitle("DetailDialog")
self.setWindowFlags(self.windowFlags() |Qt.Popup)
def btn_clicked(self):
msg_box = QMessageBox(self)
msg_box.setIcon(QMessageBox.Warning)
msg_box.setText("Are you sure?")
msg_box.setWindowTitle("Delete")
msg_box.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
msg_box.buttonClicked.connect(self.delete_handler)
msg_box.show()
def delete_handler(self, btn):
self.close()
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
mw = MainWindow()
mw.show()
sys.exit(app.exec())
Result:
Edited:
From Dialog modal
This property holds whether show() should pop up the dialog as modal
or modeless
By default, this property is false and show() pops up the dialog as
modeless. Setting this property to true is equivalent to setting
QWidget::windowModality to Qt::ApplicationModal.
exec() ignores the value of this property and always pops up the
dialog as modal.
Then as #musicamante said you should change show to exec :
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
action = QAction("Open DocumentDialog", self)
action.triggered.connect(self.show_main_dialog)
menu_bar = self.menuBar()
file = menu_bar.addMenu("File")
file.addAction(action)
self.setWindowTitle("MainWindow")
def show_main_dialog(self):
main_dialog = DocumentDialog(self)
main_dialog.exec()
class DocumentDialog(QDialog):
def __init__(self, parent):
super().__init__(parent)
vbox = QVBoxLayout()
btn = QPushButton("Open DocumentDetailDialog")
btn.clicked.connect(self.btn_clicked)
vbox.addWidget(btn)
self.setLayout(vbox)
self.setWindowTitle("DocumentDialog")
def btn_clicked(self):
detail_dialog = DetailDialog(self)
detail_dialog.exec()
class DetailDialog(QDialog):
def __init__(self, parent: QDialog):
super().__init__(parent)
vbox = QVBoxLayout()
btn = QPushButton("Open delete message box")
btn.clicked.connect(self.btn_clicked)
vbox.addWidget(btn)
self.setLayout(vbox)
self.setWindowTitle("DetailDialog")
def btn_clicked(self):
msg_box = QMessageBox(self)
msg_box.setIcon(QMessageBox.Warning)
msg_box.setText("Are you sure?")
msg_box.setWindowTitle("Delete")
msg_box.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
msg_box.buttonClicked.connect(self.delete_handler)
msg_box.exec()
def delete_handler(self, btn):
self.close()

Related

QStackedLayout shows empty window for a few moments

In this example, as the main window, I use a QWidget that contains a QStackedLayout and a QPushButton to change the current widget to a QStackedLayout.
from PySide6.QtWidgets import QFrame, QWidget, QApplication, QVBoxLayout, QStackedLayout, QPushButton
from PySide6.QtCore import Qt
class ColorWidget(QFrame):
def __init__(self, color):
super(ColorWidget, self).__init__()
self.setFixedSize(200, 200)
self.setStyleSheet(f"background-color: {color}; border-radius: 6px;")
# Some widget. In this case, just a colored background.
class MainWidget(QWidget):
def __init__(self):
super(MainWidget, self).__init__()
self.current_widget = False
layout = QStackedLayout()
layout.addWidget(ColorWidget("red"))
layout.addWidget(ColorWidget("yellow"))
layout.setCurrentIndex(0)
self.setLayout(layout)
# Main widget. Contains 2 colored widgets.
def change_visible_widget(self):
self.current_widget = not self.current_widget
self.layout().setCurrentIndex(int(self.current_widget))
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowFlag(Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground)
# no frame, no background
layout = QVBoxLayout()
main_widget = MainWidget()
button = QPushButton("change")
button.clicked.connect(main_widget.change_visible_widget)
# button to change QStackedLayout index in Main Widget
layout.addWidget(main_widget)
layout.addWidget(button)
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication()
win = MainWindow()
win.show()
app.exec()
The problem is that when the program starts, an empty window appears for a few moments.
By trial and error, I realized that this is because of the QStackedLayout and the number of windows that appear is equal to the number of created QStackedLayout (in this case it is 1).
How can this be fixed?
Just add self to layout = QStackedLayout():
from PySide6.QtWidgets import QFrame, QWidget, QApplication, QVBoxLayout, QStackedLayout, QPushButton
from PySide6.QtCore import Qt
class ColorWidget(QFrame):
def __init__(self, color):
super(ColorWidget, self).__init__()
self.setFixedSize(200, 200)
self.setStyleSheet(f"background-color: {color}; border-radius: 6px;")
# Some widget. In this case, just a colored background.
class MainWidget(QWidget):
def __init__(self):
super(MainWidget, self).__init__()
self.current_widget = False
layout = QStackedLayout(self)
layout.addWidget(ColorWidget("red"))
layout.addWidget(ColorWidget("yellow"))
layout.setCurrentIndex(0)
# Main widget. Contains 2 colored widgets.
def change_visible_widget(self):
self.current_widget = not self.current_widget
self.layout().setCurrentIndex(int(self.current_widget))
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowFlag(Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground)
# no frame, no background
layout = QVBoxLayout()
main_widget = MainWidget()
button = QPushButton("change")
button.clicked.connect(main_widget.change_visible_widget)
# button to change QStackedLayout index in Main Widget
layout.addWidget(main_widget)
layout.addWidget(button)
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication()
win = MainWindow()
win.show()
app.exec()

How to make a floating window when I press the user button in pyqt5?

I am new to the world of pyqt5 I have created a window using this code:
class Ui_menu(QtWidgets.QMainWindow):
def __init__(self):
super(Ui_menu, self).__init__() # Call the inherited classes __init__ method
uic.loadUi('Windows/menu.ui', self) # Load the .ui file
self.show()
app = QtWidgets.QApplication(sys.argv)
window = Ui_menu()
app.exec_()
I want to know if you can create an animation when you click a button at the top and open a floating window as in the attached image.
look this code
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QSize, QPoint, Qt
from PyQt5.QtGui import QIcon
class MyProxyStyle(QProxyStyle):
pass
def pixelMetric(self, QStyle_PixelMetric, option=None, widget=None):
if QStyle_PixelMetric == QStyle.PM_SmallIconSize:
return 100
else:
return QProxyStyle.pixelMetric(self, QStyle_PixelMetric, option, widget)
class main(QMainWindow):
def __init__(self):
super().__init__()
self.central = QWidget()
self.toolbar = toolbar()
self.button1 = toolbutton("your_icon")
self.button2 = toolbutton("your_icon")
self.toolbar.addWidget(self.button1)
self.toolbar.addWidget(self.button2)
self.button1.clicked.connect(lambda: self.open_menu(self.button1))
self.button2.clicked.connect(lambda: self.open_menu(self.button2))
self.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.toolbar)
self.setCentralWidget(self.central)
self.resize(600,400)
self.show()
def open_menu(self, obj):
self.menu = QMenu()
self.menu.setStyle(MyProxyStyle())
self.menu.addAction(QIcon("your_icon"), "Person")
self.menu.addMenu("Configuration")
self.menu.addSeparator()
self.menu.addMenu("Profile")
pos = self.mapToGlobal(QPoint(obj.x(), obj.y()+obj.height()))
self.menu.exec(pos)
class toolbar(QToolBar):
def __init__(self):
super().__init__()
self.setIconSize(QSize(80,80))
self.setMinimumHeight(80)
self.setMovable(False)
class toolbutton(QToolButton):
def __init__(self, icon):
super().__init__()
self.setIcon(QIcon(icon))
self.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
app = QApplication([])
window = main()
app.exec()

Share attribute between two classes "QWidget"

I'm trying to "send" the attribute from a QWidget class to another.
In the example below, I'm trying to set the text of the QLineEdit "self.edit" belonging to the class "Widget1" as text of the QLabel "self.label" belonging to the class "Widget2".
This attempt is made in the function "setLabel".
The part that I cannot figure out is "Widget2.label.setText(text)"
Having a class in a class in a function... I'm a little bit confused how to achieve that...
import sys
from PySide2.QtWidgets import (QApplication, QHBoxLayout, QVBoxLayout, QWidget, QPushButton, QLabel, QLineEdit)
class Main_UI(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
layout = QVBoxLayout()
widget1 = Widget1()
widget2 = Widget2()
layout.addWidget(widget1)
layout.addWidget(widget2)
self.setLayout(layout)
self.show()
class Widget1(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
layout = QHBoxLayout()
self.edit = QLineEdit("")
button = QPushButton("Set value")
button.clicked.connect(self.setLabel)
layout.addWidget(self.edit)
layout.addWidget(button)
self.setLayout(layout)
def setLabel(self):
text = self.edit.text()
Widget2.label.setText(text)
class Widget2(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
layout = QHBoxLayout()
self.label = QLabel("")
layout.addWidget(self.label)
self.setLayout(layout)
def main():
app = QApplication(sys.argv)
ex = Main_UI()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Any help would be appreciated, and if my example or explanations are not clear, I'll provide further explanations.
You can do this with a custom signal.
import sys
from PyQt5.QtWidgets import (QApplication, QHBoxLayout, QVBoxLayout, QWidget, QPushButton, QLabel, QLineEdit)
from PyQt5 import QtCore
class Main_UI(QWidget):
def __init__(self, parent=None):
super(Main_UI, self).__init__(parent)
self.initUI()
def initUI(self):
layout = QVBoxLayout()
widget1 = Widget1()
widget2 = Widget2()
layout.addWidget(widget1)
layout.addWidget(widget2)
self.setLayout(layout)
widget1.button_signal.connect(widget2.label.setText) # Connecting the label to the custom signal.
self.show()
class Widget1(QWidget):
button_signal = QtCore.pyqtSignal(str) # Creating a signal.
def __init__(self, parent=None):
super(Widget1, self).__init__(parent)
self.initUI()
def initUI(self):
layout = QHBoxLayout()
self.edit = QLineEdit("")
button = QPushButton("Set value")
button.clicked.connect(self.setLabel)
layout.addWidget(self.edit)
layout.addWidget(button)
self.setLayout(layout)
def setLabel(self):
"""Emit button signal with text.
This could have been solved with a lambda.
"""
self.button_signal.emit(self.edit.text()) # Emitting Signal.
class Widget2(QWidget):
def __init__(self, parent=None):
super(Widget2, self).__init__(parent)
self.initUI()
def initUI(self):
layout = QHBoxLayout()
self.label = QLabel("")
layout.addWidget(self.label)
self.setLayout(layout)
def main():
app = QApplication(sys.argv)
ex = Main_UI()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Docs: https://doc.qt.io/qt-5/signalsandslots.html

Common buttons and positioning in PyQt5's QStackedLayout

I am creating a Python PyQt5 app for a university project, which uses QStackedLayout to keep the app single-windowed.
First question: how can I create buttons that appear on every window and have the same function and property without having to recreate them in every window's UI setup (like in the code)?
Second question: after switching windows, how can I open the newly opened window at the previous's position? Right now they just open at the centre of the screen. I assume pos() or QPoint should be used but I can not figure out how.
import sys
from PyQt5.QtWidgets import (QApplication,
QMainWindow,
QPushButton,
QWidget,
QStackedLayout)
class Ui(QWidget):
def setupUi(self, Main):
self.application_width = 200
self.application_height = 200
self.stack = QStackedLayout()
self.window_1 = QWidget()
self.window_2 = QWidget()
self.window_1_UI()
self.window_2_UI()
self.stack.addWidget(self.window_1)
self.stack.addWidget(self.window_2)
def window_1_UI(self):
self.window_1.setFixedSize(self.application_width, self.application_height)
self.window_1.setWindowTitle("1")
'''REPLACE THIS BUTTON'''
self.window_1_button = QPushButton("Change window", self.window_1)
def window_2_UI(self):
self.window_2.setFixedSize(self.application_width, self.application_height)
self.window_2.setWindowTitle("2")
'''AND REPLACE THIS BUTTON'''
self.window_2_button = QPushButton("Change window", self.window_2)
class Main(QMainWindow, Ui):
def __init__(self):
super(Main, self).__init__()
self.setupUi(self)
self.window_1_button.clicked.connect(self.change_window)
self.window_2_button.clicked.connect(self.change_window)
def change_window(self):
if self.stack.currentIndex() == 0:
self.stack.setCurrentIndex(1)
else:
self.stack.setCurrentIndex(0)
if __name__ == "__main__":
app = QApplication(sys.argv)
M = Main()
sys.exit(app.exec())
Put your button outside your widgets (in your Main widget). The Ui class should manage the stacked layout.
For example:
class Ui(QWidget):
def setupUi(self, Main):
self.stack = QStackedLayout()
self.window_1 = QWidget()
self.window_2 = QWidget()
self.window_1_UI()
self.window_2_UI()
self.stack.addWidget(self.window_1)
self.stack.addWidget(self.window_2)
# Only one button
self.btn = QPushButton("Change window", self)
# Create the central widget of your Main Window
self.main_widget = QWidget()
layout = QVBoxLayout(self.main_widget)
layout.addLayout(self.stack)
layout.addWidget(self.btn)
self.setCentralWidget(self.main_widget)
self.btn.clicked.connect(self.change_window)
def change_window(self):
if self.stack.currentIndex() == 0:
self.stack.setCurrentIndex(1)
else:
self.stack.setCurrentIndex(0)
def window_1_UI(self):
label = QLabel("In Window 1", self.window_1)
def window_2_UI(self):
label = QLabel("In Window 2", self.window_2)
class Main(QMainWindow, Ui):
def __init__(self):
super(Main, self).__init__()
self.setupUi(self)
if __name__ == "__main__":
app = QApplication(sys.argv)
M = Main()
M.show()
sys.exit(app.exec())

How to make a slot or signal between pushbutton and tab widget in pyQt

I just need to know if I can make a slot or signal between a pushbutton and specific page in tabwidget or no !?
If yes then how?
I divide a tab widget into 4 pages. My ask is about if I can make a slot or signal between pushbutton in first page and second page or not.
Try it:
from PyQt5.QtWidgets import (QWidget, QApplication, QTabWidget, QLabel, QPushButton,
QVBoxLayout, QMenuBar, QAction)
class MainWidget(QWidget):
def __init__(self, textLabel, *args, **kwargs):
super(MainWidget, self).__init__(*args, **kwargs)
layout = QVBoxLayout(self)
self.label = QLabel(textLabel, self)
self.btn = QPushButton('Next', self)
layout.addWidget(self.label)
layout.addWidget(self.btn)
class Window(QWidget):
def __init__(self):
super().__init__()
bar = QMenuBar(self)
menu = bar.addMenu('File')
action = QAction('Close activ tab', self)
menu.addAction(action)
action.triggered.connect(self.closeActivTab)
self.tabwdg = QTabWidget()
self.tabwdg.setTabsClosable(True)
self.tabWidget = MainWidget('this is the first page')
self.tabwdg.addTab(self.tabWidget, 'first')
self.tabWidget.btn.clicked.connect(self.numTab)
self.tabWidget = MainWidget('this is the second page')
self.tabwdg.addTab(self.tabWidget, 'second')
self.tabWidget.btn.clicked.connect(self.numTab)
self.tabWidget = MainWidget('this is the third page')
self.tabwdg.addTab(self.tabWidget, 'third')
self.tabWidget.btn.clicked.connect(self.numTab)
self.tabWidget = MainWidget('this is the fourth page')
self.tabwdg.addTab(self.tabWidget, 'fourth')
self.tabWidget.btn.clicked.connect(self.numTab)
self.tabwdg.tabCloseRequested.connect(self.closeTab)
box = QVBoxLayout()
box.addWidget(bar)
box.addWidget(self.tabwdg)
self.setLayout(box)
def numTab(self):
nextTab = self.tabwdg.currentIndex()+1
if nextTab == self.tabwdg.count():
nextTab = 0
self.tabwdg.setCurrentIndex(nextTab)
self.tabwdg.setCurrentWidget(self.tabwdg.currentWidget())
def closeActivTab(self):
activ_tab_ind = self.tabwdg.currentIndex()
self.closeTab(activ_tab_ind)
def closeTab(self, ind):
self.tabwdg.removeTab(ind)
if __name__ == '__main__':
import sys
app = QApplication([''])
w = Window()
w.resize(400, 300)
w.show()
sys.exit(app.exec_())

Categories

Resources