c++ 正确复制QTableView中的选择(模型索引问题)

2uluyalo  于 2022-12-15  发布在  其他
关注(0)|答案(3)|浏览(271)

我有一个QTableView,它显示一个文件的解析数据。我打开文件,根据文件中每行的分隔符将其解析为不同的列,然后将其输出到视图。我使用QStandardItemModel来完成此任务。

设置模型的相关代码:

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中)。(column)在不同的行上,那么我添加一个换行符(\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

这显然不是我想要的,它似乎是添加一个换行符(\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

我在这里做错了什么?重新排列视图中的列有什么关系?

fquxozlt

fquxozlt1#

以下方法似乎有效...

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());

不管怎样,看看这是否更接近你的需要。

6yt4nkrj

6yt4nkrj2#

出于您的目的,最好使用 * 选择范围 *。

// 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()返回QItemSelectionQItemSelection是 * 选择范围 * 的列表。* 选择范围 * 是所选索引的连续矩形集合。

yqhsw0fo

yqhsw0fo3#

要复制带有标题的QTable视图中的选择,可覆盖按键事件并按如下方式使用。
无效按键事件(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);

相关问题