2015-04-05 98 views
2

我有一個使用TableView顯示內容的QML,我需要動態地從我的Python代碼注入數據到qt QML表。PyQT表模型數據綁定

以下QML:

import QtQuick 2.4 
import QtQuick.Controls 1.3 
import QtQuick.Window 2.2 
import QtQuick.Dialogs 1.2 


ApplicationWindow { 
//property var tableModel 
     title: qsTr("Hello World") 
     width: 640 
     height: 480 
     TableView { 
      model: tableModel 
      anchors.fill: parent 
      Component.onCompleted: console.log(tableModel) 
      TableViewColumn { 
       width:200 
       role: "hello_would" 
       title: "Hello Would" 
     } 
     TableViewColumn { 
      width:200 
      role: "foobar" 
      title: "Foobar" 
     } 
     TableViewColumn { 
      width:200 
      role: "zebra" 
      title: "Zebra" 
     } 
     TableViewColumn { 
      width:200 
      role: "ummmm" 
      title: "Ummmm" 
     } 

    } 
} 

所以我試圖實現QtAbstractTableModel,這裏是我的實現。

import sys 
import collections 

from PyQt5 import Qt 


class DummyList(Qt.QAbstractTableModel): 
    __DEFAULT__ = Qt.QVariant() 
    def __init__(self,*args,**kwargs): 
     super(DummyList,self).__init__(*args,**kwargs) 
     self.__roles = {} 
     self.__data = [] 
     self.append_data(
      [ 
       ("hello_would", "HELOOWOULD",), 
       ("foobar" , "ECHO",), 
       ("zebra" , "FOO",), 
       ("ummmm" , "BAR",) 
      ],True 
     ) 


    def append_data(self,value,header_update=False): 

     last_index = self.rowCount() 
     self.beginInsertRows(Qt.QModelIndex(),last_index,last_index) 
     value = collections.OrderedDict(value) 
     self.__data.append(value) 
     self.endInsertRows() 
     if header_update: 
      for key in value.keys(): 
       if key not in self.__roles.values(): 
        self.__roles[ Qt.Qt.UserRole + len(self.__roles) ] = key 


    def headerData(self,section,orientation,role): 

     if role == Qt.Qt.DisplayRole: 
      column = self.__roles.get(Qt.Qt.UserRole + section,Qt.QVariant()) 
     else: 
      column = self.__roles.get(role,Qt.QVariant()) 
     return column 


    def columnCount(self,parent=Qt.QModelIndex()): 

     return len(self.__data[0]) 


    def rowCount(self,parent=Qt.QModelIndex()): 

     return len(self.__data) 


    def roleNames(self): 
     """ 
     Model only invoked while binding to QML view . 
     """ 
     default_roles = super(DummyList,self).roleNames() 
     default_roles.update({ key:Qt.QByteArray(value.encode()) for key,value in self.__roles.items()}) 
     return default_roles 


    def data(self,index,role): 

     row = index.row() 
     if index.isValid(): 
      if role >= Qt.Qt.UserRole: 
       """ 
       Only fire on binding to QML 
       """ 
       column = self.__roles[role] 
      elif role == Qt.Qt.DisplayRole: 
       column = self.__roles[Qt.Qt.UserRole + index.column()] 
      else: 
       return DummyList.__DEFAULT__ 
      col_data = self.__data[row][column] 
      return Qt.QVariant(col_data) 
     else: 
      return DummyList.__DEFAULT__ 


class MyWindow(Qt.QWidget): 
    def __init__(self, *args): 
     Qt.QWidget.__init__(self, *args) 

     tablemodel = DummyList(self) 
     tableview = Qt.QTableView() 
     tableview.setModel(tablemodel) 

     layout = Qt.QVBoxLayout(self) 
     layout.addWidget(tableview) 
     self.setLayout(layout) 


def main(*args,**kwargs): 
    # We instantiate a QApplication passing the arguments of the script to it: 
    main_app = Qt.QApplication(list(args)) 

    """ 
    Load tableview in QML , it will Segmentation fault: 11 
    or display empty table 
    """ 

    qml_app_engine = Qt.QQmlApplicationEngine(
     main_app 
    ) 
    qml_app_engine.rootContext().setContextProperty(
     "tableModel", 
     DummyList() 
    )#If set property here , raise segementation most time 
    qml_app_engine.load(Qt.QUrl(sys.path[0]+"/hello_table.qml")) 
    qml_app_engine.rootContext().setContextProperty(
     "tableModel", 
     DummyList() 
    )#Nothing to show , but data method has been invoked 
    new_root = qml_app_engine.rootObjects()[0] 

    new_root.show() 


    """ 
    Load with custom create widget , everything is fine , 
    """ 
    #w = MyWindow() 
    #w.show() 

    # Now we can start it. 
    return_code = main_app.exec_() 
    return return_code 

if __name__ == "__main__": 
    sys.exit(main(*sys.argv)) 

但它不起作用,只顯示帶有標題的空表。

任何人都知道我的錯?

更新2015/04/06:

我也測試其他情況。

例一:在load之前調用setContextProperty QML。

案例二:調用setContextPropertyload QML。案例三:不要用QMlApplicationEngine,直接用MyWindow來顯示數據。

如果是一個,可能會導致兩個結果。

結果1:報告分段錯誤:11,系統退出。

結果2:顯示帶表頭的空表。 QtAbstractTableModel未被調用。 Component.onCompleted: console.log(tableModel) print null

在情況2中,一切正常,QtAbstractTableModel中的所有函數都被調用。 Component.onCompleted: console.log(tableModel)不會被調用。 但表仍然是空的。

我的控制檯輸出的情況下列出兩個,看到的數據方法已被調用,並正常工作。

2015-04-05 16:58:49,244 DEBUG decorator:24 data args : (<app.models.dummy.DummyList object at 0x1094680d8>, <PyQt5.QtCore.QModelIndex object at 0x109470438>, 7) , kwargs : {} 
2015-04-05 16:58:49,244 DEBUG decorator:26 data result : Foobar - P1 
2015-04-05 16:58:49,244 DEBUG decorator:24 data args : (<app.models.dummy.DummyList object at 0x1094680d8>, <PyQt5.QtCore.QModelIndex object at 0x109470438>, 8) , kwargs : {} 
2015-04-05 16:58:49,244 DEBUG decorator:26 data result : Zebra - P1 
2015-04-05 16:58:49,245 DEBUG decorator:24 data args : (<app.models.dummy.DummyList object at 0x1094680d8>, <PyQt5.QtCore.QModelIndex object at 0x109470438>, 9) , kwargs : {} 
2015-04-05 16:58:49,245 DEBUG decorator:26 data result : Ummmm - P1 

在情況三:所有的工作,正確顯示數據。但我必須使用QML而不是手動創建小部件。

+0

忘記添加我的開發環境。 我在MacOS,python3.4和Qt5下開發 – user1550863 2015-04-05 11:17:58

回答

0

我今天在Win7上也有同樣的問題,pyQt 5.4,Python 3.4.3。 我用類似的方式創建了我的表模型,將它通過setContextProperty與qml綁定,沒有錯誤,但在TableView中仍然沒有任何內容。 我也注意到,只有我的模型實例的rowCount方法被調用,並且返回的值是正確的非零值。

+0

不太確定,你有override override roleNames方法嗎? – user1550863 2015-04-06 14:43:53

+0

不,我沒有,因爲在文檔中寫道:「當繼承QAbstractTableModel時,必須實現rowCount(),columnCount()和data()。」但是我已經覆蓋了headerData()方法。 – MZPRX 2015-04-09 08:34:36

+0

[在Qt 5.0中公開QAbstractListModel元素屬性到QML](http://stackoverflow.com/questions/14031412/expose-qabstractlistmodel-element-properties-to-qml-in-qt-5-0) 您還必須實現角色名稱方法,返回您使用tableview定義的所有角色。 另外,數據方法應該根據角色而不是列索引來返回。 – user1550863 2015-04-10 15:15:55

0

我發現如何使我的代碼工作!

我只是改變

qml_app_engine = Qt.QQmlApplicationEngine(
    main_app 
) 
qml_app_engine.rootContext().setContextProperty(
    "tableModel", 
    DummyList() 
)#If set property here , raise segementation most time 

mod = DummyList() 
qml_app_engine = Qt.QQmlApplicationEngine(
    main_app 
) 
qml_app_engine.rootContext().setContextProperty("tableModel",mod)#If set property here , raise segementation most time 
qml_app_engine.load(Qt.QUrl(sys.path[0]+"/hello_table.qml")) 

只是時間更改爲instantialize模型實例,我的代碼工作。

但是任何qt專家都可以解釋爲什麼這種改變會起作用?