the widget does not close/quit with the main window - python

I have the following python code where the main window has a widget using PyQt4
import os
import sys
from PyQt4 import QtGui, QtCore, Qt
class Widget(QtGui.QLabel):
def __init__(self):
super(FringeFrame, self).__init__()
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.showFullScreen()
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.widget = Widget()
def main():
app = QtGui.QApplication(sys.argv)
mywin = MainWindow()
mywin.show()
sys.quit(app.exec_ ())
if __name__ == '__main__':
main()
the issue here is that i want widget and mywin to have their own window, it works that way, but when I close mywin, the widget is not closed with mywin.
How should i do it?

You can just override the QMainWindow's closeEvent:
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.widget = Widget()
def closeEvent(self, event):
self.widget.close()

Related

PyQt5 Access MainWindow from Another Window

I am newbie and in that project I am stuck at one point. How can I access functions in MainWindow class from another Window.
For example I need to access Main.list_refresh when Child_EditAccount.btn_EDIT_ACCOUNT_3.clicked . I tried something like signal & slot but doesn't work. I maked 3 different Window with pyQt Designer and I need to link the three together.
new.py
# Designs
from design import Ui_MainWindow
from design_addAccount import Ui_MainWindow_Add
from design_editAccount import Ui_MainWindow_Edit
# Modules
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import pyqtSignal, pyqtSlot
import sqlite3
con = sqlite3.connect('shorts.sqlite3')
cur = con.cursor()
class Main(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(Main, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.account_process = AccountProcess()
self.ui.lst_accounts.itemClicked.connect(self.account_process.edit_account)
self.ui.btn_delete_account.clicked.connect(self.account_process.delete_account)
self.ui.btn_edit_account.clicked.connect(self.account_process.edit_account)
self.ui.btn_add_account.clicked.connect(self.account_process.add_account)
self.ui.btn_refresh.clicked.connect(self.list_refresh)
self.refresh_trigger = Child_EditAccount()
self.refresh_trigger.buttonClicked.connect(self.list_refresh)
def list_refresh(self):
self.ui.lst_accounts.clear()
for row in cur.execute("SELECT * FROM users"):
self.ui.lst_accounts.addItem('%s' % (str(row[3])))
class Child_AddAccount(QtWidgets.QMainWindow,Ui_MainWindow_Add):
def __init__(self, parent=None):
super(Child_AddAccount, self).__init__()
self.ui = Ui_MainWindow_Add()
self.ui.setupUi(self)
class Child_EditAccount(QtWidgets.QMainWindow,Ui_MainWindow_Edit):
buttonClicked = pyqtSignal()
def __init__(self, parent=None):
super(Child_EditAccount, self).__init__()
self.ui = Ui_MainWindow_Edit()
self.ui.setupUi(self)
def edit_infos(self, username, password, nickname, id):
self.id = id
self.ui.txtBox_username_3.insert(username)
self.ui.txtBox_password_3.insert(password)
self.ui.txtBox_nickname_3.insert(nickname)
self.ui.btn_EDIT_ACCOUNT_3.clicked.connect(self.update_by_id)
def update_by_id(self):
cur.execute('UPDATE users SET username="%s", password="%s", nickname="%s" WHERE id=%s' % (self.ui.txtBox_username_3.text(), self.ui.txtBox_password_3.text(), self.ui.txtBox_nickname_3.text(), self.id))
con.commit()
self.buttonClicked.emit()
class AccountProcess(QtCore.QObject):
def add_account(self):
self.child_add = Child_AddAccount()
self.child_add.show()
print('Pressed edit button')
def delete_account(self):
print('Pressed delete button')
def edit_account(self, item):
self.child_edit = Child_EditAccount()
for i in cur.execute(f"SELECT * FROM users WHERE nickname=\"{item.text()}\";"):
self.child_edit.edit_infos(str(i[1]), str(i[2]), str(i[3]), str(i[0]))
self.child_edit.show()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
main = Main()
main.show()
sys.exit(app.exec_())
You can use a pyqtSignal to do what you want.
in the Child_EditAccount class add a pyqtsignal
class Child_EditAccount(QtWidgets.QMainWindow,Ui_MainWindow_Add):
buttonClicked = pyqtSignal()
def __init__(self, parent=None):
before the __init__, then when you need to trigger the function use
buttonClicked.emit()
in the function where the button is used in the Child_EditAccount class
Then in the main window, you can create a connection to a function via the pyqtsignal
self.Child_EditAccount.buttonClicked.connect(self.myfunction)
in the __init__ of the main class. where self.Child_EditAccount is the instance of your Child_EditAccount class and self.function is the function you want to trigger Main.list_refresh
You can also create a signal from QtDesigner itself when you create the Ui_MainWindow_Edit
https://doc.qt.io/qt-5/designer-connection-mode.html
be carefull, Child_EditAccount inherite from Ui_MainWindow_Add instead of Ui_MainWindow_Edit probably.
____
what you can also do is link the sigal of the button directly in the main program as in this little example
# Modules
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import pyqtSignal, pyqtSlot
class Main2(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(Main2, self).__init__()
self.centralWidget = QtWidgets.QWidget()
self.setCentralWidget(self.centralWidget)
self.but= QtWidgets.QPushButton('tdvfdbt')
self.l = QtWidgets.QHBoxLayout()
self.l.addWidget(self.but)
self.centralWidget.setLayout(self.l)
class Main(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(Main, self).__init__()
self.win = Main2()
self.a = self.win.show()
self.win.but.clicked.connect(self.myfunction)
def myfunction(self):
print('called from sub window')
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
ex = Main(app)
ex.setWindowTitle('Model micro GUI')
# ex.showMaximized()
ex.show()
sys.exit(app.exec())
if you know the name of the button widget of the Child_EditAccount class you can link it via
self.Child_EditAccount.btn_EDIT_ACCOUNT_3.clicked.connect(self.myfunction)
##______
Put the pystsignal in the AccountProcess class
class AccountProcess(QtCore.QObject):
buttonClicked = pyqtSignal()
def add_account(self):
self.child_add = Child_AddAccount()
self.child_add.show()
print('Pressed edit button')
def delete_account(self):
print('Pressed delete button')
def edit_account(self, item):
self.child_edit = Child_EditAccount()
for i in cur.execute(f"SELECT * FROM users WHERE nickname=\"{item.text()}\";"):
self.child_edit.edit_infos(str(i[1]), str(i[2]), str(i[3]), str(i[0]))
self.child_edit.buttonClicked.connect(self.EmitAgain)
self.child_edit.show()
def EmitAgain(self):
self.buttonClicked.emit()
Then use it in the main class
class Main(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(Main, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.account_process = AccountProcess()
self.account_process.buttonClicked.connect(self.list_refresh)

How to change default position of toolbar?

I'm using PyQt5, QMainWindow and I want to change default position of toolbar to the right. How can I do it?
User can carry toolbar to the edges of the window using mouse, but how can I do it using program?
def initUI(self):
self.toolbar = self.addToolBar('Example')
You have to use the addToolBar method as shown below:
import sys
from PyQt5 import QtCore, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.initUI()
def initUI(self):
self.toolbar = QtWidgets.QToolBar("Example")
self.addToolBar(QtCore.Qt.RightToolBarArea, self.toolbar)
self.toolbar.addAction("action 1")
self.toolbar.addAction("action 2")
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())

How can I inherit between classes in pyqt?

In order to track progress, this is the third question about practicing with different classes in PyQt5 .Here are the links to my previous questions:opening a new window, Open a file from main window to a new window in PyQt5 (in different files).
I'm trying to work with two classes, one with one button and when it's pressed it will load a file and show the text in a QTextEdit in other class.
In the first questions I was suggested that as an alternative to work with more classes, they can inherit from QMainWindow so I looked for more info for doing this: PyQt class inheritance
The second question code did worked but it would show both windows at the same time, so this question: PyQt: How to hide QMainWindow guided me to write this code (I attatch this here because it's a little bit different from the one in the link, plus I apply what it says in the answer):
import sys, os
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton
class Dialog_02(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Dialog_02, self).__init__(parent, QtCore.Qt.Window)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
myBoxLayout = QVBoxLayout()
Button_02 = QPushButton ("Show Dialog 01")
myBoxLayout.addWidget(Button_02)
self.setLayout(myBoxLayout)
self.setWindowTitle('Dialog 02')
Button_02.clicked.connect(self.closeAndReturn)
def closeAndReturn(self):
self.close()
self.parent().show()
class Dialog_01(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Dialog_01, self).__init__()
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
myBoxLayout = QVBoxLayout()
Button_01 = QPushButton ("Show Dialog 02")
myBoxLayout.addWidget(Button_01)
self.setLayout(myBoxLayout)
self.setWindowTitle('Dialog 01')
Button_01.clicked.connect(self.callAnotherQMainWindow)
def callAnotherQMainWindow(self):
self.hide()
self.dialog_02 = Dialog_02(self)
self.dialog_02.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
dialog_1 = Dialog_01()
dialog_1.show()
sys.exit(app.exec_())
In this code I'm not inheriting, but it works fine.
The issue is that when I try to follow the same syntax in the original question code, it won't run, I'm not sure I'm getting inheritances fine.
import sys
import os
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtWidgets import QPushButton, QVBoxLayout, QTextEdit, QHBoxLayout, QLabel, QMainWindow, QAction, QFileDialog
class SecondWindow(QWidget):
def __init__(self, Window):
super(SecondWindow, self).__init__(parent, QtCore.Qt.Window)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.text = QTextEdit(self)
self.btn_return= QPushButton("Return")
self.init_ui()
def init_ui(self):
v_layout = QVBoxLayout(self)
v_layout.addWidget(self.text)
v_layout.addWidget(self.btn_return)
self.setLayout(v_layout)
self.setWindowTitle('Opened Text')
self.btn_return.clicked.connect(self.closeAndReturn)
def closeAndReturn(self):
self.close()
self.parent().show()
class Window(QMainWindow):
textChanged = QtCore.pyqtSignal(str)
def __init__(self, *args):
super(Window, self).__init__()
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.img = QLabel()
self.load_file= QPushButton('Load')
self.width = 400
self.height = 150
self.init_ui()
def init_ui(self):
self.img.setPixmap(QtGui.QPixmap("someimage.png"))
h_layout = QHBoxLayout()
v_layout = QVBoxLayout()
h_final = QHBoxLayout()
h_layout.addWidget(self.img)
v_layout.addWidget(self.load_file)
h_final.addLayout(h_layout)
h_final.addLayout(v_layout)
self.load_file.clicked.connect(self.loadafile)
self.setLayout(h_final)
self.setWindowTitle('Main Window')
self.setGeometry(600,150,self.width,self.height)
#QtCore.pyqtSlot()
def loadafile(self):
filename = QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME'))
with open(filename[0], 'r') as f:
file_text = f.read()
self.textChanged.emit(file_text)
self.hide()
self.dialog_02 = SecondWindow(self)
self.dialog_02.show()
def main():
app = QApplication(sys.argv)
main = Window()
s = SecondWindow(main)
main.textChanged.connect(s.text.append)
main.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You are coupling many classes: What happens if at a moment SecondWindow does not have a parent? Well, your code will have problems and you will have to modify it a lot so that it works correctly. So first it is to design the behavior of each class, for example SecondWindow has to warn the other windows that it was clicked, it has to have a method that updates the text. Similarly, Window must notify that there is new text available.
On the other hand QMainWindow already has a predefined layout so you must create a centralwidget where you place the other widgets.
import os
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class SecondWindow(QtWidgets.QWidget):
closed = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(SecondWindow, self).__init__(parent, QtCore.Qt.Window)
self.text = QtWidgets.QTextEdit()
self.btn_return= QtWidgets.QPushButton("Return")
self.init_ui()
def init_ui(self):
v_layout = QtWidgets.QVBoxLayout(self)
v_layout.addWidget(self.text)
v_layout.addWidget(self.btn_return)
self.setWindowTitle('Opened Text')
self.btn_return.clicked.connect(self.close)
self.btn_return.clicked.connect(self.closed)
#QtCore.pyqtSlot(str)
def update_text(self, text):
self.text.setText(text)
self.show()
class Window(QtWidgets.QMainWindow):
textChanged = QtCore.pyqtSignal(str)
def __init__(self, *args):
super(Window, self).__init__()
self.img = QtWidgets.QLabel()
self.load_file= QtWidgets.QPushButton('Load')
self.width = 400
self.height = 150
self.init_ui()
def init_ui(self):
self.img.setPixmap(QtGui.QPixmap("someimage.png"))
self.load_file.clicked.connect(self.loadafile)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
h_layout = QtWidgets.QHBoxLayout(central_widget)
h_layout.addWidget(self.img)
h_layout.addWidget(self.load_file)
self.setWindowTitle('Main Window')
self.setGeometry(600,150,self.width,self.height)
#QtCore.pyqtSlot()
def loadafile(self):
filename, _ = QtWidgets.QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME'))
if filename:
with open(filename, 'r') as f:
file_text = f.read()
self.textChanged.emit(file_text)
self.close()
def main():
app = QtWidgets.QApplication(sys.argv)
main = Window()
s = SecondWindow()
main.textChanged.connect(s.update_text)
s.closed.connect(main.show)
main.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

mouseMoveEvent() while cursor is on button

I have to activate some function, when the cursor is moving. So, I used self.setMouseTracking(True) in MainWidget. But in this way mouseMoveEvent() works only when there is an empty form under cursor. I tried to create another widget over main, but it doesnt work at all.
class ClickButton(QPushButton):
def __init__(self, text, window):
...
def run(self):
...
class Window(QWidget):
def __init__(self):
super().__init__()
self.setGeometry(0, 0, 1000, 1000)
self.setMouseTracking(True)
self.clickers = [ClickButton('OK', self) for i in range(8)]
def mouseMoveEvent(self, ev):
for e in self.clickers:
e.run()
Whats to do?
If you want to detect the position of the mouse even when the mouse is on top of a child, a possible option is to use an event filter.
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
w_ = QtWidgets.QWidget()
lay_w = QtWidgets.QHBoxLayout(w_)
for c in (QtWidgets.QPushButton(), QtWidgets.QLineEdit()):
lay_w.addWidget(c)
lay = QtWidgets.QVBoxLayout(self)
for w in (QtWidgets.QPushButton(), QtWidgets.QLineEdit(), QtWidgets.QTextEdit(), w_):
lay.addWidget(w)
for ws in self.findChildren(QtWidgets.QWidget) + [self]:
ws.setMouseTracking(True)
ws.installEventFilter(self)
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.MouseMove:
p_respect_to_window = self.mapFromGlobal(obj.mapToGlobal(event.pos()))
print(p_respect_to_window)
return super(Widget, self).eventFilter(obj, event)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
On the other hand if you only want to do it in only one type of custom widget it is better to overwrite the mouseMoveEvent method of the custom widget:
from PyQt5 import QtCore, QtGui, QtWidgets
class ClickButton(QtWidgets.QPushButton):
def __init__(self, text, parent=None):
super(ClickButton, self).__init__(text=text, parent=parent)
self.setMouseTracking(True)
def mouseMoveEvent(self, event):
self.run()
super(ClickButton, self).mouseMoveEvent(event)
def run(self):
print("call to run function in button{} and time: {}".format(self.text(),
QtCore.QDateTime.currentDateTime().toString()))
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
lay = QtWidgets.QVBoxLayout(self)
for i in range(10):
w = ClickButton(str(i), self)
lay.addWidget(w)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())

Pyside2 how to open the dialog but not always on top

I create a main frame and a pushbutton ,
and let button clicked to open a dialog , but the dialog always on top ,
I try to used setWindowsFlag but not work.
from PySide2.QtWidgets import QApplication,QMainWindow,QTabWidget,QWidget
from PySide2.QtWidgets import QMessageBox,QFileDialog,QErrorMessage
from PySide2 import QtCore, QtGui, QtWidgets
class UI_Test20(object):
def setupUi(self, Test202):
Test202.setObjectName("Test202")
Test202.resize(100,100)
self.centralwidget = QtWidgets.QWidget(Test202)
self.centralwidget.setObjectName("centralwidget")
self.pb = QtWidgets.QPushButton(self.centralwidget)
self.pb.setText('push button!')
Test202.setCentralWidget(self.centralwidget)
self.pb.clicked.connect(self.btnClicked)
self.retranslateUi(Test202)
QtCore.QMetaObject.connectSlotsByName(Test202)
def retranslateUi(self, Test202):
Test202.setWindowTitle(QtWidgets.QApplication.translate("Test202", "MainWindow", None, -1))
def btnClicked(self):
ui = Ui_Dialog1(self)
ui.show()
class Test20(QMainWindow, UI_Test20) :
def __init__(self, parent):
super(Test20, self).__init__(parent)
self.setupUi(self)
and the dialog code
class Ui_Dialog1(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Ui_Dialog1, self).__init__(parent)
self.p = parent
self.setupUi(self)
def setupUi(self, Dialog1):
Dialog1.setObjectName("Dialog1")
Dialog1.resize(333, 173)
main
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ui = Test20(None)
ui.show()
sys.exit(app.exec_())
The problem is caused by the fact that the main window is the parent of the QDialog, so it must be removed, but if that is done, the garbage collector will delete it, so the QDialog member of the class must be made:
def btnClicked(self):
self.ui = Ui_Dialog1()
self.ui.show()
Plus: the correct thing is not to modify the design so I move the connection and the slot associated with the clicked button pb and if we want it to close when the window closes we overwrite the closeEvent() method:
class Test20(QMainWindow, UI_Test20):
def __init__(self, parent):
super(Test20, self).__init__(parent)
self.setupUi(self)
self.pb.clicked.connect(self.btnClicked)
def btnClicked(self):
self.ui = Ui_Dialog1()
self.ui.show()
def closeEvent(self, *args, **kwargs):
self.ui.close()
QMainWindow.closeEvent(self, *args, **kwargs)

Categories

Resources