Solved How to connect child tableview to parent model?
I'm building a PyQt5 application that will have several QWidget windows. Some of these windows will have different tableviews connected to the same source model so the user can view the data in different ways. Here is my minimum example:
class MainWindow(QMainWindow): def __init__(self): super().__init__() self._ui = Ui_MainWindow() self._ui.setupUi(self) self._patient_search_model_source = models.PatientProfileModel() #QSqlRelationalTableModel self._patient_search_proxyModel = QSortFilterProxyModel() self._patient_search_proxyModel.setSourceModel(self._patient_search_model_source) def get_patient_search_proxyModel(self): return self._patient_search_proxyModel
And the class for the ApptView (which is a stacked widget in MainWindow)
class ApptView(QWidget): def __init__(self, parent): super().__init__(parent) self.appt_ui = Ui_appt_form() self.appt_ui.setupUi(self) self.appt_ui.patient_search_tableView.setModel(MainWindow.get_patient_search_proxyModel)
However I get a compile error for the line
"TypeError: setModel(self, QAbstractItemModel): argument 1 has unexpected type 'function'"
I've tried passing in 'parent':
But that gives me the error of "AttributeError: 'MainWindow' object has no attribute '_patient_search_proxyModel'"
I feel like I must be missing something fundamental here!
Anyone have any suggestions?
Thanks for your time, Jude
EDIT: I should note, I probably could just make a new QSqlRelationalTable model for each seperate QWidget, but I thought that would be bad form.
@judethedude You could simply pass the model as parameter to ApptView constructor...
JonB last edited by JonB
get_patient_search_proxyModelis a function, but you omit the trailing parentheses (
"AttributeError: 'MainWindow' object has no attribute '_patient_search_proxyModel'"
You must not pass
parentfrom the caller as
self. Or something like that, not quite sure, you pass a
parentparameter to a method which does accept any parameters (other than the special
In any case, you intend:
Having said that, you are no better off, it won't work. You don't have the
ApptView, so you can't (easily) access anything in the calling
MainWindowanyway. And you should not be trying to do this.
You could "cast" the
MainWndow. This should do what you were attemtping:
But it's really messy, you're taking advantage of Python's non-explicit types and you are trying to access the
MainWindowinstance through parent in
ApptViewwhich is not good code design.
If you must access proxy model in
ApptView, pass as parameter to constructor. [Oh, while I have been typing my helpful stuff I see @jsulm has just typed this curtly ;-)]
class ApptView(QWidget): def __init__(self, proxyModel, parent): super().__init__(parent) self._proxy_model = proxyModel # when you create the `ApptView` in `MainWindow` stacked widget or wherever: # (but must come after `self._patient_search_proxyModel = QSortFilterProxyModel()`) self._apptView = ApptView(self._patient_search_proxyModel, self)
@jsulm @JonB Thanks for the solution gentlemen. And thanks for the explanation Jon. If you could expand a bit further, if my goal is multiple tableviews throughout my application, I should pass them as a parameter to each constructor? Instead of creating a new model for each tableview?
EDIT: Also, if you had any recommended reading for program structure for PyQt that would be much appreciated. After your comment I feel I might not have structured my Widgets properly.
JonB last edited by JonB
Let's separate out what models you have, what views you have and how you access the model(s).
If you have only one model/data source and you want to view it from multiple places, you only one to create one model instance. Don't duplicate/create extra models if there is a single source of data.
You can have multiple views onto the same individual model instance, that is no problem. You could have multiple views within, say, the same widget, or one view in each of several widgets, no matter.
Views belong where they are shown to the user: in code for the widget they are shown in, or in their own modules. Views can know about the models they show (they have to), but models should not know about any views which might be attached to the model. If you choose to, say, put models and views in their own classes/modules, view modules should import the model class, but not vice versa. Don't let your model/data code include any view stuff or even be able to see it, this keeps the segregation clean.
Now you need to be able to access the model from each view/widget. Some sort of singleton pattern is often good. In
MainWindowyou already have
self._patient_search_model_source = models.PatientProfileModel() #QSqlRelationalTableModel
So, depending on how you defined that, is
models....available to your view/widget modules too? Or, see how
QSqlDatabaseclass handles (one or more) database(s) using
On a final matter. You actually want the proxy model for your view. You create this via
self._patient_search_proxyModel = QSortFilterProxyModel()in
MainWindow. Why, why inside
MainWindow? The ability to proxy does not seem to be a main window thing --- indeed, your
ApptViewcode shows it is not. Depends on what you do and how you use it, but it might be better placed in the same
models..., or similar, you are getting the
_patient_search_model_sourcefrom? Or, if the sort-filter proxy is only for use in the view, it could just be in
ApptView. Unless you want to share the same sorting/filtering across your various views.....
@JonB ok. I'm picking up what you're laying down. I went through my code and made sure my structure is actually following the patterns I've decided on. To answer your question, I do need access to both the proxy model and source model, but thanks for clarifying the distinction. Thank you for your time!