How to connect child tableview to parent model?
-
Hey everyone,
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
self.appt_ui.patient_search_tableView.setModel(MainWindow.get_patient_search_proxyModel)
"TypeError: setModel(self, QAbstractItemModel): argument 1 has unexpected type 'function'"I've tried passing in 'parent':
self.appt_ui.patient_search_tableView.setModel(MainWindow.get_patient_search_proxyModel(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, JudeEDIT: 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...
-
@judethedude said in How to connect child tableview to parent model?:
self.appt_ui.patient_search_tableView.setModel(MainWindow.get_patient_search_proxyModel)
get_patient_search_proxyModel
is a function, but you omit the trailing parentheses (()
).self.appt_ui.patient_search_tableView.setModel(MainWindow.get_patient_search_proxyModel(parent))
"AttributeError: 'MainWindow' object has no attribute '_patient_search_proxyModel'"
You must not pass
parent
from the caller asself
. Or something like that, not quite sure, you pass aparent
parameter to a method which does accept any parameters (other than the specialself
).In any case, you intend:
self.appt_ui.patient_search_tableView.setModel(MainWindow.get_patient_search_proxyModel())
Having said that, you are no better off, it won't work. You don't have the
MainWindow
instance inApptView
, so you can't (easily) access anything in the callingMainWindow
anyway. And you should not be trying to do this.You could "cast" the
parent
parameter inApptView
toMainWndow
. This should do what you were attemtping:self.appt_ui.patient_search_tableView.setModel(parent.get_patient_search_proxyModel())
But it's really messy, you're taking advantage of Python's non-explicit types and you are trying to access the
MainWindow
instance through parent inApptView
which 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?
Thanks again.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.
-
@judethedude
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
MainWindow
you already haveself._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 howQSqlDatabase
class handles (one or more) database(s) usingQSqlDatabase.database()
as astatic
method.On a final matter. You actually want the proxy model for your view. You create this via
self._patient_search_proxyModel = QSortFilterProxyModel()
inMainWindow
. Why, why insideMainWindow
? The ability to proxy does not seem to be a main window thing --- indeed, yourApptView
code shows it is not. Depends on what you do and how you use it, but it might be better placed in the samemodels...
, or similar, you are getting the_patient_search_model_source
from? Or, if the sort-filter proxy is only for use in the view, it could just be inApptView
. 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!