pyqt5 tableView 显示对象数组(objectModel objectView)

 面向对象编程更符合人类思维。python 中orm要比sql更常用,因而在tableview中显示对象数组是个自然需求,网上这方面资料很少。这里是个完整的对象数组显示方案,再结合sqlalchmy就不难了。

# import copy
from datetime import datetime
from PyQt5.QtCore import Qt, QAbstractTableModel, QModelIndex
from PyQt5.QtWidgets import QTableView, QMenu, QInputDialog, QErrorMessage, QDialog, QDialogButtonBox, QVBoxLayout
import traceback
import logging
from PyQt5 import QtCore
from MyTableView import MyTableView
def getAttrRecursive(obj, attr):
    """ Recursive introspection (i.e. get the member 'b' of a member 'a' by name as 'a.b').
    """
    return getattr(obj, attr)
    # try:
    #     p = attr.index(".")
    #     obj = getattr(obj, attr[0:p])
    #     return getAttrRecursive(obj, attr[p+1:])
    # except ValueError:
    #     return getattr(obj, attr)


def setAttrRecursive(obj, attr, value):
    """ Recursive introspection (i.e. set the member 'b' of a member 'a' by name as 'a.b').
    """
    setattr(obj, attr, value)
    # try:
    #     p = attr.index(".")
    #     obj = getattr(obj, attr[0:p])
    #     setAttrRecursive(obj, attr[p+1:], value)
    # except ValueError:
    #     setattr(obj, attr, value)


class ObjectModel(QAbstractTableModel):
    def __init__(self,  parent=None):
        QAbstractTableModel.__init__(self, parent)
        self.objects = []
        self.properties =  []
    def getObject(self, index):
        if not index.isValid():
            return None
        objectIndex = index.row()
        try:
            return self.objects[objectIndex]
        except IndexError:
            return None
    def getObjectRow(self, row):
        try:
            return self.objects[row]
        except IndexError:
            return None
    def getProperty(self, index):
        if not index.isValid():
            return None
        propertyIndex = index.column()
        try:
            return self.properties[propertyIndex]
        except IndexError:
            return None

    def rowCount(self, parent=None, *args, **kwargs):
        return len(self.objects)

    def columnCount(self, parent=None, *args, **kwargs):
        return len(self.properties)

    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid(): 
            return None
        # elif role != Qt.DisplayRole: 
        #     return None
        obj = self.getObject(index)
        prop = self.getProperty(index)
        if (obj is None) or (prop is None):
            return None
        try:
            if role in [Qt.DisplayRole, Qt.EditRole]:
                return getAttrRecursive(obj, prop['attr'])
        except:
            return None
        return None

    def setData(self, index, value, role=Qt.EditRole):
        if not index.isValid():
            logging.info("index invalid")
            return False
        obj = self.getObject(index)
        prop = self.getProperty(index)
        if (obj is None) or (prop is None):
            logging.info("obj is None or prop is None")
            return None
        try:
            if role == Qt.EditRole:
                logging.info(type(value))
                logging.info([obj,prop['attr'],value])
                setAttrRecursive(obj, prop['attr'], value)
                return True
        except:
            traceback.print_exc()
            return False
        return False

    def flags(self, index):
        flags = QAbstractTableModel.flags(self, index)
        if not index.isValid():
            return flags
        prop = self.getProperty(index)
        if prop is None:
            return flags
        flags |= Qt.ItemIsEnabled
        flags |= Qt.ItemIsSelectable
        mode = prop.get('mode', "Read/Write")
        if "Write" in mode:
            flags |= Qt.ItemIsEditable
        return flags
    def headerData(self, col, orientation, role= Qt.DisplayRole):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            if col<len(self.properties):
                p=self.properties[col]
                if p!=None:
                    return QtCore.QVariant(p["header"])
                else:
                    return QtCore.QVariant()
            # return QtCore.QVariant(self.properties[col]['header'])
        return QtCore.QVariant()

    def setObjects(self,objects,properties):
        self.beginResetModel()
        self.objects=objects
        self.properties=properties
        self.endResetModel()

    def clearObjects(self):
        if len(self.objects):
            self.beginResetModel()
            del self.objects[:]
            self.endResetModel()

    def sort(self, Ncol, order):
        """Sort table by given column number.
        """
        self.layoutAboutToBeChanged.emit()#SIGNAL("layoutAboutToBeChanged()"))
        print(Ncol)
        # self.objects =sorted(self.objects, key=operator.itemgetter(Ncol))        
        try:
            self.objects =sorted(self.objects,key=lambda x: getattr(x,self.properties[Ncol]["attr"]))
        except TypeError:
            pass
        if order == Qt.DescendingOrder:
            self.objects.reverse()
        self.layoutChanged.emit()#SIGNAL("layoutChanged()"))

class ObjectView(MyTableView):
    def __init__(self, model, parent=None):
        super().__init__(parent)
    def setModel(self, model):
        QTableView.setModel(self, model)
        for i, prop in enumerate(model.properties):
            # logging.info([i,prop])
            if prop.get('visible',True)==True:
                self.setColumnHidden(i,False)
            else:
                self.setColumnHidden(i,True)
            width=prop.get('width',100)
            self.setColumnWidth(i,width)
            # self.setItemDelegateForColumn(i, prop.get('delegate'))

    def selectedRows(self):
        selectedIndexes = self.selectionModel().selection().indexes()
        rows = set()
        for index in selectedIndexes:
            rows.add(index.row())
        return sorted(list(rows))

    def selectedColumns(self):
        selectedIndexes = self.selectionModel().selection().indexes()
        columns = set()
        for index in selectedIndexes:
            columns.add(index.column())
        return sorted(list(columns))

    def clearObjects(self):
        self.model().clearObjects()



if __name__ == "__main__":
    import sys
    from PyQt5.QtWidgets import QApplication

    # We'll create a table model/view for a list of these objects.
    class MyObject(object):
        def __init__(self, name="New Obj", s="", i=0, f=0.0, b=True, hasChild=True):
            self.name = name
            self.strValue = s
            self.intValue = i
            self.floatValue = f
            self.boolValue = b
            self.dateValue = datetime.now()
            self._fileName = ""
            if hasChild:
                self.child = MyObject(name, s, i, f, b, False)

        # We'll have the model/view access the fileName property
        # rather than the _fileName attribute so that we
        # run our custom code whenever the fileName is changed.
        @property
        def fileName(self):
            return self._fileName

        @fileName.setter
        def fileName(self, fileName):
            if len(fileName) and (fileName != self._fileName):
                print("Setting file name for " +
                      self.name + " to " + fileName + ".")
                self._fileName = fileName

        # We'll have the model/view call this
        # when a button is clicked in the object's row/column.
        def clicked(self):
            print(self.name + " was clicked.")

    # Create the QApplication.
    app = QApplication(sys.argv)

    # Create our object list.
    a = MyObject("obj A", "a str", 3, 0.042, True)
    b = MyObject("obj B", "b str", -1, -10.069, False)
    objects = [a, b]

    # Specify the properties to display in the model/view.
    properties = [
        {'width':90,'visible':True,'attr': "name",           'header': "Read Only Name",      'mode': "Read Only"},
        {'attr': "strValue",       'header': "String"},
        {'attr': "intValue",       'header': "Integer"},
        {'attr': "floatValue",     'header': "Float"},
        {'attr': "boolValue",      'header': "Bool"},
        {'attr': "dateValue",      'header': "Date/Time",           'text': "%c"},
        {'attr': "fileName",       'header': "File Name",
            'action': "fileDialog"},
        {'attr': "clicked",        'header': "Button",
            'action': "button", 'text': "Click Me!"},
        {'attr': "child.intValue", 'header': "Child Int"},
        {'attr': "strValue",       'header': "String Combo Box",
            'choices': ['First Choice', 'Second Choice']},
        {'attr': "child.intValue",
            'header': "Child Int Combo Box", 'choices': [42, 82]},
        {'attr': "floatValue",     'header': "Float Combo Box",     'choices': [('PI', 3.14), ('-PI', -3.14)]}]

    # Print property names/values/types prior to editing.
    print("---------- BEFORE EDITING ----------")
    for obj in objects:
        for prop in properties:
            attr = prop['attr']
            try:
                value = getattr(obj, attr)
                print(attr, value, type(value))
            except:
                pass

    # Create the model/view.
    model = ObjectModel()
    view = ObjectView(model)

    model.setObjects(objects,properties)
    view.setModel(model)
    # Show the model/view and run the application.
    view.setAttribute(Qt.WA_DeleteOnClose)
    view.show()
    status = app.exec()

    # Print property names/values/types post editing.
    print("---------- AFTER EDITING ----------")
    for obj in objects:
        for prop in properties:
            attr = prop['attr']
            try:
                value = getattr(obj, attr)
                print(attr, value, type(value))
            except:
                pass
    sys.exit(status)
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值