[英]Copying a selection in a QTableView properly (model index issues)
I have a QTableView
that displays parsed data from a file.我有一个
QTableView
,它显示文件中已解析的数据。 I open the file, parse it into different columns based on its delimiter for each line in the file, and then output it to the view.我打开文件,根据文件中每一行的分隔符将其解析为不同的列,然后将其 output 显示到视图中。 I am using a
QStandardItemModel
for this.我为此使用
QStandardItemModel
。
Relevant code that sets up the model:设置model的相关代码:
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);
Note that I have it so that I can drag the column headers around to reorder the columns in the table.请注意,我有它以便我可以拖动列标题来重新排序表中的列。 This will be important later.
这在以后很重要。
I also have the view set up so that I can select an entire row (or individual cells), hit Ctrl+C, and have the contents of the selection copy to the clipboard.我还设置了视图,以便我可以 select 整行(或单个单元格),按 Ctrl+C,并将选择的内容复制到剪贴板。 I made a separate model for the selected items/rows:
我为所选项目/行单独制作了 model:
QApplication::clipboard()->clear();
QItemSelectionModel* selection = ui->tableView->selectionModel();
QModelIndexList indexes = selection->selectedIndexes();
QString clipboardString;
QModelIndexList selectedIndexes = ui->tableView->selectionModel()->selectedIndexes();
I then have some code to handle what happens if the selection spans multiple rows.然后我有一些代码来处理如果选择跨越多行会发生什么。 I set up a
QModelIndex
for this.我为此设置了一个
QModelIndex
。 The implementation for this is simple.这个的实现很简单。 I use a for loop to iterate through the indices of the selected cells.
我使用 for 循环遍历所选单元格的索引。 If the next index (column) is on the same row as the current one, I add that data to the variable I will eventually write to the clipboard and append a tab (
\t
) to it (since I want the copied cells to be delimited by tabs so I can easily copy to the clipboard and paste into Excel if I want).如果下一个索引(列)与当前索引(列)在同一行,我将该数据添加到我最终将写入剪贴板的变量和 append 一个制表符(
\t
)到它(因为我希望复制的单元格是由制表符分隔,以便我可以轻松复制到剪贴板并粘贴到 Excel(如果需要)。 If the next index (column) is on a DIFFERENT row, then I append a line break ( \n
).如果下一个索引(列)在不同的行上,则 I append 换行符 (
\n
)。 Here is the code:这是代码:
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);
Here's where I have a problem.这是我遇到问题的地方。 If I rearrange the columns in my view, the index check breaks.
如果我重新排列视图中的列,索引检查就会中断。 My untouched view looks like this:
我未受影响的视图如下所示:
+------+------+
| Col1 | Col2 |
+------+------+
| A | B |
| W | X |
+------+------+
If I select any of the values in the table, I can Ctrl+C and paste as expected.如果我 select 表中的任何值,我可以按 Ctrl+C 并按预期粘贴。 No problem.
没问题。 A copy and paste after selecting this entire table would result in this output:
选择整个表格后进行复制和粘贴将导致此 output:
A B
W X
However, if I drag Col1
over to where Col2
is, effectively switching them to look like this:但是,如果我将
Col1
拖到Col2
所在的位置,有效地将它们切换为如下所示:
+------+------+
| Col2 | Col1 |
+------+------+
| B | A |
| X | W |
+------+------+
...and select the entire thing and paste it, I get this output: ...和 select 整个东西并粘贴它,我得到这个 output:
B
X
A
W
This is obviously not what I want and it seems to be adding a line break ( \n
) after every selected cell.这显然不是我想要的,它似乎在每个选定的单元格之后添加一个换行符(
\n
)。
My qDebug()
outputs for the first, unmodified table are:我的第一个未修改表的
qDebug()
输出是:
next.row() 0
current.row() 0
next.row() 1
current.row() 0
next.row() 1
current.row() 1
My qDebug()
outputs for the second, rearranged table are:我的第二个重新排列的表的
qDebug()
输出是:
next.row() 1
current.row() 0
next.row() 0
current.row() 1
next.row() 1
current.row() 0
What am I doing wrong here?我在这里做错了什么? Why would it matter that the columns in the view are rearranged?
为什么重新排列视图中的列很重要?
The following appears to work...以下似乎有效...
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";
}
}
Note how the index columns are mapped using...请注意索引列是如何使用...映射的
int col = horizontalHeader()->visualIndex(current.column());
Anyway, see if that's any closer to what you need.无论如何,看看这是否更接近您的需要。
For your purposes it is better to work with the selection ranges .为了您的目的,最好使用选择范围。
// 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";
}
The basic idea is to process selection row by row.基本思想是逐行处理选择。
The QItemSelectionModel::selection()
retruns QItemSelection
which is a list of selection ranges . QItemSelectionModel::selection()
重新运行QItemSelection
,它是一个选择范围列表。 Selection range is a continuous rectangular set of selected indices.选择范围是一组连续的矩形选定索引。
For copying the selection in the QTableview with header, you can override the keypressevent and use like this.要使用 header 复制 QTableview 中的选择,您可以覆盖按键事件并像这样使用。
void keyPressEvent(QKeyEvent *event) { void keyPressEvent(QKeyEvent *事件){
this->setSelectionBehavior(QAbstractItemView::SelectRows);
this->setSelectionMode(QAbstractItemView::MultiSelection);
QModelIndexList selectedRows = selectionModel()->selectedRows();
QModelIndexList selectedColumns = selectionModel()->selectedColumns();
if(event->key() == Qt::Key_Insert)
{
tblview_model->customInsertRows();
}
else if(event->key() == Qt::Key_Delete)
{ if(!selectedRows.isEmpty())
model()->removeRows(selectedRows.at(0).row(),
selectedRows.size());
}
else if(event->matches(QKeySequence::Paste)) {
if(copyPasteEnabled == true){
text = QApplication::clipboard()->text();
if(!text.isEmpty()){
tblview_model->pasteData(text);
}
}
}
if(!selectedIndexes().isEmpty()){
if(event->matches(QKeySequence::Copy)){
QString text;
QStringList hdr_lst;
const std::vector<std::string>& hdrs = tblview_model->getHeader();
QItemSelectionRange range = selectionModel()->selection().first();
if (!selectedColumns.isEmpty())
{
for (auto j = 0; j < selectedColumns.size(); ++j)
{
std::string hd = hdrs[selectedColumns.at(j).column()];
if (std::find(hdrs.begin(), hdrs.end(), hd) != hdrs.end())
hdr_lst << QString::fromStdString(hd);
}
text += hdr_lst.join("\t");
text += "\n";
for (auto i = 0; i < tblview_model->rowCount(); ++i)
{
QStringList rowContents;
for (auto j = 0; j < selectedColumns.size(); ++j)
{
if (!isColumnHidden(j)) {
rowContents << model()->index(i, selectedColumns.at(j).column()).data().toString();
}
text += rowContents.join("\t");
text += "\n";
}
}
else if (!selectedRows.isEmpty())
{
for (auto h : hdrs)
{
hdr_lst << QString::fromStdString(h);
}
text += hdr_lst.join("\t");
text += "\n";
//for (auto i = range.top(); i <= range.bottom(); ++i)
for (auto i = 0; i < selectedRows.size(); ++i)
{
QStringList rowContents;
for (auto j = 0; j <= tblview_model->columnCount(); ++j)
{
if (!isColumnHidden(j)) {
rowContents << model()->index(selectedRows.at(i).row(), j).data().toString();}
}
text += rowContents.join("\t");
text += "\n";
}
}
QApplication::clipboard()->setText(text);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.