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))

Attachment: 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))

Attachment: 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

Reply via email to