How to communicate or switch between two windows in PyQt? - python

I am developing an application using python and Qt.
I have designed 2 Main windows ie..QMainWindow (Not QWidget or QDialog) using Qt.
Let it be.
1.LoginWindow -- LoginUI(Qt)
2.StuffWindow --- StuffUI
First i should display Login Window.
Then i should pass the username to StaffWindow (username needed for managing stuffs)
StaffWindow should be shown and LoginWindow Should be Closed..
How can i achieve this..? Help me..

Regardless of your description, I think your LoginWindow should be a QDialog, and your StuffWIndow be the MainWindow, and function like this...
Your StuffWindow MainWindow should be created (not shown)
Call a login() method that creates and exec_() your login QDialog as a application MODAL dialog
Start the app.exec_() event loop now, and wait for the user to interact with login
User interacts with login dialog, and the result of the dialog closing will then allow your app to check its values and choose to show its main interface.
Here is a quick outline:
class MainWindow():
def login():
loginDialog = LoginDialog()
# this is modal. wait for it to close
if loginDialog.exec_():
# dialog was accepted. check its values and maybe:
self.show()
else:
# maybe reshow the login dialog if they rejected it?
loginDialog.exec_()
if __name__ == "__main__":
app = QApp
win = MainWindow()
win.login()
app.exec_()

I do agree most of the points jdi raised, but I prefer a slightly different approach.
LoginWindow should be a QDialog started as MODAL.
Check the return of exec_() (i.e. accept/reject) for login or cancel/quit.
Check the login inside the LoginWindow
If successful login, launch MainWindow with parameters supplied
I started coding a simple example before seeing jdi's answer. I might as well put it here.
import sys
from PyQt4 import QtGui, QtCore
class LoginDialog(QtGui.QDialog):
def __init__(self, parent=None):
super(LoginDialog, self).__init__(parent)
self.username = QtGui.QLineEdit()
self.password = QtGui.QLineEdit()
loginLayout = QtGui.QFormLayout()
loginLayout.addRow("Username", self.username)
loginLayout.addRow("Password", self.password)
self.buttons = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
self.buttons.accepted.connect(self.check)
self.buttons.rejected.connect(self.reject)
layout = QtGui.QVBoxLayout()
layout.addLayout(loginLayout)
layout.addWidget(self.buttons)
self.setLayout(layout)
def check(self):
if str(self.password.text()) == "12345": # do actual login check
self.accept()
else:
pass # or inform the user about bad username/password
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.label = QtGui.QLabel()
self.setCentralWidget(self.label)
def setUsername(self, username):
# do whatever you want with the username
self.username = username
self.label.setText("Username entered: %s" % self.username)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
login = LoginDialog()
if not login.exec_(): # 'reject': user pressed 'Cancel', so quit
sys.exit(-1)
# 'accept': continue
main = MainWindow()
main.setUsername(login.username.text()) # get the username, and supply it to main window
main.show()
sys.exit(app.exec_())

Although this is not directly relevant to your question, you should always set QLineEdit.EchoMode for the password field as follows (see here):
self.password.setEchoMode(QtGui.QLineEdit.Password)

This is PyQt5 updated version of Avaris. Some exception handling was added to show how to catch a few errors (while coding your thing. Enjoy!
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Ref to this OP question. https://stackoverflow.com/questions/9689053/how-to-communicate-or-switch-between-two-windows-in-pyqt4
import sys
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtWidgets import QApplication, QDialog, QDialogButtonBox, QFormLayout, QLabel, QLineEdit, QWidget, QVBoxLayout
class LoginDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(LoginDialog, self).__init__(parent)
self.username = QLineEdit()
self.password = QLineEdit()
loginLayout = QFormLayout()
loginLayout.addRow("Username", self.username)
loginLayout.addRow("Password", self.password)
self.buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttons.accepted.connect(self.check)
self.buttons.rejected.connect(self.reject)
layout = QVBoxLayout()
layout.addLayout(loginLayout)
layout.addWidget(self.buttons)
self.setLayout(layout)
def check(self):
if str(self.password.text()) == "12345": # do actual login check
self.accept()
else:
pass # or inform the user about bad username/password
def my_exception_hook(exctype, value, traceback):
# Print the error and traceback
print(exctype, value, traceback)
# Call the normal Exception hook after
sys._excepthook(exctype, value, traceback)
sys.exit(1)
# Back up the reference to the exceptionhook
sys._excepthook = sys.excepthook
# Set the exception hook to our wrapping function
sys.excepthook = my_exception_hook
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.label = QLabel()
self.setCentralWidget(self.label)
def setUsername(self, username):
# do whatever you want with the username
self.username = username
self.label.setText("Username entered: %s" % self.username)
if __name__ == "__main__":
app = QApplication(sys.argv)
login = LoginDialog()
if not login.exec_(): # 'reject': user pressed 'Cancel', so quit
sys.exit(-1) # instead of -1 another action can be triggered here.
# 'accept': continue
main = MainWindow()
# get the username, and supply it to main window
main.setUsername(login.username.text())
main.show()
sys.exit(app.exec_())

to match username and password.
import sys
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtWidgets import QApplication, QDialog, QDialogButtonBox, QFormLayout, QLabel, QLineEdit, QWidget, QVBoxLayout, QMessageBox
class LoginDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(LoginDialog, self).__init__(parent)
self.username = QLineEdit()
self.password = QLineEdit()
loginLayout = QFormLayout()
loginLayout.addRow("Username", self.username)
loginLayout.addRow("Password", self.password)
self.buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttons.accepted.connect(self.check)
self.buttons.rejected.connect(self.reject)
layout = QVBoxLayout()
layout.addLayout(loginLayout)
layout.addWidget(self.buttons)
self.setLayout(layout)
def check(self):
if str(self.username.text()) == "foo" and str(self.password.text()) == "bar": # do actual login check
self.accept()
else:
QMessageBox.warning(
self, 'Error', 'Bad user or password')
pass # or inform the user about bad username/password
def my_exception_hook(exctype, value, traceback):
# Print the error and traceback
print(exctype, value, traceback)
# Call the normal Exception hook after
sys._excepthook(exctype, value, traceback)
sys.exit(1)
# Back up the reference to the exceptionhook
sys._excepthook = sys.excepthook
# Set the exception hook to our wrapping function
sys.excepthook = my_exception_hook
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.label = QLabel()
self.setCentralWidget(self.label)
def setUsername(self, username):
# do whatever you want with the username
self.username = username
self.label.setText("%s%s%s" % ("Username entered: ", self.username, "\npassword ok!"))
if __name__ == "__main__":
app = QApplication(sys.argv)
login = LoginDialog()
if not login.exec_(): # 'reject': user pressed 'Cancel', so quit
sys.exit(-1) # instead of -1 another action can be triggered here.
# 'accept': continue
main = MainWindow()
# get the username, and supply it to main window
main.setUsername(login.username.text())
main.show()
sys.exit(app.exec_())

Related

Return clicked button id instead of (accept, reject flags) on QDialog- PyQt5

Summary:
I've been using QMessageBox in my application (for this purpose asking for saving project before closing, Error messages), And now I want to have a custom style (Custom title bar, custom buttons and so on), I found it is hard to do that with QMessageBox, And since I've been using a QDialog in my application as well (For this purpose: Taking input from user), I decided to use a custom QDialog (Make my own style on it) instead of QMessageBox for these all previous purpose, since it is easier to customize and creating my style.
The problem:
The problem that I have now is: QDialog return only a flag value (0,1) depending on the button role And that's fine if I have only 2 buttons BUT here I have 3 (Save, Cancel, Close), Save will return 1 and both Close and Cancel return 0. And QMessageBox as I've seen it return the id of the Button that was clicked.
An example (commented well hopefully :D):
from PyQt5.QtWidgets import *
class CustomDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Data will be lost")
label = QLabel("Are you sure you want close the app before saving?")
buttonBox = QDialogButtonBox()
buttonBox.addButton(QDialogButtonBox.Save)
buttonBox.addButton(QDialogButtonBox.Cancel)
buttonBox.addButton(QDialogButtonBox.Close)
layout = QVBoxLayout()
layout.addWidget(label)
layout.addWidget(buttonBox)
self.resize(300, 100)
self.setLayout(layout)
# These two lines, return 0 or 1.
buttonBox.rejected.connect(self.reject)
buttonBox.accepted.connect(self.accept)
class Window(QWidget):
def __init__(self):
super().__init__()
label = QLabel('Hello Dialog', self)
open_dialog_button = QPushButton('Open Dialog', self)
open_dialog_button.clicked.connect(self.showDialog)
open_message_box_button = QPushButton('Open Message Box', self)
open_message_box_button.clicked.connect(self.show_message_box)
layout = QVBoxLayout()
layout.addWidget(label)
layout.addWidget(open_dialog_button)
layout.addWidget(open_message_box_button)
self.setLayout(layout)
def showDialog(self):
self.dialog = CustomDialog(self)
btn_clicked = self.dialog.exec_() # dialog exec returns 0 or 1 (Save = 1, Close and Cancel returns 0)
# I want something like this.
if btn_clicked == QDialogButtonBox.Save:
print("Close.. After Saving...")
elif btn_clicked == QDialogButtonBox.Close:
print("Close.. Without saving")
else:
print("Cancel.. Don't exit the program")
def show_message_box(self):
msg = QMessageBox()
msg.setIcon(QMessageBox.Warning)
msg.setText("Are you sure you want close the app before saving?")
msg.setStandardButtons(QMessageBox.Close | QMessageBox.Save | QMessageBox.Cancel)
msg.setWindowTitle("Data will be lost")
btn_clicked = msg.exec_()
# Here i can do this.
if btn_clicked == QMessageBox.Save:
print("Close.. After Saving...")
elif btn_clicked == QMessageBox.Close:
print("Close.. Without saving")
else:
print("Cancel.. Don't exit the program")
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
win = Window()
win.resize(200, 200)
win.show()
sys.exit(app.exec_())
I've found the solution and it worked with me by using a custom signal and slot, but still not sure if it is good or not. waiting for approval and then I can mark this solution as an accepted one.
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class CustomDialog(QDialog):
# Create a signal
signal = pyqtSignal(int)
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Data will be lost")
label = QLabel("Are you sure you want close the app before saving?")
self.buttonBox = QDialogButtonBox()
self.buttonBox.addButton(QDialogButtonBox.Save)
self.buttonBox.addButton(QDialogButtonBox.Cancel)
self.buttonBox.addButton(QDialogButtonBox.Close)
layout = QVBoxLayout()
layout.addWidget(label)
layout.addWidget(self.buttonBox)
self.resize(300, 100)
self.setLayout(layout)
# connect each button with custom slot
self.buttonBox.button(QDialogButtonBox.Save).clicked.connect(lambda: self.customSlot(QDialogButtonBox.Save))
self.buttonBox.button(QDialogButtonBox.Close).clicked.connect(lambda: self.customSlot(QDialogButtonBox.Close))
self.buttonBox.button(QDialogButtonBox.Cancel).clicked.connect(lambda: self.customSlot(QDialogButtonBox.Cancel))
# connect signal with buil-in function from QDialog (done())
# This done function will return <int> value and close the dialog.
self.signal.connect(self.done)
def customSlot(self, button_id):
# emit button's id
self.signal.emit(button_id)
class Window(QWidget):
def __init__(self):
super().__init__()
label = QLabel('Hello Dialog', self)
open_dialog_button = QPushButton('Open Dialog', self)
open_dialog_button.clicked.connect(self.showDialog)
layout = QVBoxLayout()
layout.addWidget(label)
layout.addWidget(open_dialog_button)
self.setLayout(layout)
def showDialog(self):
dialog = CustomDialog()
btn_clicked = dialog.exec_() # dialog exec returns button's id
# Now you can use the following lines of code
if btn_clicked == QDialogButtonBox.Save:
print("Close.. After Saving...")
elif btn_clicked == QDialogButtonBox.Close:
print("Close.. Without saving")
else:
print("Cancel.. Don't exit the program")
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
win = Window()
win.resize(200, 200)
win.show()
sys.exit(app.exec_())

how to save data being edited when QtableView cell loses focus on close of form

In the default behavior of editing a cell in a QtableView, when the user clicks away either to another widget or closes the form, the edits are lost. After a lot of googling, I have found a way to save the edits if the user selects another widget in the form, but if the form is closed, the edits are still lost. The blog post is here.
I attempted to call the closeEditor method from the forms closeEvent, but it requires two parameters: the editor and hint. I can provide QAbstractItemDelegate.NoHint but the editor is expecting the QlineEdit object where the editing is taking place. I am lost on how to provide this for the cell currently being edited.
Here is a gif of the current behaviour:
My question is how do I provide the QlineEdit of the cell being edited?
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtSql import *
from PyQt5.QtWidgets import *
from phones import *
class Main(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.resize(490, 998)
self.layoutWidget = QWidget(self)
self.layoutWidget.setObjectName("layoutWidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.layoutWidget)
self.horizontalLayout_7 = QtWidgets.QHBoxLayout()
self.new_phone = QtWidgets.QPushButton(self.layoutWidget)
self.new_phone.setObjectName("new_phone")
self.new_phone.setText("New Phone")
self.horizontalLayout_7.addWidget(self.new_phone)
self.delete_phone = QtWidgets.QPushButton(self.layoutWidget)
self.delete_phone.setObjectName("delete_phone")
self.delete_phone.setText("Delete phone")
self.horizontalLayout_7.addWidget(self.delete_phone)
self.verticalLayout.addLayout(self.horizontalLayout_7)
self.phone_view = Syn_tableview()
self.verticalLayout.addWidget(self.phone_view)
self.cont_id = '9'
self.setCentralWidget(self.layoutWidget)
self.new_phone.clicked.connect(self.add_phone)
self.populate_phones()
def populate_phones(self):
self.phone_model = QSqlTableModel(self)
self.phone_model.setTable("contact_phones")
self.phone_model.setFilter("contact_id='{0}'".format(self.cont_id))
self.phone_model.select()
self.phone_view.setModel(self.phone_model)
self.phone_view.resizeColumnsToContents()
def add_phone(self):
self.phone_model.submitAll()
self.phone_model.setEditStrategy(QSqlTableModel.OnManualSubmit)
row = self.phone_model.rowCount()
record = self.phone_model.record()
record.setGenerated('id', False) #primary key
record.setValue('contact_id', self.cont_id) #foreign key
self.phone_model.insertRecord(row, record)
phone_index_edit = self.phone_model.index(row, self.phone_model.fieldIndex('phone_number'))
self.phone_view.edit(phone_index_edit)
def closeEvent(self, event):
submit = self.phone_model.submitAll()
#This is the problem
self.phone_view.closeEditor("QLineEdit", QAbstractItemDelegate.NoHint)
class Syn_tableview(QTableView):
def __init__(self, *args, **kwargs):
QTableView.__init__(self, *args, **kwargs)
def closeEditor(self, editor, hint):
if hint == QAbstractItemDelegate.NoHint:
QTableView.closeEditor(self, editor,
QAbstractItemDelegate.SubmitModelCache)
if __name__=="__main__":
app=QApplication(sys.argv)
db = QSqlDatabase.addDatabase("QPSQL");
db.setHostName(server)
db.setDatabaseName(database)
db.setUserName(user)
db.setPassword(pword)
myapp = Main()
myapp.show()
sys.exit(app.exec_())
The delegate editors are children of the QTableView so you can use findChildren to get them, to make sure they are not other children you can set an objectName that allows you to filter them:
import sys
from PyQt5 import QtCore, QtSql, QtWidgets
def create_connection():
db = QtSql.QSqlDatabase.addDatabase("QPSQL")
# FIXME
db.setHostName("server")
db.setDatabaseName("database")
db.setUserName("user")
db.setPassword("pword")
if not db.open():
print(db.lastError().text())
return False
return True
class Syn_Delegate(QtWidgets.QStyledItemDelegate):
def createEditor(self, parent, option, index):
editor = super(Syn_Delegate, self).createEditor(parent, option, index)
if isinstance(editor, QtWidgets.QWidget):
editor.setObjectName("syn_editor")
return editor
class Syn_Tableview(QtWidgets.QTableView):
def closeEditor(self, editor, hint):
if hint == QtWidgets.QAbstractItemDelegate.NoHint:
hint = QtWidgets.QAbstractItemDelegate.SubmitModelCache
super(Syn_Tableview, self).closeEditor(editor, hint)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.new_phone = QtWidgets.QPushButton(self.tr("New Phone"))
self.delete_phone = QtWidgets.QPushButton(self.tr("Delete phone"))
self.phone_view = Syn_Tableview()
self.phone_model = QtSql.QSqlTableModel()
self.phone_model.setEditStrategy(QtSql.QSqlTableModel.OnManualSubmit)
self.phone_view.setModel(self.phone_model)
self.phone_view.resizeColumnsToContents()
delegate = Syn_Delegate(self)
self.phone_view.setItemDelegate(delegate)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QGridLayout(central_widget)
lay.addWidget(self.new_phone, 0, 0)
lay.addWidget(self.delete_phone, 0, 1)
lay.addWidget(self.phone_view, 1, 0, 1, 2)
self._contact_id = "9"
self.populate_phones()
self.new_phone.clicked.connect(self.add_phone)
#property
def contact_id(self):
return self._contact_id
def populate_phones(self):
self.phone_model.setTable("contact_phones")
self.phone_model.setFilter("contact_id='{0}'".format(self.contact_id))
self.phone_model.select()
#QtCore.pyqtSlot()
def add_phone(self):
self.phone_model.submitAll()
row = self.phone_model.rowCount()
record = self.phone_model.record()
record.setGenerated("id", False) # primary key
record.setValue("contact_id", self.contact_id) # foreign key
self.phone_model.insertRecord(row, record)
phone_index_edit = self.phone_model.index(
row, self.phone_model.fieldIndex("phone_number")
)
if phone_index_edit.isValid():
self.phone_view.edit(phone_index_edit)
def closeEvent(self, event):
for editor in self.phone_view.findChildren(QtWidgets.QWidget, "syn_editor"):
self.phone_view.commitData(editor)
submit = self.phone_model.submitAll()
super().closeEvent(event)
def main():
app = QtWidgets.QApplication(sys.argv)
if not create_connection():
sys.exit(-1)
w = MainWindow()
w.show()
w.resize(640, 480)
ret = sys.exit(app.exec_())
sys.exit(ret)
if __name__ == "__main__":
main()

QFileDialog.getOpenFileName change button text from 'Open' to 'Remove'

I am using QFileDialog.getOpenFileName(self,'Remove File', "path", '*.pdf') to select a file and get the path in order to remove it from my application. The issue is the QFileDialog.getOpenFileName window button says 'Open' when selecting a file which will be confusing to the user.
Is there any way to change the button text from 'Open' to 'Remove'/'Delete'
When using the static method QFileDialog::getOpenFileName() the first thing is to obtain the QFileDialog object and for that we use a QTimer and the findChild() method:
# ...
QtCore.QTimer.singleShot(0, self.on_timeout)
filename, _ = QtWidgets.QFileDialog.getOpenFileName(...,
options=QtWidgets.QFileDialog.DontUseNativeDialog)
def on_timeout(self):
dialog = self.findChild(QtWidgets.QFileDialog)
# ...
Then you can get the text iterating over the buttons until you get the button with the searched text and change it:
for btn in dialog.findChildren(QtWidgets.QPushButton):
if btn.text() == "&Open":
btn.setText("Remove")
That will work at the beginning but every time you interact with the QTreeView they show, update the text to the default value, so the same logic will have to be applied using the currentChanged signal from the selectionModel() of the QTreeView but for synchronization reasons it is necessary Update the text later using another QTimer.singleShot(), the following code is a workable example:
import sys
from PyQt5 import QtCore, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
button = QtWidgets.QPushButton("Press me")
button.clicked.connect(self.on_clicked)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(button)
#QtCore.pyqtSlot()
def on_clicked(self):
QtCore.QTimer.singleShot(0, self.on_timeout)
filename, _ = QtWidgets.QFileDialog.getOpenFileName(
self,
"Remove File",
"path",
"*.pdf",
options=QtWidgets.QFileDialog.DontUseNativeDialog,
)
def on_timeout(self):
dialog = self.findChild(QtWidgets.QFileDialog)
dialog.findChild(QtWidgets.QTreeView).selectionModel().currentChanged.connect(
lambda: self.change_button_name(dialog)
)
self.change_button_name(dialog)
def change_button_name(self, dialog):
for btn in dialog.findChildren(QtWidgets.QPushButton):
if btn.text() == self.tr("&Open"):
QtCore.QTimer.singleShot(0, lambda btn=btn: btn.setText("Remove"))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
The first step can be avoided if the static method is not used and create the dialog using an instance of QFileDialog:
import sys
from PyQt5 import QtCore, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
button = QtWidgets.QPushButton("Press me")
button.clicked.connect(self.on_clicked)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(button)
#QtCore.pyqtSlot()
def on_clicked(self):
dialog = QtWidgets.QFileDialog(
self,
"Remove File",
"path",
"*.pdf",
supportedSchemes=["file"],
options=QtWidgets.QFileDialog.DontUseNativeDialog,
)
self.change_button_name(dialog)
dialog.findChild(QtWidgets.QTreeView).selectionModel().currentChanged.connect(
lambda: self.change_button_name(dialog)
)
if dialog.exec_() == QtWidgets.QDialog.Accepted:
filename = dialog.selectedUrls()[0]
print(filename)
def change_button_name(self, dialog):
for btn in dialog.findChildren(QtWidgets.QPushButton):
if btn.text() == self.tr("&Open"):
QtCore.QTimer.singleShot(0, lambda btn=btn: btn.setText("Remove"))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
While I appreciate the solution provided by #eyllanesc, I'd like to propose a variation.
Under certain circumstances, the code for that answer might fail, specifically:
the delay that X11 suffers from mapping windows;
the checking of localized button strings;
the selection using the file name edit box;
Considering the above, I propose an alternate solution, based on the points above.
For obvious reasons, the main point remains: the dialog must be non-native.
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class FileDialogTest(QWidget):
def __init__(self):
super().__init__()
layout = QHBoxLayout(self)
self.fileEdit = QLineEdit()
layout.addWidget(self.fileEdit)
self.selectBtn = QToolButton(icon=QIcon.fromTheme('folder'), text='…')
layout.addWidget(self.selectBtn)
self.selectBtn.clicked.connect(self.showSelectDialog)
def checkSelectDialog(self):
dialog = self.findChild(QFileDialog)
if not dialog.isVisible():
# wait for dialog finalization, as testOption might fail
return
# dialog found, stop the timer and delete it
self.sender().stop()
self.sender().deleteLater()
if not dialog.testOption(dialog.DontUseNativeDialog):
# native dialog, we cannot do anything!
return
def updateOpenButton():
selection = tree.selectionModel().selectedIndexes()
if selection and not tree.model().isDir(selection[0]):
# it's a file, change the button text
button.setText('Select my precious file')
tree = dialog.findChild(QTreeView)
button = dialog.findChild(QDialogButtonBox).button(
QDialogButtonBox.Open)
# check for selected files on open
updateOpenButton()
# connect the selection update signal
tree.selectionModel().selectionChanged.connect(
lambda: QTimer.singleShot(0, updateOpenButton))
def showSelectDialog(self):
QTimer(self, interval=10, timeout=self.checkSelectDialog).start()
path, filter = QFileDialog.getOpenFileName(self,
'Select file', '<path_to_file>',
"All Files (*);;Python Files (*.py);; PNG Files (*.png)",
options=QFileDialog.DontUseNativeDialog)
if path:
self.fileEdit.setText(path)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
ex = FileDialogTest()
ex.show()
sys.exit(app.exec())
Obviously, for PyQt6 you'll need to use the proper Enum namespaces (i.e. QFileDialog.Option.DontUseNativeDialog, etc.).

How do I get a signal from a QWiget to change get object?

Hello I have a QWidget and If I click on it, I want to get the object (the QWidget Element I clicked on) is there anyway to do that?
I already found some code but I only get the MouseClickEvent from
self.widget_34.mouseReleaseEvent = lambda event: self.myfunction(event)
Although the solution offered by #Cin is interesting, it has a serious problem: it cancels the mousePressEvent of the widget, so the widget loses the behavior it could have when the widget is pressed, for example the button no longer emits the clicked signal, other widget also They will have the same problem.
A less intrusive solution is to use eventFilter:
import sys
import weakref
from PyQt5 import QtCore, QtWidgets
class ClickListener(QtCore.QObject):
clicked = QtCore.pyqtSignal(QtWidgets.QWidget)
def addWidget(self, widget, other_widget=None):
if not hasattr(self, "_widgets"):
self._widgets = {}
widget.installEventFilter(self)
self._widgets[widget] = widget if other_widget is None else other_widget
weakref.ref(widget, self.removeWidget)
def eventFilter(self, obj, event):
if (
obj in self._widgets
and event.type() == QtCore.QEvent.MouseButtonPress
):
self.clicked.emit(self._widgets[obj])
return super(ClickListener, self).eventFilter(obj, event)
def removeWidget(self, widget):
if hasattr(self, "_widgets"):
if widget in self._widgets:
del self._widgets[widget]
class App(QtWidgets.QWidget):
def __init__(self):
super().__init__()
button = QtWidgets.QPushButton("Press Me")
label = QtWidgets.QLabel("Stack Overflow")
spinBox = QtWidgets.QSpinBox()
te = QtWidgets.QTextEdit()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(button)
lay.addWidget(label)
lay.addWidget(spinBox)
lay.addWidget(te)
listener = ClickListener(self)
listener.clicked.connect(self.onClicked)
listener.addWidget(button)
listener.addWidget(label)
listener.addWidget(spinBox.lineEdit(), spinBox)
listener.addWidget(te.viewport(), te)
def onClicked(self, obj):
print("Clicked, from", obj)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())
I am not sure this will be a proper solution or not but I think, you can use the partial method of functools module. A collable object can be treated as a function for the purposes of this module. Here you can see my example,
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLabel
import functools
class App(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(200,200,200,200)
self.button = QPushButton('Button', self)
self.button.move(50,50)
self.label = QLabel(self)
self.label.setText("Label")
self.label.move(100,100)
self.items = [self.button, self.label]
for i in self.items:
i.mousePressEvent = functools.partial(self.getClickedItem, source_object=i)
self.show()
def getClickedItem(self, event, source_object=None):
print("Clicked, from", source_object)
#item text
#print(source_object.text())
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())

Text from QLineEdit not displaying

I am grabbing the user input from the line edit and displaying it on the QMessageBox but it won't show for some reason. I thought maybe I wasn't grabbing the input from QLineEdit at all but when I tried printing it on the terminal (it still wouldn't show there btw) the terminal scrolled down, recognizing that there is new data in it but just not displaying it. Get what I am saying?
import os
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
def main():
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
class MyWindow(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
# create objects
label = QLabel(self.tr("enter the data "))
self.le = QLineEdit()
self.te = QTextEdit()
# layout
layout = QVBoxLayout(self)
layout.addWidget(label)
layout.addWidget(self.le)
layout.addWidget(self.te)
self.setLayout(layout)
# create connection
self.mytext = str(self.le.text())
self.connect(self.le, SIGNAL("returnPressed(void)"),
self.display)
def display(self):
QApplication.instance().processEvents()
msg = QMessageBox.about(self, 'msg', '%s' % self.mytext)
print(self.mytext)
self.te.append(self.mytext)
self.le.setText("")
if __name__ == "__main__":
main()
You are currently reading the QLineEdit in the constructor, and at that moment the QLineEdit is empty, you must do it in the slot:
def display(self):
mytext = self.le.text()
msg = QMessageBox.about(self, 'msg', '%s' % mytext)
self.te.append(mytext)
self.le.clear()
Note: use clear() to clean the QLineEdit

Categories

Resources