pyqt5 can not close a topmost fullscreen qdialog on mac osx - pyqt

I am writing an app that can take a screenshot on osx 10.9.5 using pyqt5; code sample is like this:
import sys
from PyQt5 import QtWidgets,QtCore,Qt
from PyQt5.Qt import QApplication
class CScreenshot(QtWidgets.QDialog):
def __init__(self):
super(CScreenshot,self).__init__()
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) #1
def screenshot(self):
#grab screen and paint into this dialog
#so user can modify the pix through this dialog
_screen = Qt.QApplication.primaryScreen()
self.m_snapPix = _screen.grabWindow(0)
#...
self.showFullScreen()
self.exec_()
def drawLine(_p1,_p2):
pass
#...
class CMainWindow(QtWidgets.QWidget):
def __init__(self):
super(CMainWindow,self).__init__()
self.m_btnScreenshot = QtWidgets.QPushButton(self)
self.m_btnScreenshot.clicked.connect(self.onBtnClk)
def onBtnClk(self):
ss = CScreenshot()
ss.screenshot() #here into the CScreenshot's message loop
#process next step balabala...
if __name__ == "__main__":
app = QApplication(sys.argv)
w = CMainWindow()
w.show()
sys.exit(app.exec_())
Here comes the question: When I click the button, the fullscreen dialog pops up and stays on topmost, everything works fine. But when I press the Esc button, the dialog should close and show the main window, but the screen just fixed by a black fullscreen window, which has the title python. If I remove the #1 marked line, it will work fine, but I need the dialog to stay topmost, hope someone can help me to figure out the problem.

I read the doc,and found the answer:"To return from full-screen mode,call showNormal()".It's my stupid.

Related

How to fix missing Task Bar icon in second QMainWindow widget

I am making a GUI that had the Welcome page and the main page. The purpose is to let user agree on the welcome page, the welcome page is dismissed and the main page will show up for further step. However, the icon in the taskbar only shows up in the welcome page, when we click into the main window the icon is disappeared and the app appeared to be a minimized window on the bottom left corner in the screen.
The starting page and main window layout is appear like this.
class welcome_window(QtWidgets.QMainWindow):
def __init__(self,parent = None):
super(welcome_window, self).__init__(parent)
self.confirm_button = QtWidgets.QPushButton('Yes')
self.confirm_button.clicked.connect(self.startup)
Main_layout = QtWidgets.QHBoxLayout()
Main_layout.addWidget(self.confirm_button)
self.main.setLayout(Main_layout)
def startup(self):
self.close()
dialog = Main_window(self)
self.dialogs.append(dialog)
dialog.show()
class Main_window(QtWidgets.QMainWindow):
def __init__(self,parent = None):
super(Main_window, self).__init__(parent)
self.setGeometry(50, 50, 1500, 850)
# here is all the step for later operation
def main():
app = QtWidgets.QApplication(sys.argv)
main = welcome_window()
main.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I expected that if the icon located in the taskbar could always stay on, it would be great for my GUI. Thank you all.
First of all, the MRE you gave is not reproducible. When I tried to run it it just didn't work. In this case you had a simple issue so I could just guess what was intended, but when you get more complicated problems people might not be able to help you. So in the future please make sure that we can just copy-paste-execute your code.
The reason that the main window disappears is that it's a member of the Welcome window. When you close the Welcome window, the corresponding python object will deleted and therefore Python will no longer have a reference to the main window. The main window object will be garbage-collected and all kinds of strange things might happen (I would expect it to just disappear).
The solution is to have a reference to the main window that stays valid until the program closes. This can be done by defining it in the main function (and then giving it as a parameter to the Welcome window). Like this...
import sys
from PyQt5 import QtWidgets
# Use a QWidget if you don't need toolbars.
class welcome_window(QtWidgets.QWidget):
def __init__(self, main_window=None, parent = None):
super(welcome_window, self).__init__(parent)
self.main_window = main_window
self.confirm_button = QtWidgets.QPushButton('Yes')
self.confirm_button.clicked.connect(self.startup)
main_layout = QtWidgets.QHBoxLayout() # use lower case for variable names
main_layout.addWidget(self.confirm_button)
self.setLayout(main_layout)
def startup(self):
self.main_window.show()
self.close()
class Main_window(QtWidgets.QMainWindow):
def __init__(self,parent = None):
super(Main_window, self).__init__(parent)
self.setGeometry(50, 50, 1500, 850)
# here is all the step for later operation
# Don't use self.setLayout on a QMainWindow,
# use a central widget and set a layout on that.
self.main_widget = QtWidgets.QWidget()
self.setCentralWidget(self.main_widget)
main_layout = QtWidgets.QHBoxLayout()
self.main_widget.setLayout(main_layout)
main_layout.addWidget(QtWidgets.QLabel("Hello"))
def main():
app = QtWidgets.QApplication(sys.argv)
main = Main_window()
welcome = welcome_window(main_window=main)
welcome.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Some more tips. Don't use setLayout on a QMainWindow. Use a central widget and add your widgets to the layout of the central widget. The layout of the main window is for toolbars and such. See: https://doc.qt.io/qt-5/qmainwindow.html#qt-main-window-framework
Just use a QWidget if you want a simple window without toolbars (like your welcome window),
Best to use lower case for variable names and upper case for class names. E.g. I renamed Main_layout to main_layout. Look at the difference in syntax highlighting by Stack Overflow above.

How to bring PyQt QProcess window to front?

I am starting a QProcess and would like to bring the resulting window on top everytime a command is written to QProcess.
However, if my process is "gnuplot.exe" (Win7) the plot window will be updated but always stays behind the PyQt window. I haven't yet found a way to bring it to front, or make it active, or put the focus on, or however you would call it.
Something like
self.process. ...
raise(), show(), activateWindow(), SetForegroundWindow, WindowStaysOnTopHint ... ???
These posts didn't help me further
How to find the active PyQt window and bring it to the front
https://forum.qt.io/topic/30018/solved-bring-to-front-window-application-managed-with-qprocess/3
Bring QProcess window to front (running Qt Assistant)
Here is the code:
import sys
from PyQt5.QtCore import QProcess, pyqtSlot
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
import subprocess
class MyWindow(QWidget):
def __init__(self):
super(MyWindow,self).__init__()
self.setGeometry(100,100,500,500)
self.button1 = QPushButton('New plot',self)
self.button1.clicked.connect(self.button1_clicked)
self.process = QProcess(self)
self.process.start(r'C:\Programs\gnuplot\bin\gnuplot.exe', ["-p"])
self.n = 1
def button1_clicked(self):
plotcmd = "plot x**"+str(self.n)+"\n"
self.process.write(plotcmd.encode())
self.n += 1
response = self.process.readAllStandardOutput()
print(str(response, encoding="utf-8"))
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyle("plastique")
window = MyWindow()
window.show()
sys.exit(app.exec_())
Based on the comment and links from #buck54321, I got to the following version which seems to (almost) work.
The solution in the above links seem to be more general and a bit too complicated for my case, since I know the exact name of my window which I want to bring to front.
To my original code I just had to add three lines. I was not familiar with win32gui.
However, with my code, only after the button is pressed the second time the window will be brought to front. After the first button press hwnd is still 0 which leads to a crash. I guess this is because gnuplot.exe will start without opening a window, and opens a window only after the first command has been sent.
If anybody can show me how to bring the window to front even after the first button click that would be great.
If there are limitation or improvements I would be happy to learn about them.
Here is the code:
import sys
from PyQt5.QtCore import QProcess, pyqtSlot
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
import subprocess
import win32gui ####### new line
class MyWindow(QWidget):
def __init__(self):
super(MyWindow,self).__init__()
self.setGeometry(100,100,500,500)
self.button1 = QPushButton('New plot',self)
self.button1.clicked.connect(self.button1_clicked)
self.process = QProcess(self)
self.process.start(r'C:\Programs\gnuplot\bin\gnuplot.exe', ["-p"])
self.n = 1
def button1_clicked(self):
plotcmd = "set term wxt 999\n plot x**"+str(self.n)+"\n"
self.process.write(plotcmd.encode())
self.n += 1
hwnd = win32gui.FindWindow(None, "Gnuplot (window id : 999)") ####### new line
response = self.process.readAllStandardOutput()
print(str(response, encoding="utf-8"))
if hwnd != 0: win32gui.SetForegroundWindow(hwnd) ####### new line
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyle("plastique")
window = MyWindow()
window.show()
sys.exit(app.exec_())

App crashes when using QWidgets.QMessageBox

So I've been trying my luck with PyQT5 to give a GUI to an app I've been working on.
I've encountered an issue with QMessageBox feature.
I've been trying to create an "Exit" Action on the MenuBar of the app.
And at first I only made it exit when clicked and it worked.
Now I want to make it give a pop up message of "Are you sure?", which is exactly what the QMessageBox does. So this is my code now:
class Window(QtWidgets.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.ui = uic.loadUi('rent_creation.ui', self)
self.home()
def home(self):
self.ui.actionExit.triggered.connect(self.close_application)
self.show()
def close_application(self):
choice = QMessageBox.question(self, 'Quit?',
"Are you sure you want to quit?",
QMessageBox.Yes | QMessageBox.No)
if choice == QMessageBox.Yes:
sys.exit()
else:
pass
Now every time I click on the Exit button when I run this code, The Python crashes.
I'm not sure what I'm doing wrong... I've been looking around the internet and it all look well.... I've tried all the variation possible of passing QmessageBox (for example I tried adding QWidgets.QMessageBox.Yes/No and it didn't fix this issue).
I've been following a tutorial on the internet where This code is practically the same as his, and it works for him in the tutorial somehow.
caveat: I am on linux, so things are likely a bit different.
However I wouldn't be surprised if the problem is related with the fact that you use sys.exit to quit the GUI. You probably should cleanly close the window, the QApplication and then exit the program.
The following example might solve your issue. Since I don't have you ui file, I just added a menu action to close the the window and connect it with the QMainWindow.close slot and then override the closeEvent method. See the comments in the code:
import sys
from PyQt5 import QtWidgets
class Window(QtWidgets.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.home()
def home(self):
# add a menu bar with a File menu and a Close action
menu_bar = QtWidgets.QMenuBar(self)
menu = QtWidgets.QMenu('File', menu_bar)
menu_bar.addMenu(menu)
action = menu.addAction('Close')
# connect the Close action with the QMainWindow.close slot
action.triggered.connect(self.close)
self.setMenuBar(menu_bar)
def closeEvent(self, event):
"""override the QMainWindow.closeEvent method to:
* fire up a QMessageBox with a question
* accept the close event if the user click yes
* ignore it otherwise.
Parameters
----------
event : QtCloseEvent
emitted when someone or something asks to close the window
"""
if self.ask_quit():
event.accept()
else:
event.ignore()
def ask_quit(self):
choice = QtWidgets.QMessageBox.question(self, 'Quit?',
"Are you sure you want to quit?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
return choice == QtWidgets.QMessageBox.Yes
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Window()
w.resize(250, 150)
w.move(300, 300)
w.setWindowTitle('Simple')
w.show()
sys.exit(app.exec_())
The above way of closing the window, i.e. using the closeEvent and connect the menu action to close, has the advantage that the confirmation box is opened every time someone asks to close the window, independently of the method: you get the message box also clicking on the window X button or with alt+F4
Edit: example of how to cleanly close the QApplication only from the Close menu. This should be more in line with the original behavior of the app in the question (see comment).
class Window(QtWidgets.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.home()
def home(self):
menu_bar = QtWidgets.QMenuBar(self)
menu = QtWidgets.QMenu('File', menu_bar)
menu_bar.addMenu(menu)
action = menu.addAction('Close')
# connect the Close menu to the ``ask_quit`` slot to ask and exit the
# application on "yes"
action.triggered.connect(self.ask_quit)
self.setMenuBar(menu_bar)
def closeEvent(self, event):
"""Ignore all ways of closing"""
event.ignore()
#QtCore.pyqtSlot()
def ask_quit(self):
choice = QtWidgets.QMessageBox.question(self, 'Quit?',
"Are you sure you want to quit?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
if choice == QtWidgets.QMessageBox.Yes:
QtWidgets.QApplication.quit()

PyQt4: Second window does not show

Here is the code open.py (main file) that opens a login page that has a button which when clicked closes the login page and opens a menu page:
from PyQt4 import QtGui
import sys
from loginpage import Ui_loginPage
from menu import Ui_Menu
class Form1(QtGui.QWidget, Ui_loginPage):
def __init__(self,parent=None):
QtGui.QWidget.__init__(self,parent)
self.setupUi(self)
self.login.clicked.connect(self.handleButton)
self.window2 = None
def handleButton(self):
self.close()
if self.window2 is None:
self.window2 = menu(self)
self.window2.show()
sys.exit(app.exec_())
class menu(QtGui.QWidget, Ui_Menu):
def __init__(self,parent=None):
QtGui.QWidget.__init__(self,parent)
self.setupUi(self)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Form1()
window.show()
sys.exit(app.exec_())
The code runs fine without any error, but when i click the login button, the login page closes but the menu page doesn't appear. It just doesn't show at all.
If the menu window is a child of the Form1 window, it will be hidden when the Form1 window is closed - so don't give it a parent. You also shouldn't be calling sys.exit(app.exec_()) again, so your code should look like this:
class Form1(QtGui.QWidget, Ui_loginPage):
...
def handleButton(self):
self.close()
if self.window2 is None:
self.window2 = menu()
self.window2.show()
PS: please see this answer for a more complete example of how to implement a login dialog in PyQt.

How to see signals from QWidgets inside dynamically created QTabWidget pages?

EDIT : I've come up with a solution, and it's much more straightforward than I thought. Original code and question at the top. My solution after "The Question" below..
The Example
from PyQt4 import QtGui, QtCore
from example_Ui import Ui_MainWindow
from filler_Ui import Form
class TabFiller(Form):
def __init__(self, parent=None):
Form.__init__(self, parent)
def TabButtonClicked(self):
print("Tab button pressed.")
def LineEditChanged(self):
print("LineEdit contents edited in tab page!")
class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
tab_filler = [] # create empty list for tab contents
tab_page = [] # create empty list for tab page
tab_count = 0
def CreateNewTab(self):
tab_title = "New Tab : " + str(self.tab_count)
self.tab_filler.append(TabFiller())
self.tab_filler[self.tab_count].label.setText(tab_title)
self.tab_page.append(self.tab_filler[self.tab_count])
self.tabWidget.addTab(self.tab_page[self.tab_count], tab_title)
self.tab_count += 1
def MainButtonPressed(self):
self.CreateNewTab()
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
MainWindow contains a QTabWidget, which is a Button. clicked() signal has been defined in QtDesigner to be sent to the MainButtonPressed() function inside the MainWindow class.
Form widget also created in QTdesigner. Used to fill additional Tab Pages.
This contains a Button widget, and a LineEdit Widget.
The Question
I can't get my head around how I can tell which widget has been clicked or edited in each tab.
I know that each Tab Page is stored in the list called tab_page.
Within the MainWindow class, how would I receive a clicked() or finishedEditing() signal for a given widget in a currently active tab?
A Solution
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PyQt4 import QtGui
from example_Ui import Ui_MainWindow
from filler_Ui import Form
class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
tab_index = 1 # 1 because we already made a default tab in QtDesigner
def LineEditChanged(self):
findWidget = self.tabWidget.widget(self.tabWidget.currentIndex()).findChildren(QtGui.QLineEdit, "lineEdit")
if findWidget[0].isModified() == True:
print("LineEdit contents edited in tab page!")
print("Name of page edited :", "'", self.tabWidget.tabText(self.tabWidget.currentIndex()),"'")
def TabButtonPressed(self):
print("YOU DID IT!")
print("Current Tab Index = ", self.tabWidget.currentIndex())
def CreateNewTab(self, tabNum):
tab_title = "New Tab : " + str(self.tab_index)
self.tabWidget.addTab(Form(), tab_title)
def MainButtonPressed(self):
self.CreateNewTab(self.tab_index)
findWidget = self.tabWidget.widget(self.tab_index).findChildren(QtGui.QPushButton, "tabButton")
findWidget[0].clicked.connect(self.TabButtonPressed)
findWidget = self.tabWidget.widget(self.tab_index).findChildren(QtGui.QLineEdit, "lineEdit")
findWidget[0].editingFinished.connect(self.LineEditChanged)
self.tab_index += 1
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
Using this there's no need for storing each tab page object in a list. You basically use the QTabWidget to index your pages, and off you go.
If anyone has a more elegant way than this, please inform ;)
As outlined in my edited question, I did find the solution to this, which is to use the QTabWidget to "index" each dynamically created tab page.
In QtDesigner I created a main window with one QTabWidget and one button thusly;
Here's the object tree for that;
NOTE: I added a signal/slot for the "Click Me!" button in QtDesigner, so that when that button is clicked, the MainButtonPressed function is called.
To fill the tab pages, I also created a Form in QtDesigner, with a button and a QLineEdit widget;
And the object tree for that;
I'll reproduce the code here. NOTE: I've now updated this answer to use findChild rather than findChildren above:
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PyQt4 import QtGui
from example_Ui import Ui_MainWindow
from filler_Ui import Form
class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
tab_index = 1 # 1 because we already made a default tab in QtDesigner
def LineEditChanged(self):
findWidget = self.tabWidget.widget(self.tabWidget.currentIndex()).findChild(QtGui.QLineEdit, "lineEdit")
if findWidget.isModified() == True:
print("LineEdit contents edited in tab page!")
print("Name of page edited :", "'", self.tabWidget.tabText(self.tabWidget.currentIndex()),"'")
def TabButtonPressed(self):
print("YOU DID IT!")
print("Current Tab Index = ", self.tabWidget.currentIndex())
def CreateNewTab(self, tabNum):
tab_title = "New Tab : " + str(self.tab_index)
self.tabWidget.addTab(Form(), tab_title)
def MainButtonPressed(self):
self.CreateNewTab(self.tab_index)
findWidget = self.tabWidget.widget(self.tab_index).findChild(QtGui.QPushButton, "tabButton")
findWidget.clicked.connect(self.TabButtonPressed)
findWidget = self.tabWidget.widget(self.tab_index).findChild(QtGui.QLineEdit, "lineEdit")
findWidget.editingFinished.connect(self.LineEditChanged)
self.tab_index += 1
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
When run, pressing the "Click Me!" button on the main tab page creates a new tab, and adds the contents of the "filler" page to it.
The variable tab_index keeps track of how many tabs there are and allows you to reference the contents of each tab.
To find a widget in a tab, you use the findChild function of Qt;
findWidget = self.tabWidget.widget(self.tab_index).findChild(QtGui.QPushButton, "tabButton")
Finding a specific widget is straightforward. You specify the type of widget you're looking for (QtGui.QPushButton) , and the name you assigned it in QtDesigner (tabButton)
In this case the found widget can be referenced by the variable findWidget.
You can then connect signals to function slots as usual;
findWidget.clicked.connect(self.TabButtonPressed)
In this case I used the new-style signal connection method to connect the clicked() signal to a function named TabButtonPressed in my program.
Rinse and repeat for each widget on the Tab Page you wish to do something with.
After that, it really is plain sailing ;)
I hope this information helps others in their GUI endeavours. You can probably use the same technique with the QToolBox widget.

Resources