Hi. Thank you all for your answers/precisions.
I adapted the code proposed by Aaron to my needs. I'm attaching the new
version.
I implemented Add, Edit, Remove, and even Copy (i.e. "Edit as new").
A few questions remain (and probably much more to come...).
Isn't there a data duplication in the TableModel class, with both the
buildings[] list, and the model's Items ? In practice, this is what I
was going to do since there will be attributes that are not values but
references to other custom classes instances. But although I was
expecting to do this, I suspected it might not be the best way.
I ended up removing the Controller and merging it with the Main window.
It looked simpler to me, but it might not be the best choice.
I also had to remove the TableView class, because the TableView is
already instanciated in the .py file generated by Qt Designer and I
didn't know how to do otherwise.
In practice, I now have a main window that acts both as view and
controller, and a building model. (Qt's doc talks about Model/View
programming.)
The whole {TableView + AddEditCopyRemove buttons} is meant to be used
extensively in this application, so it may become a custom widget.
There is currently no value check. At some point, the model would need
to be able to perform a check and return an error if, for instance, we
don't want two buildings with the same name. I suppose I'll find a way
using Exceptions.
Thanks again for your precious help. Things are clearer now and I'm not
stuck anymore.
Have a nice evening.
--
Jérôme
import sys
from PySide.QtGui import *
#from PySide.QtCore import *
# Generate and import UIs
from subprocess import call
call(["pyside-uic", "mainwindow.ui", "-omainwindow.py"])
call(["pyside-uic", "dialogBuilding.ui", "-odialogBuilding.py"])
import mainwindow
import building as bui
class BuildingSurvey(QMainWindow):
def __init__(self, parent=None):
super(BuildingSurvey, self).__init__(parent)
self.ui = mainwindow.Ui_mainwindow()
self.ui.setupUi(self)
#Â Name
self.name = "Dummy name"
self.ui.nameLineEdit.setText(self.name)
# Buildings
self.buildingsModel = bui.BuildingTableModel([])
# This call does not work
#self.ui.buildingsTableView = bui.BuildingTableView(self.buildingsModel, self)
self.ui.buildingsTableView.setModel(self.buildingsModel)
#self.ui.buildingsTableView.horizontalHeader().setStretchLastSection(True)
#self.ui.buildingsTableView.resizeColumnsToContents()
# Test : hide a column
# self.ui.buildingsTableView.setColumnHidden(0, True)
self.buildingsModel.add_building(bui.Building('Bat 1', '1069', '3', '5', '48'))
self.buildingsModel.add_building(bui.Building('Bat 2', '500', '2', '5', '48'))
# Signals / slots connections
# self.ui.pbAdd.clicked.connect(self.on_pbAdd)
# self.ui.pbEdit.clicked.connect(self.on_pbEdit)
# self.ui.pbCopy.clicked.connect(self.on_pbCopy)
# self.ui.pbRemove.clicked.connect(self.on_pbRemove)
self.ui.pbAdd.clicked.connect(lambda: self.AddEditCopyRemoveBuilding('Add'))
self.ui.pbEdit.clicked.connect(lambda: self.AddEditCopyRemoveBuilding('Edit'))
self.ui.pbCopy.clicked.connect(lambda: self.AddEditCopyRemoveBuilding('Copy'))
self.ui.pbRemove.clicked.connect(lambda: self.AddEditCopyRemoveBuilding('Remove'))
# First implementation with four handlers + common function, duplicate code
#
# def on_pbAdd(self):
#
# self.editBuilding()
#
# def on_pbEdit(self):
#
# selected = self.ui.buildingsTableView.selectedIndexes()
# if selected != []:
# index = selected[0].row()
# building = self.buildingsModel.buildings[index]
# self.editBuilding(index, building)
#
# def on_pbCopy(self):
#
# selected = self.ui.buildingsTableView.selectedIndexes()
# if selected != []:
# index = selected[0].row()
# building = self.buildingsModel.buildings[index]
# self.editBuilding(None, building)
#
# def on_pbRemove(self):
#
# selected = self.ui.buildingsTableView.selectedIndexes()
# if selected != []:
# self.buildingsModel.remove_building(selected[0].row())
#
# def editBuilding(self, index=None, building=None):
#
# dialog = bui.BuildingDialog(building)
# if dialog.exec_():
# b = bui.Building(dialog.name, \
# dialog.surf, \
# dialog.nb_floors, \
# dialog.occ_w, \
# dialog.occ_y)
# if index == None:
# self.buildingsModel.add_building(b)
# else:
# self.buildingsModel.edit_building(index, b)
# Common handler with action parameter
def AddEditCopyRemoveBuilding(self, action):
if action == 'Add':
building = None
else:
selected = self.ui.buildingsTableView.selectedIndexes()
if selected == []:
return
index = selected[0].row()
building = self.buildingsModel.buildings[index]
if action == 'Remove':
self.buildingsModel.remove_building(index)
else:
dialog = bui.BuildingDialog(building)
if dialog.exec_() == QDialog.Accepted:
building = dialog.building
if (action == 'Add') or (action == 'Copy'):
self.buildingsModel.add_building(building)
else: # 'Edit'
self.buildingsModel.edit_building(index, building)
if __name__ == "__main__":
app = QApplication(sys.argv)
mySW = BuildingSurvey()
mySW.show()
sys.exit(app.exec_())
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'dialogBuilding.ui'
#
# Created: Mon Mar 3 18:05:31 2014
# by: pyside-uic 0.2.13 running on PySide 1.1.1
#
# WARNING! All changes made in this file will be lost!
from PySide import QtCore, QtGui
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(403, 292)
self.buttonBox = QtGui.QDialogButtonBox(Dialog)
self.buttonBox.setGeometry(QtCore.QRect(30, 240, 341, 32))
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.formLayoutWidget = QtGui.QWidget(Dialog)
self.formLayoutWidget.setGeometry(QtCore.QRect(10, 20, 381, 191))
self.formLayoutWidget.setObjectName("formLayoutWidget")
self.formLayout = QtGui.QFormLayout(self.formLayoutWidget)
self.formLayout.setFieldGrowthPolicy(QtGui.QFormLayout.AllNonFixedFieldsGrow)
self.formLayout.setContentsMargins(0, 0, 0, 0)
self.formLayout.setObjectName("formLayout")
self.laName = QtGui.QLabel(self.formLayoutWidget)
self.laName.setObjectName("laName")
self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.laName)
self.leName = QtGui.QLineEdit(self.formLayoutWidget)
self.leName.setObjectName("leName")
self.formLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.leName)
self.laSurf = QtGui.QLabel(self.formLayoutWidget)
self.laSurf.setObjectName("laSurf")
self.formLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.laSurf)
self.laFloors = QtGui.QLabel(self.formLayoutWidget)
self.laFloors.setObjectName("laFloors")
self.formLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.laFloors)
self.laOcc_w = QtGui.QLabel(self.formLayoutWidget)
self.laOcc_w.setObjectName("laOcc_w")
self.formLayout.setWidget(3, QtGui.QFormLayout.LabelRole, self.laOcc_w)
self.laOcc_y = QtGui.QLabel(self.formLayoutWidget)
self.laOcc_y.setObjectName("laOcc_y")
self.formLayout.setWidget(4, QtGui.QFormLayout.LabelRole, self.laOcc_y)
self.sbSurface = QtGui.QSpinBox(self.formLayoutWidget)
self.sbSurface.setMaximum(100000)
self.sbSurface.setObjectName("sbSurface")
self.formLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.sbSurface)
self.sbFloors = QtGui.QSpinBox(self.formLayoutWidget)
self.sbFloors.setObjectName("sbFloors")
self.formLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.sbFloors)
self.sbOcc_w = QtGui.QSpinBox(self.formLayoutWidget)
self.sbOcc_w.setSuffix("")
self.sbOcc_w.setPrefix("")
self.sbOcc_w.setMaximum(7)
self.sbOcc_w.setObjectName("sbOcc_w")
self.formLayout.setWidget(3, QtGui.QFormLayout.FieldRole, self.sbOcc_w)
self.sbOcc_y = QtGui.QSpinBox(self.formLayoutWidget)
self.sbOcc_y.setMaximum(52)
self.sbOcc_y.setObjectName("sbOcc_y")
self.formLayout.setWidget(4, QtGui.QFormLayout.FieldRole, self.sbOcc_y)
self.retranslateUi(Dialog)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), Dialog.accept)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), Dialog.reject)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Dialog", None, QtGui.QApplication.UnicodeUTF8))
self.laName.setText(QtGui.QApplication.translate("Dialog", "Name", None, QtGui.QApplication.UnicodeUTF8))
self.laSurf.setText(QtGui.QApplication.translate("Dialog", "Surface area", None, QtGui.QApplication.UnicodeUTF8))
self.laFloors.setText(QtGui.QApplication.translate("Dialog", "Floors", None, QtGui.QApplication.UnicodeUTF8))
self.laOcc_w.setText(QtGui.QApplication.translate("Dialog", "Occupancy d/w", None, QtGui.QApplication.UnicodeUTF8))
self.laOcc_y.setText(QtGui.QApplication.translate("Dialog", "Occupancy w/y", None, QtGui.QApplication.UnicodeUTF8))
self.sbSurface.setSuffix(QtGui.QApplication.translate("Dialog", " m2", None, QtGui.QApplication.UnicodeUTF8))
dialogBuilding.ui
Description: XML document
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'mainwindow.ui'
#
# Created: Mon Mar 3 18:05:31 2014
# by: pyside-uic 0.2.13 running on PySide 1.1.1
#
# WARNING! All changes made in this file will be lost!
from PySide import QtCore, QtGui
class Ui_mainwindow(object):
def setupUi(self, mainwindow):
mainwindow.setObjectName("mainwindow")
mainwindow.resize(800, 600)
self.centralwidget = QtGui.QWidget(mainwindow)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayoutWidget_2 = QtGui.QWidget(self.centralwidget)
self.verticalLayoutWidget_2.setGeometry(QtCore.QRect(10, 30, 761, 191))
self.verticalLayoutWidget_2.setObjectName("verticalLayoutWidget_2")
self.verticalLayout_3 = QtGui.QVBoxLayout(self.verticalLayoutWidget_2)
self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.horizontalLayout_2 = QtGui.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.nameLineEdit = QtGui.QLineEdit(self.verticalLayoutWidget_2)
self.nameLineEdit.setObjectName("nameLineEdit")
self.horizontalLayout_2.addWidget(self.nameLineEdit)
self.editNamePushButton = QtGui.QPushButton(self.verticalLayoutWidget_2)
self.editNamePushButton.setObjectName("editNamePushButton")
self.horizontalLayout_2.addWidget(self.editNamePushButton)
self.verticalLayout_3.addLayout(self.horizontalLayout_2)
self.horizontalLayout = QtGui.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.buildingsTableView = QtGui.QTableView(self.verticalLayoutWidget_2)
self.buildingsTableView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
self.buildingsTableView.setObjectName("buildingsTableView")
self.buildingsTableView.verticalHeader().setVisible(False)
self.horizontalLayout.addWidget(self.buildingsTableView)
self.verticalLayout_2 = QtGui.QVBoxLayout()
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.pbAdd = QtGui.QPushButton(self.verticalLayoutWidget_2)
self.pbAdd.setObjectName("pbAdd")
self.verticalLayout_2.addWidget(self.pbAdd)
self.pbEdit = QtGui.QPushButton(self.verticalLayoutWidget_2)
self.pbEdit.setObjectName("pbEdit")
self.verticalLayout_2.addWidget(self.pbEdit)
self.pbCopy = QtGui.QPushButton(self.verticalLayoutWidget_2)
self.pbCopy.setObjectName("pbCopy")
self.verticalLayout_2.addWidget(self.pbCopy)
self.pbRemove = QtGui.QPushButton(self.verticalLayoutWidget_2)
self.pbRemove.setObjectName("pbRemove")
self.verticalLayout_2.addWidget(self.pbRemove)
self.horizontalLayout.addLayout(self.verticalLayout_2)
self.verticalLayout_3.addLayout(self.horizontalLayout)
mainwindow.setCentralWidget(self.centralwidget)
self.menubar = QtGui.QMenuBar(mainwindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 25))
self.menubar.setObjectName("menubar")
mainwindow.setMenuBar(self.menubar)
self.statusbar = QtGui.QStatusBar(mainwindow)
self.statusbar.setObjectName("statusbar")
mainwindow.setStatusBar(self.statusbar)
self.retranslateUi(mainwindow)
QtCore.QMetaObject.connectSlotsByName(mainwindow)
def retranslateUi(self, mainwindow):
mainwindow.setWindowTitle(QtGui.QApplication.translate("mainwindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
self.editNamePushButton.setText(QtGui.QApplication.translate("mainwindow", "Edit name", None, QtGui.QApplication.UnicodeUTF8))
self.pbAdd.setText(QtGui.QApplication.translate("mainwindow", "Add", None, QtGui.QApplication.UnicodeUTF8))
self.pbEdit.setText(QtGui.QApplication.translate("mainwindow", "Edit", None, QtGui.QApplication.UnicodeUTF8))
self.pbCopy.setText(QtGui.QApplication.translate("mainwindow", "Copy", None, QtGui.QApplication.UnicodeUTF8))
self.pbRemove.setText(QtGui.QApplication.translate("mainwindow", "Remove", None, QtGui.QApplication.UnicodeUTF8))
mainwindow.ui
Description: XML document
from PySide.QtGui import *
#from PySide.QtCore import *
import dialogBuilding
class Building(object):
def __init__(self, name, surf, nb_floors, occ_w, occ_y):
self.name = name
self.surf = surf
self.nb_floors = nb_floors
self.occ_w = occ_w
self.occ_y = occ_y
class BuildingTableModel(QStandardItemModel):
def __init__(self, buildings, parent = None):
super(BuildingTableModel, self).__init__(parent)
self.buildings = buildings
for i in range(len(self.buildings)):
building = self.buildings[i]
self.setItem(i, 0, QStandardItem(buildings.name))
self.setItem(i, 1, QStandardItem(str(buildings.surf)))
self.setItem(i, 2, QStandardItem(str(buildings.nb_floors)))
self.setItem(i, 3, QStandardItem(str(buildings.occ_w)))
self.setItem(i, 4, QStandardItem(str(buildings.occ_y)))
self.setHorizontalHeaderLabels(['Name', 'Surf', 'Floors', 'Occ W', 'Occ Y'])
def add_building(self, building):
self.buildings.append(building)
self.appendRow([QStandardItem(building.name), \
QStandardItem(str(building.surf)), \
QStandardItem(str(building.nb_floors)), \
QStandardItem(str(building.occ_w)), \
QStandardItem(str(building.occ_y))])
def edit_building(self, index, building):
self.buildings[index] = building
self.removeRow(index)
self.insertRow(index,
[QStandardItem(building.name), \
QStandardItem(str(building.surf)), \
QStandardItem(str(building.nb_floors)), \
QStandardItem(str(building.occ_w)), \
QStandardItem(str(building.occ_y))])
def remove_building(self, index):
del(self.buildings[index])
self.removeRow(index)
# Apparently, doing this call in main window does not work
#
# self.ui.buildingsTableView = bui.BuildingTableView(self.buildingsModel, self)
#
# because the TableView is declared in the .py generated thanks to Qt Designer
#
# Therefore, I'm not using this class, and I do the setModel() in the main window
#
# class BuildingTableView(QTableView):
#
# def __init__(self, model, parent = None):
# super(BuildingTableView, self).__init__(parent)
# self.setModel(model)
# #self.ui.buildingsTableView.horizontalHeader().setStretchLastSection(True)
# #self.ui.buildingsTableView.resizeColumnsToContents()
class BuildingDialog(QDialog):
'''Dialog to Edit a building'''
def __init__(self, building=None):
super(BuildingDialog, self).__init__()
self.ui = dialogBuilding.Ui_Dialog()
self.ui.setupUi(self)
self.name = None
self.surf = None
self.nb_floors = None
self.occ_w = None
self.occ_y = None
if building != None:
self.ui.leName.setText(building.name)
self.ui.sbSurface.setValue(int(building.surf))
self.ui.sbFloors.setValue(int(building.nb_floors))
self.ui.sbOcc_w.setValue(int(building.occ_w))
self.ui.sbOcc_y.setValue(int(building.occ_y))
# This is done by Qt Designer
#self.connectSignals()
def accept(self):
self.name = self.ui.leName.text()
self.surf = int(self.ui.sbSurface.cleanText())
self.nb_floors = int(self.ui.sbFloors.cleanText())
self.occ_w = int(self.ui.sbOcc_w.cleanText())
self.occ_y = int(self.ui.sbOcc_y.cleanText())
super(BuildingDialog, self).accept()
@property
def building(self):
return Building(self.name, \
self.surf, \
self.nb_floors, \
self.occ_w, \
self.occ_y)
_______________________________________________ PySide mailing list [email protected] http://lists.qt-project.org/mailman/listinfo/pyside
