2016-06-14 69 views
2

我有一個QTableView顯示來自文件的解析數據。我打開文件,根據文件中每行的分隔符將其解析爲不同的列,然後將其輸出到視圖。我爲此使用了QStandardItemModel正確地複製QTableView中的選擇(模型索引問題)

相關代碼,樹立典型:

QStandardItemModel* model = new QStandardItemModel(this); 

int lineIndex = 0; 
QStringList headers; 
headers << "Col1" << "Col2"; 
model->setHorizontalHeaderLabels(headers); 
file.seek(0); 
QTextStream inData(&file); 
while (!inData.atEnd()){ 
    QString line = inData.readLine(); 
    QStringList lineToken = line.split(":"/*, QString::SkipEmptyParts*/); 
    for (int j = 0; j < lineToken.size(); j++) { 
    QString value = lineToken.at(j); 
    QStandardItem* item = new QStandardItem(value); 
    model->setItem(lineIndex, j, item); 
    } 
lineIndex++; 

} 
ui->tableView->setModel(model); 
ui->tableView->horizontalHeader()->setSectionsMovable(true); 

請注意,我有它,這樣我可以左右拖動列標題來重新排序表中的列。這將在稍後重要。

我也已經設置了,這樣我可以選擇一整行(或單個細胞)的圖,按Ctrl + C,並且具有的選擇拷貝到剪貼板的內容。我爲選定的項目/行一個單獨的模型:

QApplication::clipboard()->clear(); 
QItemSelectionModel* selection = ui->tableView->selectionModel(); 
QModelIndexList indexes = selection->selectedIndexes(); 

QString clipboardString; 
QModelIndexList selectedIndexes = ui->tableView->selectionModel()->selectedIndexes(); 

然後我有一些代碼來處理,如果選擇跨多個行會發生什麼。我爲此設置了一個QModelIndex。這個實現很簡單。我使用for循環遍歷所選單元格的索引。如果下一個索引(列)是在同一行作爲當前一個,我添加數據到變量我最終會寫入剪貼板和標籤(\t)追加到它(因爲我想複製的細胞分隔通過標籤,我可以很容易地複製到剪貼板並粘貼到Excel中,如果我想)。如果下一個索引(列)是在不同的行,然後我追加一個換行符(\n)。這裏是代碼:

for (int i = 0; i < selectedIndexes.count(); ++i) 
{ 
    QModelIndex current = selectedIndexes[i]; 
    QString displayText = current.data(Qt::DisplayRole).toString(); 
    // If there exists another column beyond this one. 
    if (i + 1 < selectedIndexes.count()) { 
    QModelIndex next = selectedIndexes[i+1]; 
    // If the column is on different row, the clipboard should take note. 
    qDebug() << "next.row()" << next.row(); 
    qDebug() << "current.row()" << current.row(); 
    if (next.row() != current.row()){ 
     displayText.append("\n"); 
    } else { 
     // Otherwise append a column separator. 
     displayText.append("\t"); 
    } 
    } 
    clipboardString.append(displayText); 
} 
QApplication::clipboard()->setText(clipboardString); 

這裏是我有一個問題。如果我重新排列視圖中的列,則索引檢查會中斷。我的觀點不變看起來像這樣:

+------+------+ 
| Col1 | Col2 | 
+------+------+ 
| A | B | 
| W | X | 
+------+------+ 

如果我在表中選擇任何值,我可以按Ctrl + C和粘貼預期。沒問題。選擇這個整個表格後,複製和粘貼會導致這樣的輸出:

A B 
W X 

但是,如果我拖Col1到哪裏Col2是,有效地切換它們看起來像這樣:

+------+------+ 
| Col2 | Col1 | 
+------+------+ 
| B | A | 
| X | W | 
+------+------+ 

。 ..並選擇整個東西,並粘貼它,我得到這個輸出:

B 
X 
A 
W 

這顯然不是我想要的,它似乎是添加一條線ak(\n)在每個選定的單元格之後。

qDebug()輸出爲先,未經修改的表是:

next.row() 0 
current.row() 0 
next.row() 1 
current.row() 0 
next.row() 1 
current.row() 1 

qDebug()輸出第二,重新安排表:

next.row() 1 
current.row() 0 
next.row() 0 
current.row() 1 
next.row() 1 
current.row() 0 

我在做什麼錯在這裏?爲什麼視圖中的列會重新排列?

+0

您似乎認爲'QItemSelectionRange'將連續存儲屬於給定行的所有索引。我不確定是否有這樣的保證。不要在循環中構建一個QString,而是嘗試構造像'map >',然後處理它以生成所需的最終QString。 –

+0

我看到你在說什麼,但在我將其處理成最終字符串的情況下,我仍然需要了解列的順序以及用戶如何更改它們。我想我可能能夠捕捉到列重新排序時發出的信號。那麼,或者我創建一組與新列順序相對應的索引。 – orbit

回答

2

下的工作原理。

std::map<int/* row */, std::map<int/* col */, QString> > stuff; 
int minCol = std::numeric_limits<int>::max(); 
int maxCol = std::numeric_limits<int>::min(); 
for (int i = 0; i < selectedIndexes.count(); ++i) { 
    QModelIndex current = selectedIndexes[i]; 
    int col = horizontalHeader()->visualIndex(current.column()); 
    stuff[current.row()][col] = current.data(Qt::DisplayRole).toString(); 
    minCol = std::min(minCol, current.column()); 
    maxCol = std::max(maxCol, current.column()); 
} 

QString displayText; 
for (const auto &byRow: stuff) { 
    for (int col = minCol; col <= maxCol; ++col) { 
    auto i = byRow.second.find(col); 
    if (i != byRow.second.end()) 
     displayText += i->second; 
    displayText += col == maxCol ? "\n" : "\t"; 
    } 
} 

注意索引列是如何使用映射...

int col = horizontalHeader()->visualIndex(current.column()); 

無論如何,看看是否是任何接近你所需要的。

+0

非常感謝你,這很好,你幫我解決了這個問題,節省了我很多時間和頭痛。 – orbit

0

你的目的是更好地與選擇工作的範圍

// If your selection is continuous, there should be just one range. 
QItemSelectionRange range = selection->selection().front(); 

int row, col; 
for (int i = 0; i < range.height(); i++) 
{ 
    QStringList rowStrList; 
    for (int j = 0; j < range.width(); j++) 
    { 
     row = range.top() + i; 
     col = range.left() + j; 
     rowStrList << model->index(row, col).data(Qt::DisplayRole).toString(); 
    } 

    clipboardString += rowStrList.join("\t"); 
    clipboardString += "\n"; 
} 

其基本思想是逐行處理選擇。

QItemSelectionModel::selection() rendns QItemSelection這是一個列表選擇範圍選擇範圍是一組選定索引的連續矩形。

+0

什麼是'itemSelection()'?我找不到任何文件。 – orbit

+0

對不起,這是一個錯誤。該方法僅被稱爲'QItemSelectionModel :: selection()',返回類型是'QItemSelection'。我會解決答案。 – Tomas

+0

當我使用您的解決方案時,我得到了相同的結果。當我重新排列列時,選擇範圍不連續,因此會創建一堆不同的選擇範圍,而不是僅創建一個。然後,選擇範圍將從上到下而不是從左到右。我無法弄清楚這一點。我能想到的唯一方法就是讓我們擁有另一組索引。有模型索引和視圖索引會使這更容易,但在這裏不存在(但它存在於Java中,使用'JTable')。 :( – orbit