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)
Related
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)
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_())
I have a mainframe and clicked the pushbutton to open a new dialog ,
the dialog have a timer , let the timer to show message at 3sec interval ,
but if I click the top right close button to close the dialog , the messagebox still run ,
how to avoid it ?
Thanks!
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):
self.ui = Ui_Dialog1(self)
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)
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.update)
self.timer.start(3000)
def update(self):
QMessageBox.information(self,'123','123')
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_())
When you call show() method with QDialog your dialog will be modeless and will be hidden not deleted when it's close. To make sure your dialog deleted add one more line in your dialog init method:
def __init__(self, parent=None):
super(Ui_Dialog1, self).__init__(parent)
self.p = parent
self.setupUi(self)
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.update)
self.timer.start(3000)
#Make sure that your dialog will be deleted not hidden
self.setAttribute(QtGui.Qt.WA_DeleteOnClose)
I have 2 QListWidget lists, List2 is being filled when some item has been selected from List1
The problem is that, before filling List2 i have to do alot of tasks which freezes my UI for about 5 seconds which is too annoying, i want to make it fill List2 with QThread but it's not working since before initilizing whole class I'm getting an annoying error
from ui import Ui_Win
from PyQt4 import QtGui, QtCore
class GenericThread(QtCore.QThread):
def __init__(self, parent=None):
QtCore.QThread.__init__(self, parent)
def __del__(self):
self.quit()
self.wait()
def run(self):
self.emit( QtCore.SIGNAL('itemSelectionChanged()'))
return
class MainUI(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self)
self.ui = Ui_Win()
self.ui.setupUi(self)
...
genericThread = GenericThread(self)
self.connect(genericThread, QtCore.SIGNAL("itemSelectionChanged()"), self.fill_List2 )
genericThread.start()
def fill_List2(self):
self.ui.List2.clear()
list1SelectedItem = str(self.ui.List1.currentItem().text()) # ERROR HERE
Traceback:
# AttributeError: 'NoneType' object has no attribute 'text'
This accures because self.ui.List1.currentItem().text() is None
Why this fucntion is being called before triggering itemSelectionChanged signal?
from ui import Ui_Win ## ui.py is a file that has been generated from Qt Designer and it contains main GUI objects like QListWidget
from PyQt4 import QtGui, QtCore
class GenericThread(QtCore.QThread):
def __init__(self, parent=None, listIndex=0):
QtCore.QThread.__init__(self, parent)
self.listIndex = listIndex
def __del__(self):
self.quit()
self.wait()
def run(self):
if self.listIndex == 2:
for addStr in Something:
#Some long stuff
self.emit( QtCore.SIGNAL('fillListWithItems(QString, int'), addStr, self.listIndex)
class MainUI(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self)
self.ui = Ui_Win()
self.ui.setupUi(self)
...
self.ui.List1.list1SelectedItem.connect(self.fill_List2)
#QtCore.pyqtSlot(QString, int)
def self.fillListWithItems(addStr, lstIdx):
if lstIdx==2:
self.ui.List2.addItem(addStr)
def fill_List2(self):
self.ui.List2.clear()
list1SelectedItem = str(self.ui.List1.currentItem().text())
genericThread = GenericThread(self, listIndex=2)
self.connect(genericThread, QtCore.SIGNAL("fillListWithItems(QString, int)"), self.fillListWithItems )
genericThread.start()
Thanks #ekhumoro
Your problem is that simply starting the thread is firing the itemSelectionChanged() signal (because it is in the run function), which you have connected to your fill_List2() function. You need to connect the itemSelectionChanged event on List1 to a SLOT that will trigger the thread to do the heavy computation and update List2.
I've had to make a number of assumptions about what ui is and what List1 and List2 are and how they're set up, but here is a working example. I've replaced the heavy computation in your GenericThread with a simple 2 second delay.
If I've misinterpreted / made bad assumptions, please update the question and leave a comment
test_slotting.py
from ui import Ui_Win
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import pyqtSlot
from PyQt4.QtGui import *
import time
class GenericThread(QtCore.QThread):
def __init__(self, parent=None):
QtCore.QThread.__init__(self, parent)
def __del__(self):
self.quit()
self.wait()
def run(self):
#Do all your heavy processing here
#I'll just wait for 2 seconds
time.sleep(2)
self.emit( QtCore.SIGNAL('itemSelectionChanged()'))
return
class MainUI(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self)
self.ui = Ui_Win()
self.ui.setupUi(self)
self.ui.List1 = QListWidget(self)
self.ui.List2 = QListWidget(self)
hbox = QtGui.QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(self.ui.List1)
hbox.addWidget(self.ui.List2)
self.ui.centralWidget.setLayout(hbox)
self.ui.List1.addItems(['alpha','beta','gamma','delta','epsilon'])
self.ui.List2.addItems(['Item1','Item2'])
self.ui.List1.itemSelectionChanged.connect(self.start_heavy_processing_thread)
#pyqtSlot()
def start_heavy_processing_thread(self):
genericThread = GenericThread(self)
self.connect(genericThread, QtCore.SIGNAL("itemSelectionChanged()"), self.fill_List2 )
genericThread.start()
def fill_List2(self):
self.ui.List2.clear()
list1SelectedItem = str(self.ui.List1.currentItem().text())
self.ui.List2.addItem(list1SelectedItem)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
MainWindow = MainUI()
MainWindow.show()
sys.exit(app.exec_())
ui.py
from PyQt4 import QtCore, QtGui
class Ui_Win(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(416, 292)
self.centralWidget = QtGui.QWidget(MainWindow)
self.centralWidget.setObjectName("centralWidget")
MainWindow.setCentralWidget(self.centralWidget)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
MainWindow = QtGui.QMainWindow()
ui = Ui_Win()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
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()