从两个QTableView链接行以进行视觉比较

问题描述:

我试图开发一个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

这里是我的解决方案,我创建的。

我用两个代理:

  • 我自己的映射代理模型( “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()