fjp | 2020-12-07 09:14:44 UTC | #1
Hi, I followed this https://www.pythonguis.com/tutorials/modelview-architecture/ tutorial, and I am trying to display an image depending on the currently selected image path from the QTableView.
I think a possible approach is to create a custom QAbstractItemView to display the currently selected image.
The ImageModel
and main window class (in image_app.py
) look like this
import sys
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtWidgets import QFileDialog
from PySide2.QtCore import Qt
from MainWindow import Ui_MainWindow
class ImageModel(QtCore.QAbstractListModel):
def __init__(self, images=None):
super().__init__()
self.images = images or []
def data(self, index, role):
if role == Qt.DisplayRole:
_, text = self.images[index.row()]
return text
def rowCount(self, index):
return len(self.images)
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.model = ImageModel()
self.tableView.setModel(self.model)
self.imageView.setModel(self.model)
self.tableView.selectionModel().selectionChanged.connect(self.imageView.selectionChanged)
self.actionImport.triggered.connect(self.onImportImageClicked)
def onImportImageClicked(self, s):
self.open_file()
def open_file(self):
filename, _ = QFileDialog.getOpenFileName(
self,
"Open file",
"",
"Ok Image (*.png *.jpg *.bmp *.jpeg);;" "All files(*.*)",
)
if filename:
self.add(filename)
def add(self, image_filename):
# Access the list via the model.
self.model.images.append((False, image_filename))
# Trigger refresh.
self.model.layoutChanged.emit()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
And my custom QAbstractItemView should display the image using a QLabel (defined in image_view.py
):
class ImageView(QtWidgets.QLabel, QtWidgets.QAbstractItemView):
def __init__(self, parent) -> None:
print(parent)
QtWidgets.QLabel.__init__(self, parent)
QtWidgets.QAbstractItemView.__init__(self, parent)
#super().__init__()
#self.label = QtWidgets.QLabel()
def selectionChanged(self, selected, deselected):
print("selectionChanged", type(selected))
indexes = selected.indexes()
print(indexes)
if not indexes:
return
image_filename = self.model().data(indexes[0], Qt.DisplayRole)
print(image_filename)
pixmap = QtGui.QPixmap(image_filename).scaled(128, 128, QtCore.Qt.KeepAspectRatio)
self.setPixmap(pixmap)
The GUI is designed with Qt Designer and looks like this
Create GUI Applications with Python & Qt5 by Martin Fitzpatrick — (PySide2 Edition) The hands-on guide to making apps with Python — Over 10,000 copies sold!
I have promoted a QWidget in the Input tab to the ImageView
class in the image_view.py
It can display images when one is selected from the QTableListView, but after I run the MainWindow I get this console output:
<PySide2.QtWidgets.QWidget(0x7f8cd002cad0, name="tabInput") at 0x7f8ceaebc980>
QObject::connect: No such slot ImageView::_q_modelDestroyed()
QObject::connect: No such slot ImageView::dataChanged(QModelIndex,QModelIndex,QVector<int>)
QObject::connect: No such slot ImageView::_q_headerDataChanged()
QObject::connect: No such slot ImageView::rowsInserted(QModelIndex,int,int)
QObject::connect: No such slot ImageView::_q_rowsInserted(QModelIndex,int,int)
QObject::connect: No such slot ImageView::rowsAboutToBeRemoved(QModelIndex,int,int)
QObject::connect: No such slot ImageView::_q_rowsRemoved(QModelIndex,int,int)
QObject::connect: No such slot ImageView::_q_rowsMoved(QModelIndex,int,int,QModelIndex,int)
QObject::connect: No such slot ImageView::_q_columnsAboutToBeRemoved(QModelIndex,int,int)
QObject::connect: No such slot ImageView::_q_columnsRemoved(QModelIndex,int,int)
QObject::connect: No such slot ImageView::_q_columnsInserted(QModelIndex,int,int)
QObject::connect: No such slot ImageView::_q_columnsMoved(QModelIndex,int,int,QModelIndex,int)
QObject::connect: No such slot ImageView::reset()
QObject::connect: No such slot ImageView::_q_layoutChanged()
QObject::connect: No such slot ImageView::selectionChanged(QItemSelection,QItemSelection)
QObject::connect: No such slot ImageView::currentChanged(QModelIndex,QModelIndex)
selectionChanged <class 'PySide2.QtCore.QItemSelection'>
[]
selectionChanged <class 'PySide2.QtCore.QItemSelection'>
[<PySide2.QtCore.QModelIndex(0,0,0x0,TodoModel(0x1b10c90)) at 0x7f8ceaec24c0>]
/home/fjp/Pictures/motor.png
It seems also that the QLabel is displayed above the pixmap and when I click it, the app crashes:
Purchasing Power Parity
Developers in [[ country ]] get [[ discount.discount_pc ]]% OFF on all books & courses with code [[ discount.coupon_code ]][1] 150858 segmentation fault (core dumped) python image_app.py
How to avoid these warnings and crashing the app? Whats the correct way to display an image with the currently selected file name in the QTableView? Should I avoid the QAbstractItemView and just use a QLabel to update its pixmap when the selection changes?
Edit
Here is the pyside2 generated gui MainWindow.py
, used in image_app.py
to better reproduce this issue:
PyQt/PySide 1:1 Coaching with Martin Fitzpatrick — Get one on one help with your Python GUI projects. Working together with you I'll identify issues and suggest fixes, from bugs and usability to architecture and maintainability.
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *
from image_view import ImageView
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
if not MainWindow.objectName():
MainWindow.setObjectName(u"MainWindow")
MainWindow.resize(800, 600)
self.actionImport = QAction(MainWindow)
self.actionImport.setObjectName(u"actionImport")
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName(u"centralwidget")
self.verticalLayout = QVBoxLayout(self.centralwidget)
self.verticalLayout.setObjectName(u"verticalLayout")
self.splitter = QSplitter(self.centralwidget)
self.splitter.setObjectName(u"splitter")
self.splitter.setOrientation(Qt.Vertical)
self.tabWidget = QTabWidget(self.splitter)
self.tabWidget.setObjectName(u"tabWidget")
self.tabInput = QWidget()
self.tabInput.setObjectName(u"tabInput")
self.verticalLayout_2 = QVBoxLayout(self.tabInput)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.imageView = ImageView(self.tabInput)
self.imageView.setObjectName(u"imageView")
self.verticalLayout_2.addWidget(self.imageView)
self.tabWidget.addTab(self.tabInput, "")
self.splitter.addWidget(self.tabWidget)
self.tableView = QTableView(self.splitter)
self.tableView.setObjectName(u"tableView")
self.splitter.addWidget(self.tableView)
self.verticalLayout.addWidget(self.splitter)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QMenuBar(MainWindow)
self.menubar.setObjectName(u"menubar")
self.menubar.setGeometry(QRect(0, 0, 800, 22))
self.menuFile = QMenu(self.menubar)
self.menuFile.setObjectName(u"menuFile")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QStatusBar(MainWindow)
self.statusbar.setObjectName(u"statusbar")
MainWindow.setStatusBar(self.statusbar)
self.menubar.addAction(self.menuFile.menuAction())
self.menuFile.addAction(self.actionImport)
self.retranslateUi(MainWindow)
self.tabWidget.setCurrentIndex(0)
QMetaObject.connectSlotsByName(MainWindow)
# setupUi
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None))
self.actionImport.setText(QCoreApplication.translate("MainWindow", u"Import", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabInput), QCoreApplication.translate("MainWindow", u"Input", None))
self.menuFile.setTitle(QCoreApplication.translate("MainWindow", u"File", None))
# retranslateUi