Reputation: 13
I have embedded a QPushbutton in a Qwidget in a QListView: QPushbutton >> QWidget >> QListView
list_widget = QWidget()
list_widget.layout(QHBoxLayout())
btn = QPushButton()
btn.pressed.connect(clicked)
list_widget.layout().addWidget(QPushButton())
list_view.setIndexWidget(self.list_model.index(row, 0), list_widget)
def clicked():
row = list_view.selectedIndexes()
The problem is now list_view.selectedIndexes() does not return the row of the pressed button, when pressed. This seems to work only when the QPushbutton is embedded in the QListView directly: QPushbutton >> QListView.
Does anyone have an idea how to delegate the focus of the pushbutton to the QListView?
Upvotes: 1
Views: 150
Reputation: 243907
When you click on the button it is not transmitted to the QListView because the button consumes it and does not transmit it to other widgets so if you want to obtain the row it must be obtained indirectly, a possible solution is to use the geometry for it you must obtain the sender , in this case the button, and then for its topleft to global positions, then convert it to a local position with respect to the viewport of QListView, using that position with the method indexAt()
you get the QModelIndex.
from PyQt5 import QtCore, QtGui, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.list_model = QtGui.QStandardItemModel(200, 1)
self.list_view = QtWidgets.QListView()
self.list_view.setModel(self.list_model)
self.setCentralWidget(self.list_view)
for row in range(self.list_model.rowCount()):
list_widget = QtWidgets.QWidget()
hlay = QtWidgets.QHBoxLayout(list_widget)
btn = QtWidgets.QPushButton(str(row))
btn.pressed.connect(self.clicked)
hlay.addWidget(btn)
hlay.setContentsMargins(0, 0, 0, 0)
self.list_view.setIndexWidget(self.list_model.index(row, 0), list_widget)
@QtCore.pyqtSlot()
def clicked(self):
btn = self.sender()
gp = btn.mapToGlobal(QtCore.QPoint())
lp = self.list_view.viewport().mapFromGlobal(gp)
ix = self.list_view.indexAt(lp)
print("row", ix.row())
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Another much simpler way is to pass the row as an argument using functools.partial()
:
from functools import partial
from PyQt5 import QtCore, QtGui, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.list_model = QtGui.QStandardItemModel(200, 1)
self.list_view = QtWidgets.QListView()
self.list_view.setModel(self.list_model)
self.setCentralWidget(self.list_view)
for row in range(self.list_model.rowCount()):
list_widget = QtWidgets.QWidget()
hlay = QtWidgets.QHBoxLayout(list_widget)
btn = QtWidgets.QPushButton(str(row))
btn.pressed.connect(partial(self.clicked, row))
hlay.addWidget(btn)
hlay.setContentsMargins(0, 0, 0, 0)
self.list_view.setIndexWidget(self.list_model.index(row, 0), list_widget)
@QtCore.pyqtSlot(int)
def clicked(self, row):
print(row)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Or using a lambda method:
from PyQt5 import QtCore, QtGui, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.list_model = QtGui.QStandardItemModel(200, 1)
self.list_view = QtWidgets.QListView()
self.list_view.setModel(self.list_model)
self.setCentralWidget(self.list_view)
for row in range(self.list_model.rowCount()):
list_widget = QtWidgets.QWidget()
hlay = QtWidgets.QHBoxLayout(list_widget)
btn = QtWidgets.QPushButton(str(row))
btn.pressed.connect(lambda *args, row=row: self.clicked(row))
hlay.addWidget(btn)
hlay.setContentsMargins(0, 0, 0, 0)
self.list_view.setIndexWidget(self.list_model.index(row, 0), list_widget)
@QtCore.pyqtSlot(int)
def clicked(self, row):
print(row)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
In my case I prefer to use partials since you do not need to write a lot of logic and it's thread-safe.
Upvotes: 1