2017-09-05 42 views
0

我試圖開發一個GUI來管理科學結果。爲此,我想介紹兩個數據集的結果。用戶將有一個可視化的reprentation來幫助他比較兩個QTableView中的結果。從兩個QTableView鏈接行以進行視覺比較

圖片:比較器的結果,

我想從兩個錶鏈接線,所以他們始終存在面對面。 當在一張表中改變命令時,其他命令將按照其順序依次相連。 最後,我想添加空行面到另一個表中沒有相對行的行。

我被認爲使用QSortFilterProxyModel,但我不知道如何使用它。

編輯 我的問題似乎並不清楚。我在這裏公式化。我發現自己是一個解決方案,所以這裏是我正在尋找的一個例子。

small Comparaison 在此示例行上,我根據名稱(bla,bli blo,blu)鏈接線。我們在同一行看到,表格呈現結果「bla」和「bli」的面對面線,因爲左側模型和右側都有。 右表中沒有「藍光」。所以我添加一個空行。 同樣在左邊的表中使用「blo」

在此示例中,項目按右表的「配置」進行排序。左表必須遵循右表選擇的順序。

這裏我沒有解決方案代碼

class Result(object): 
    def __init__(self, headerOrder, infoResult): 
     for n, headerName in enumerate(headerOrder): 
      self.__setattr__(headerName, infoResult[n]) 

     self.diff = self.reference_redshift - self.estimate 

ModelClass

class Result_model(QtCore.QAbstractTableModel): 

    def __init__(self, header, parent=None): 
     QtCore.QAbstractTableModel.__init__(self, parent) 
     self.__datas = [] 
     self.__headers = header 

    def rowCount(self, parent=None): 
     return len(self.__datas) 

    def columnCount(self, parent=None): 
     return len(self.__headers) 

    def data(self, index, role): 
     if role == QtCore.Qt.ToolTipRole: 
      row = index.row() 
      column = index.column() 
      return "{}: {}".format(self.__headers[column], getattr(self.__datas[row], self.__headers[column])) 

     if role == QtCore.Qt.DisplayRole: 

      row = index.row() 
      column = index.column() 
      value = getattr(self.__datas[row], self.__headers[column]) 

      return value 

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

     if role == QtCore.Qt.DisplayRole: 

      if orientation == QtCore.Qt.Horizontal: 

       if section < len(self.__headers): 
        return self.__headers[section] 
       else: 
        return "not implemented" 
      else: 
       return section 

    def supportedDragActions(self): 
     return QtCore.Qt.CopyAction 

    def supportedDropActions(self): 
     return Qt.CopyAction | Qt.MoveAction 

    def getResult(self, index): 
     row = index.row() 
     return self.__datas[row] 

    def sort(self, Ncol, order): 
     """Sort table by given column number. 
     """ 
     self.emit(QtCore.SIGNAL("layoutAboutToBeChanged()")) 
     attribut = self.__headers[Ncol] 
     self.__datas = sorted(
      self.__datas, key=lambda x: getattr(x, attribut), reverse=(order == QtCore.Qt.DescendingOrder)) 

     self.emit(QtCore.SIGNAL("layoutChanged()")) 

    def addResults(self, results): 
     self.beginInsertRows(QtCore.QModelIndex(), len(
      self.__datas), len(self.__datas) + len(results)) 
     for res in results: 
      self.__datas.append(res) 
     self.endInsertRows() 

的TableView只下跌拖動

class TableResult(QtGui.QTableView): 

    def __init__(self, parent=None): 
     QtGui.QTableView.__init__(self, parent) 
     self.setDragEnabled(True) 
     self.setDragDropMode(QtGui.QAbstractItemView.InternalMove) 
     self.header, self.aid_index = [["aid", "estimate", "reference_redshift", "diff", "amazed_executable_id", "amazed_configuration_id", 
             "astronomical_object_name", "star_forming_rate", "magnitude", "log_f_halpha", "emission_velocity_dispersion", "res_dir"], 0] 
     self.tag_info_result = ["aid", "estimate", "reference_redshift", "amazed_executable_id", "amazed_configuration_id", 
           "astronomical_object_name", "star_forming_rate", "magnitude", "log_f_halpha", "emission_velocity_dispersion", "res_dir"] 
     self.setItemDelegateForColumn(self.aid_index, ButtonDelegate(self)) 
     self.parent = parent 

    def startDrag(self, dropAction): 
     if(self.parent is not None and hasattr(self.parent, "selection")): 
      # create mime data object 
      mime = QtCore.QMimeData() 
      # start drag 
      drag = QtGui.QDrag(self) 
      drag.setMimeData(mime) 
      drag.start(QtCore.Qt.CopyAction | QtCore.Qt.CopyAction) 
     else: 
      print("Drag impossible") 

    def mouseMoveEvent(self, event): 
     self.startDrag(event) 

的TableView

class Selection_receiver(TableResult): 
    "Add the drop possibility from TableResult" 

    def __init__(self, setResultFunction, parent=None): 
     TableResult.__init__(self, parent) 
     self.setAcceptDrops(True) 
     self.setResultFunction = setResultFunction 

    def dragEnterEvent(self, event): 
     if (isinstance(event.source(), TableResult)): 
      event.accept() 
      event.acceptProposedAction() 
     else: 
      event.ignore() 

    def dropEvent(self, event): 
     print("dropEvent") 
     if (isinstance(event.source(), TableResult)): 
      event.acceptProposedAction() 
      model_result = event.source().parent.resModel 
      self.setResultFunction(model_result) 
     else: 
      event.ignore() 

窗件呈現兩個表

class Comparater_result_widget(QtGui.QWidget): 
    """ 
    Present two table for easy comparaison. 
    """ 

    def __init__(self,parent=None): 
     super(self.__class__, self).__init__(parent) 
     self.setWindowTitle("Result Comparer") 
     main_layout = QtGui.QVBoxLayout() 

     receiverSplitter = QtGui.QSplitter() 
     receiverSplitter.setOrientation(QtCore.Qt.Horizontal) 
     self.left_receiver = Selection_receiver(self.setLeftResult) 
     receiverSplitter.addWidget(self.left_receiver) 

     self.right_receiver = Selection_receiver(self.setRightResult) 
     receiverSplitter.addWidget(self.right_receiver) 

     main_layout.addWidget(receiverSplitter) 

     self.left_receiver.horizontalScrollBar().valueChanged.connect(
      self.right_receiver.horizontalScrollBar().setValue) 
     self.right_receiver.horizontalScrollBar().valueChanged.connect(
      self.left_receiver.horizontalScrollBar().setValue) 
     self.left_receiver.verticalScrollBar().valueChanged.connect(
      self.right_receiver.verticalScrollBar().setValue) 
     self.right_receiver.verticalScrollBar().valueChanged.connect(
      self.left_receiver.verticalScrollBar().setValue) 

     self.right_results = None 
     self.left_results = None 
     self.setLayout(main_layout) 

    def setLeftResult(self, model_result): 
     print("setLeftResult []".format(model_result)) 
     self.left_results = model_result 
     self.add_model_result(self.left_receiver, model_result) 

    def setRightResult(self, model_result): 
     print("setRightResult {}".format(model_result)) 
     self.right_results = model_result 
     self.add_model_result(self.right_receiver, model_result) 

    def add_model_result(self, receiver, model_result): 
     receiver.setModel(model_result) 

     if(self.right_results is not None and self.left_results is not None): 
      self.link_result() 

    def link_result(self): 
     # parse the two model and link results if the have equal on one 
     # particular attribut 
     pass 

    def OnLeftChangeOrder(self): 
     # somthing like right_proxy.reorder(left_order) 
     pass 

    def OnRightChangeOrder(self): 
     # something link left_proxy.reorder(right_order) 
     pass 
+0

你的描述是混亂的,你可以更好地解釋我請。 – eyllanesc

回答

0

這裏是我的解決方案,我創建的。

我用兩個代理:

  • 我自己的映射代理模型( 「MixRowProxyModel」)
  • 一個基本正常QSortFilterProxyModel( 「MySortProxyModel」)

源模型是MixRowProxyModel模型。 MixRowProxyModel是MySortProxyModel的型號

表可以處於兩種狀態,主機或從機。如果我把右邊的表格排在一列,右邊變成主人,左邊是奴隸。

當一個表是主,其MySortProxyModel是活動的,MixRowProxyModel是不活動的

當一個表是從設備,其MySortProxyModel是無效的,MixRowProxyModel是活性

當兩個源模型被設置。我在源模型的行之間創建一個映射。表格排序後,此貼圖不會更改。

當一個表成爲slave時,我爲其MixRowProxyModel構造映射(請參閱Comparater_result_widget中的方法更改)。爲此,我使用我在modelsource之間創建的初始映射。

class MixRowProxyModel(QtGui.QAbstractProxyModel): 

    def __init__(self, controller, side, parent=None): 
     QtGui.QAbstractProxyModel.__init__(self, parent=parent) 
     self.controller = controller 
     self.nbEmpty = 0 
     self.mapProxyToSource = None 
     self.isMaster = True 
     self.side = side 

    def setMap(self, mapping): 
     self.isMaster = False 
     self.mapProxyToSource = mapping 
     self.mapSourceToProxy = {v: k for k, 
           v in self.mapProxyToSource.items()} 

    def mapFromSource(self, sourceIndex): 
     #print("MixRowProxyModel Proxy Index model {}".format(sourceIndex.model())) 
     if(not sourceIndex.isValid()): 
      return self.index(-1, -1) 
     if(self.isMaster): 
      return self.index(sourceIndex.row(), sourceIndex.column(), parent=QtCore.QModelIndex) 
     else: 
      row = sourceIndex.row() 
      if(row in self.mapSourceToProxy): 
       return self.index(self.mapSourceToProxy[row], sourceIndex.column()) 
      else: 
       print("Invalid sourceIndex {}".format(row)) 
       return self.index(-1, -1) 

    def mapToSource(self, proxyIndex): 
     if(not proxyIndex.isValid()): 
      return self.sourceModel().index(-1, -1) 
     if(self.isMaster): 
      return self.sourceModel().index(proxyIndex.row(), proxyIndex.column()) 
     else: 
      row = proxyIndex.row() 
      if(row in self.mapProxyToSource): 
       return self.sourceModel().index(self.mapProxyToSource[row], proxyIndex.column()) 
      else: 
       # print("Invalid proxyIndex {}".format(row)) 
       return self.sourceModel().index(-1, -1) 

    def rowCount(self, parent=None): 

     return self.sourceModel().rowCount() + self.nbEmpty 

    def columnCount(self, parent=None): 

     return self.sourceModel().columnCount() 

    def addEmptyRow(self): 
     print("addEmptyRow {}".format(self.side)) 
     self.beginInsertRows(QtCore.QModelIndex(), 
          self.rowCount(), self.rowCount()) 

     self.nbEmpty += 1 
     self.endInsertRows() 
     return -1 

    def parent(self, index): 
     return QtCore.QModelIndex() 

    def index(self, row, column, parent=QtCore.QModelIndex): 
     if(row >= self.rowCount() or row < 0 or column < 0 or column >= self.columnCount()): 
      return QtCore.QModelIndex() 

     return self.createIndex(row, column, parent) 

    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole): 

     if role == QtCore.Qt.DisplayRole: 

      if orientation == QtCore.Qt.Horizontal: 

       return self.sourceModel().headerData(section, orientation, role) 
      else: 
       return section 

    def flags(self, index): 
     return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable 

    def data(self, index, role=QtCore.Qt.DisplayRole): 

     if(not index.isValid()): 
      return None 
     if(self.isMaster): 
      if(index.row() < self.sourceModel().rowCount()): 
       return self.sourceModel().data(index, role) 
      else: 
       return None 
     else: 
      if(not index.row() in self.mapProxyToSource): 
       return None 
      return self.sourceModel().data(self.sourceModel().index(self.mapProxyToSource[index.row()], index.column()), role) 

第二ProxyModel

class MySortProxyModel(QtGui.QSortFilterProxyModel): 
    sortedSignal = QtCore.pyqtSignal(QtCore.Qt.SortOrder) 

    def sort(self, column, order): 
     self.sourceModel().isMaster = True 
     #print("Sort order : {} sort Role {}".format(order, self.sortRole())) 
     self.setSortRole(QtCore.Qt.DisplayRole) 
     super().sort(column, order) 

     self.sortedSignal.emit(order) 

the Parent Widget controlling the two tables 

class Comparater_result_widget(QtGui.QWidget): 

    def __init__(self, controller, parent=None): 
     super(self.__class__, self).__init__(parent) 
     self.controller = controller 
     self.setWindowTitle("Result Comparer") 
     main_layout = QtGui.QVBoxLayout() 

     receiverSplitter = QtGui.QSplitter() 
     receiverSplitter.setOrientation(QtCore.Qt.Horizontal) 
     self.left_receiver = Selection_receiver(
      self.controller, self.setLeftResult) 
     receiverSplitter.addWidget(self.left_receiver) 

     self.right_receiver = Selection_receiver(
      self.controller, self.setRightResult) 
     receiverSplitter.addWidget(self.right_receiver) 

     main_layout.addWidget(receiverSplitter) 

     self.left_receiver.horizontalScrollBar().valueChanged.connect(
      self.right_receiver.horizontalScrollBar().setValue) 
     self.right_receiver.horizontalScrollBar().valueChanged.connect(
      self.left_receiver.horizontalScrollBar().setValue) 
     self.left_receiver.verticalScrollBar().valueChanged.connect(
      self.right_receiver.verticalScrollBar().setValue) 
     self.right_receiver.verticalScrollBar().valueChanged.connect(
      self.left_receiver.verticalScrollBar().setValue) 

     self.right_source_model = None 
     self.right_proxy_model = None 
     self.right_proxy_model2 = None 
     self.left_source_model = None 
     self.left_proxy_model = None 
     self.left_proxy_model2 = None 

     self.setLayout(main_layout) 

    def setLeftResult(self, model_result): 
     self.left_source_model = model_result 
     self.left_proxy_model = MixRowProxyModel(
      self.controller, "left", parent=self) 
     # self.left_proxy_model.sortedSignal.connect(self.onLeftChange) 
     self.left_proxy_model.setSourceModel(self.left_source_model) 
     self.left_proxy_model2 = MySortProxyModel(parent=self) 
     self.left_proxy_model2.sortedSignal.connect(self.onLeftChange) 
     self.left_proxy_model2.setSourceModel(self.left_proxy_model) 
     self.add_model_result(self.left_receiver, self.left_proxy_model2) 

    def setRightResult(self, model_result): 
     self.right_source_model = model_result 
     self.right_proxy_model = MixRowProxyModel(
      self.controller, "right", parent=self) 
     self.right_proxy_model.setSourceModel(self.right_source_model) 
     # self.right_proxy_model.sortedSignal.connect(self.onRightChange) 

     self.right_proxy_model2 = MySortProxyModel(parent=self) 
     self.right_proxy_model2.sortedSignal.connect(self.onRightChange) 
     self.right_proxy_model2.setSourceModel(self.right_proxy_model) 
     self.add_model_result(self.right_receiver, self.right_proxy_model2) 

    def add_model_result(self, receiver, model_result): 
     receiver.setModel(model_result) 

     if(self.right_source_model is not None and self.left_source_model is not None): 
      self.link_result() 

    def link_result(self): 
     name_to_row = {} 
     for numSourceLeftRow in range(self.left_source_model.rowCount()): 
      res = self.left_source_model.getResultNumRow(numSourceLeftRow) 
      name = res.astronomical_object_name 
      if(name in name_to_row): 
       name_to_row[name][0].append(numSourceLeftRow) 
      else: 
       name_to_row[name] = ([numSourceLeftRow], []) 

     for numSourceRightRow in range(self.right_source_model.rowCount()): 
      res = self.right_source_model.getResultNumRow(numSourceRightRow) 
      name = res.astronomical_object_name 
      if(name in name_to_row): 
       name_to_row[name][1].append(numSourceRightRow) 
      else: 
       name_to_row[name] = ([], [numSourceRightRow]) 
     self.mapLeftToRight = {} # key = leftRow; value = rightRow 
     self.mapRightToLeft = {} # key = rightRow; value = leftRow 
     for list_leftRow, list_rightRow in name_to_row.values(): 
      if(len(list_rightRow) > 1): 
       print(
        "Error more that index at right for same astronomical name {}".list_rightRow) 
      if(len(list_leftRow) > 1): 
       print(
        "Error more that index at left for same astronomical name {}".list_leftRow) 
      if(len(list_leftRow) == 0): 
       leftRow = self.left_proxy_model.addEmptyRow() 
      else: 
       leftRow = list_leftRow[0] 
      if(len(list_rightRow) == 0): 
       rightRow = self.right_proxy_model.addEmptyRow() 
      else: 
       rightRow = list_rightRow[0] 

      self.mapLeftToRight[leftRow] = rightRow 
      self.mapRightToLeft[rightRow] = leftRow 
     self.left_receiver.rowCountChanged(
      self.left_source_model.rowCount(), self.left_proxy_model.rowCount()) 
     self.right_receiver.rowCountChanged(
      self.right_source_model.rowCount(), self.right_proxy_model.rowCount()) 
     print("Link Done : LtoR : {}; RtoL {}".format(
      self.mapLeftToRight, self.mapRightToLeft)) 

    def onRightChange(self, order): 
     print("RightChange") 
     self.change(self.left_source_model, self.left_proxy_model, self.left_proxy_model2, self.right_source_model, self.right_proxy_model, 
        self.right_proxy_model2, self.mapLeftToRight, self.left_receiver, order) 

    def onLeftChange(self, order): 
     print("LeftChange") 
     self.change(self.right_source_model, self.right_proxy_model, self.right_proxy_model2, self.left_source_model, self.left_proxy_model, 
        self.left_proxy_model2, self.mapRightToLeft, self.right_receiver, order) 

    def change(self, slave_source_model, slave_proxy_model, 
       slave_proxy_model2, master_source_model, master_proxy_model, master_proxy_model2, 
       map_slave_to_master, slave_receiver, order): 
     if(slave_source_model is not None): 

      slaveMapping = dict() # in slave table key = indexProxy , value = index Source 
      if(order == QtCore.Qt.AscendingOrder): 
       unlinkIndex = 0 
      else: 
       unlinkIndex = master_source_model.rowCount() 
      for slaveSourceRow in range(slave_source_model.rowCount()): 

       # this line is link to one in master, so we keep the same 
       # proxy number 
       master_source_row = map_slave_to_master[slaveSourceRow] 
       if(master_source_row != -1): 
        master_source_index = master_source_model.index(
         master_source_row, 0) 
        master_proxy = master_proxy_model.mapFromSource(
         master_source_index) 
        master_proxy2 = master_proxy_model2.mapFromSource(
         master_proxy) 
        slaveProxyRow = master_proxy2.row() # same as master 
       else: 
        slaveProxyRow = unlinkIndex # we put it at the end or begining depending on order 
        unlinkIndex += 1 

       slaveMapping[slaveProxyRow] = slaveSourceRow 

      slave_proxy_model.layoutAboutToBeChanged.emit() 
      slave_proxy_model.setMap(slaveMapping) 
      slave_proxy_model2.setSortRole(
       QtCore.Qt.InitialSortOrderRole) # proxy 2 is reinitialise 
      slave_proxy_model.layoutChanged.emit() 
相關問題