I have a very simple program that opens a DB and loads a tableview.
So:
Layout file called TestLayouts.py
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'basic.ui'
##
## Created by: Qt User Interface Compiler version 5.14.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide2.QtCore import (QCoreApplication, QMetaObject,
QRect)
from PySide2.QtWidgets import *
class Ui_MainWindow(object) :
def setupUi(self, MainWindow) :
if not MainWindow.objectName() :
MainWindow.setObjectName(u"MainWindow")
MainWindow.resize(1034, 803)
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName(u"centralwidget")
self.pushButton = QPushButton(self.centralwidget)
self.pushButton.setObjectName(u"pushButton")
self.pushButton.setGeometry(QRect(920, 730, 89, 25))
self.tableView = QTableView(self.centralwidget)
self.tableView.setObjectName(u"tableView")
self.tableView.setGeometry(QRect(10, 20, 1001, 711))
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QStatusBar(MainWindow)
self.statusbar.setObjectName(u"statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QMetaObject.connectSlotsByName(MainWindow)
# setupUi
def retranslateUi(self, MainWindow) :
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None))
self.pushButton.setText(QCoreApplication.translate("MainWindow", u"PushButton", None))
# retranslateUi
My main file called tests.py
import sys
import webbrowser
from PySide2 import QtWidgets, QtGui
from PySide2.QtCore import QCoreApplication, QSortFilterProxyModel
from PySide2.QtCore import Slot
from PySide2.QtSql import QSqlDatabase, QSqlQueryModel
from PySide2.QtWidgets import QMenu, QAction
from TestLayouts import Ui_MainWindow
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow) :
def __init__(self, parent=None) :
super(MainWindow, self).__init__()
self.setupUi(self)
self.showMaximized()
self.pushButton.clicked.connect(self._basic)
def _basic(self) :
db = QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName("data.sqlite")
db.open()
sourceModel = QSqlQueryModel()
sourceModel.setQuery(
"SELECT id,url FROM database",
db)
proxyModel = QSortFilterProxyModel(self)
proxyModel.setSourceModel(sourceModel)
self.tableView.setModel(proxyModel)
self.tableView.setSortingEnabled(True)
#Slot()
def closeEvent(self, event) :
super(MainWindow, self).closeEvent(event)
QCoreApplication.instance().quit()
def contextMenuEvent(self, event) :
self.menu = QMenu(self)
openlinkAction = QAction('Open Link In A Browser', self)
openlinkAction.triggered.connect(lambda : self.openInANewTab(event))
self.menu.addAction(openlinkAction)
self.menu.popup(QtGui.QCursor.pos())
def openInANewTab(self, event) :
self.row = self.tableView.rowAt(event.pos().y())
self.col = self.tableView.columnAt(event.pos().x())
self.cell = self.tableView.item(self.row, self.col)
cellText = self.cell.text()
webbrowser.open(cellText)
if __name__ == "__main__" :
app = QtWidgets.QApplication([])
w = MainWindow()
w.show()
sys.exit(app.exec_())
Once you run the program and click load, the db loads into view. The right click menu is displayed as expected but on clicking I get the error.
RuntimeError: Internal C++ object (PySide2.QtGui.QContextMenuEvent) already deleted.
I have looked at the few threads like this one here which says
If a QObject falls out of scope in Python, it will get deleted. You have to take care of keeping a reference to the object:
Store it as an attribute of an object you keep around, e.g. self.window = QMainWindow()
Pass a parent QObject to the object’s constructor, so it gets owned by the parent
I am not sure how this can be achieved. Any help is appreciated.
Explanation:
In the contextMenuEvent method you are creating a popup and you are showing it but that consumes very little time so when the user selects an option that method is already finished and therefore Qt removes the "event" object as it is no longer needs to. But you are trying to access an item removed by Qt causing those kinds of errors.
Solution:
There are several solutions:
Use exec_() instead of popup() so that the contextMenuEvent method does not finish executing and thus the "event" object is not removed, in addition the QTableView does not have an item() method so it will throw another exception, instead therefore use the index method:
def contextMenuEvent(self, event):
self.menu = QMenu(self)
openlinkAction = QAction("Open Link In A Browser", self)
openlinkAction.triggered.connect(lambda: self.openInANewTab(event))
self.menu.addAction(openlinkAction)
self.menu.exec_(QtGui.QCursor.pos())
def openInANewTab(self, event):
gp = self.mapToGlobal(event.pos())
vp = self.tableView.viewport().mapFromGlobal(gp)
index = self.tableView.indexAt(vp)
if index.isValid():
cellText = index.data()
if isinstance(cellText, str):
webbrowser.open(cellText)
Get the information from the text and pass it to the lambda before displaying the popup so it is no longer necessary to use the event:
def contextMenuEvent(self, event):
gp = self.mapToGlobal(event.pos())
vp = self.tableView.viewport().mapFromGlobal(gp)
index = self.tableView.indexAt(vp)
if not index.isValid():
return
self.menu = QMenu(self)
cellText = index.data()
openlinkAction = QAction("Open Link In A Browser", self)
openlinkAction.triggered.connect(
lambda *args, text=cellText: self.openInANewTab(text)
)
self.menu.addAction(openlinkAction)
self.menu.popup(QtGui.QCursor.pos())
def openInANewTab(self, text):
if isinstance(text, str):
webbrowser.open(text)
Related
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()
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.).
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_())
I am using the given code, I want the user to enter text in the QLineEdit widget, press the Copy! button and see the inputted text replace the 'N/A' label. My questions is: following this procedure, how can I clear the text inputted in the QLineEdit widget with a simple mouse click?
From what I read (this, this and this) it seems like I need to reimplement focusInEvent() in a new class extending QLineEdit. My problem is that the code for my GUI has been imported from Qt Designer using pyuic5 and the examples cited above don't seem to take this in consideration.
Here is my code:
from PyQt5.QtWidgets import *
import sys
import QLineEdit_test
class MainWindow(QMainWindow, QLineEdit_test.Ui_QLineEdit_test):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.copy_button.clicked.connect(self.copy_and_print)
def copy_and_print(self):
self.label.setText(self.lineEdit.text())
def main():
app = QApplication(sys.argv)
form = MainWindow()
form.show()
app.exec_()
if __name__ == "__main__":
main()
Here is my converted .ui file:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_QLineEdit_test(object):
def setupUi(self, QLineEdit_test):
QLineEdit_test.setObjectName("QLineEdit_test")
QLineEdit_test.resize(300, 200)
QLineEdit_test.setMaximumSize(QtCore.QSize(300, 200))
self.centralwidget = QtWidgets.QWidget(QLineEdit_test)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout_2 = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout_2.setObjectName("gridLayout_2")
self.gridLayout = QtWidgets.QGridLayout()
self.gridLayout.setObjectName("gridLayout")
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setMaximumSize(QtCore.QSize(120, 16777215))
self.lineEdit.setObjectName("lineEdit")
self.gridLayout.addWidget(self.lineEdit, 0, 0, 1, 1)
self.copy_button = QtWidgets.QPushButton(self.centralwidget)
self.copy_button.setObjectName("copy_button")
self.gridLayout.addWidget(self.copy_button, 1, 0, 1, 1)
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setMaximumSize(QtCore.QSize(200, 20))
self.label.setAlignment(QtCore.Qt.AlignCenter)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 2, 0, 1, 1)
self.gridLayout_2.addLayout(self.gridLayout, 0, 0, 1, 1)
QLineEdit_test.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(QLineEdit_test)
self.menubar.setGeometry(QtCore.QRect(0, 0, 300, 22))
self.menubar.setObjectName("menubar")
QLineEdit_test.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(QLineEdit_test)
self.statusbar.setObjectName("statusbar")
QLineEdit_test.setStatusBar(self.statusbar)
self.retranslateUi(QLineEdit_test)
QtCore.QMetaObject.connectSlotsByName(QLineEdit_test)
def retranslateUi(self, QLineEdit_test):
_translate = QtCore.QCoreApplication.translate
QLineEdit_test.setWindowTitle(_translate("QLineEdit_test", "MainWindow"))
self.copy_button.setText(_translate("QLineEdit_test", "Copy!"))
self.copy_button.setShortcut(_translate("QLineEdit_test", "Return"))
self.label.setText(_translate("QLineEdit_test", "N/A"))
The solution is to promote QtDesigner use our custom QLineEdit where we implement the signal clicked with the help of mousePressEvent, this class will be called ClickableLineEdit and the file will be called ClickableLineEdit.py.
ClickableLineEdit.py
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QLineEdit
class ClickableLineEdit(QLineEdit):
clicked = pyqtSignal()
def mousePressEvent(self, event):
self.clicked.emit()
QLineEdit.mousePressEvent(self, event)
To promote it, the following structure will be considered:
.
├── ClickableLineEdit.py
├── main.py
├── your.ui
└── QLineEdit_test.py
Open the design with Qt Designer and right click on the QLineEdit and select Promote to ...:
A menu will open and place the following
then press and Promote. Then we generate the code again.
Then we connect the signal to clear:
class MainWindow(QMainWindow, QLineEdit_test.Ui_QLineEdit_test):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.copy_button.clicked.connect(self.copy_and_print)
self.lineEdit.clicked.connect(self.lineEdit.clear)
def copy_and_print(self):
self.label.setText(self.lineEdit.text())
Update:
PySide2:
from PySide2 import QtCore, QtWidgets
class ClickableLineEdit(QtWidgets.QLineEdit):
clicked = QtCore.Signal()
def mousePressEvent(self, event):
super(ClickableLineEdit, self).mousePressEvent(event)
self.clicked.emit()
class App(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.lineedit = ClickableLineEdit()
self.lineedit.clicked.connect(self.lineedit.clear)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.lineedit)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication.instance()
if app is None:
app = QtWidgets.QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
layout = QGridLayout()
self.setLayout(layout)
self.lineedit = QLineEdit()
self.lineedit.returnPressed.connect(self.press)
layout.addWidget(self.lineedit, 0, 0)
def press(self):
print("Hi World")
self.lineedit.clear()
If someone is still looking for a way to do this, but you only want the Line edit to be cleared when it first clicked and not every time it is clicked, you can do so without using variables like this :
def init_UI(self):
self.text_input = QLineEdit('Type your name')
self.text_input.mousePressEvent = self._mousePressEvent
def _mousePressEvent(self, event):
self.text_input.clear()
self.text_input.mousePressEvent = None
This way the _mousePressEvent gets called only once
I have an optional solution in one line:
self.lineEdit.mouseReleaseEvent = self.copy_and_print
Make sure you are receiving two parameters in your function (self,event)
I hope I helped you
Full post: https://wiki.python.org/moin/PyQt/Making%20non-clickable%20widgets%20clickable
Use mousePressEvent of QLineEdit to detect mouse click. To clear the text use clear() method or setText() method of QLineEdit.
#called when ever mouse is pressed
def mousePressed(self, event):
print('mouse pressed')
self.lineEdit=QLineEdit("Awesome day")#from PyQt5.QtWidget import QLineEdit
self.lineEdit.mousePressEvent = self.mousePressed
Example program :
import sys
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QApplication, QLineEdit
class ButtonPanel(QWidget):
def __init__(self, heading):
self.initUI(heading)
def initUI(self, heading):
super().__init__()
self.layout = QHBoxLayout()
self.lineEdit = QLineEdit(heading)
#self.lineEdit.setReadOnly(True)
self.lineEdit.returnPressed.connect(self.returnPressed)
self.lineEdit.mousePressEvent = self.mousePressed
self.delete = QPushButton("D")
self.layout.addWidget(self.lineEdit)
self.layout.addWidget(self.delete)
self.setLayout(self.layout)
self.show()
#called when mouse is clicked
def mousePressed(self, event):
self.lineEdit.clear() #text is cleared
//self.lineEdit.setText("") #this way we can also clear the text
print('mouse pressed')
//called when return key is pressed
def returnPressed(self):
print('return pressed')
if __name__ == "__main__":
app = QApplication(sys.argv)
b = ButtonPanel("Awesome")
sys.exit(app.exec())
Output :
I have a comboBox and the contents need to change dynamically. I also need to know when the user clicks on the comboBox. When the comboBox has content, it will fire signals, but when it's empty I don't see any signals fired at all. The following code is a toy example demonstrating that for an empty comboBox, no signal will fire.
from PyQt4 import QtCore, QtGui
import sys
class Ui_Example(QtGui.QDialog):
def setupUi(self, Dialog):
self.dialog = Dialog
Dialog.setObjectName("Dialog")
Dialog.resize(300,143)
self.comboBox = QtGui.QComboBox(Dialog)
self.comboBox.setGeometry(QtCore.QRect(60,20,230,20))
self.comboBox.setObjectName("comboBox")
class Ui_Example_Logic(QtGui.QMainWindow):
def __init__(self):
super(Ui_Example_Logic, self).__init__()
def create_main_window(self):
self.ui = Ui_Example()
self.ui.setupUi(self)
self.ui.comboBox.highlighted.connect(self.my_highlight)
self.ui.comboBox.activated.connect(self.my_activate)
def my_highlight(self):
print "Highlighted"
def my_activate(self):
print "Activated"
if __name__ == '__main__':
APP = QtGui.QApplication([])
WINDOW = Ui_Example_Logic()
WINDOW.create_main_window()
WINDOW.show()
sys.exit(APP.exec_())
So for example, if the line below is added to the create_main_window function, "activated" and "highlighted" will print out on expected events, but as the code is now (with the comboBox empty) nothing will print.
self.ui.comboBox.addItems(['a', 'b'])
How can I detect if the user has interacted with the comboBox when it is empty?
If your combobox is empty, no signal will be emitted. But you can installEventFilter for your combobox and reimplement eventfilter (link). First, create filter:
class MouseDetector(QtCore.QObject):
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.MouseButtonPress and obj.count() == 0:
print 'Clicked'
return super(MouseDetector, self).eventFilter(obj, event)
It will be print Clicked when user press mouse button on empty comboBox, created inside Ui_Example. Then, install event:
class Ui_Example(QtGui.QDialog):
def setupUi(self, Dialog):
self.dialog = Dialog
Dialog.setObjectName("Dialog")
Dialog.resize(300,143)
self.comboBox = QtGui.QComboBox(Dialog)
self.comboBox.setGeometry(QtCore.QRect(60,20,230,20))
self.comboBox.setObjectName("comboBox")
self.mouseFilter = MouseDetector()
self.comboBox.installEventFilter(self.mouseFilter)