【问题标题】:QTableWidget: signal itemChanged fires too late (after signals for other actions, close, etc.)QTableWidget:信号itemChanged触发太晚(在其他动作的信号之后,关闭等)
【发布时间】:2021-02-27 19:10:29
【问题描述】:

我使用QTableWidget 来表示一些用户可编辑的数据。编辑后,通过连接到表的itemChanged(QTableWidgetItem*) 信号来更新数据的内部表示。 我有一个菜单操作“保存”来保存数据,如果用户尝试关闭窗口,我会检查closeEvent 中是否修改了当前文档。如果用户在未保存修改时尝试关闭,系统会询问他们是否要保存。

现在的问题是,itemChanged 信号似乎只在表格失去输入焦点时发送。考虑这种情况:用户双击表格中的一个单元格,更改文本,然后立即单击保存。在发送itemChanged 信号之前触发保存操作,因此在用户输入实际同步之前。同样,如果用户关闭窗口。

我尝试在保存函数中主动从表中读取数据,而不是等待信号触发,但这也不起作用,因为相应项目在其编辑器打开时仍包含旧数据。 这显然是一个大问题,因为要么保存了错误的数据,要么根本没有保存,即数据丢失。

我该如何正确处理这个问题?


附件是一个演示问题的最小工作示例。出于演示目的,closeEvent 只是无条件地执行“保存”(仅在此处打印)。在实际应用中,它会先检查数据是否被修改,没有正确执行。

主窗口.h:

#include <QMainWindow>
#include <QMenu>
#include <QMenuBar>
#include <QTableWidget>

#include <iostream>
#include <vector>

class MainWindow : public QMainWindow
{
    Q_OBJECT

private:
    std::vector<double> data;
    QTableWidget* table;

public:
    MainWindow(QWidget* parent = nullptr) : QMainWindow(parent)
    {
        data = {1.0, 2.0, 3.0};
        table = new QTableWidget(this);
        table->setRowCount(1);
        table->setColumnCount(data.size());
        for (size_t i = 0; i < data.size(); i++)
            table->setItem(0, i, new QTableWidgetItem(QString::number(data[i])));
        connect(table, SIGNAL(itemChanged(QTableWidgetItem*)),
                SLOT(on_table_itemChanged(QTableWidgetItem*)));
        setCentralWidget(table);

        QMenuBar* menubar = new QMenuBar(this);
        QMenu* file = new QMenu("File", menubar);
        QAction* save = new QAction("Save", this);
        file->addAction(save);
        menubar->addMenu(file);
        setMenuBar(menubar);
        connect(save, SIGNAL(triggered()), SLOT(on_actionSave_triggered()));
        setGeometry(0, 0, 400, 100);
    }

private slots:
    void on_actionSave_triggered()
    {
        std::cout << "data: " << data[0] << ", " << data[1] << ", " << data[2] << std::endl;
        std::cout << "item 0: " << table->item(0, 0)->text().toDouble()
                  << ", item 1: " << table->item(0, 1)->text().toDouble()
                  << ", item 2: " << table->item(0, 2)->text().toDouble() << std::endl;
    }
    void on_table_itemChanged(QTableWidgetItem* item)
    {
        data[item->column()] = item->text().toDouble();
    }
};

main.cpp:

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char* argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

【问题讨论】:

  • 您可以使用itemDoubleClicked 解决问题吗?每次双击单元格时保存单元格的值,而不是检查是否实际修改了数据。当然在检查重复项时
  • @MarKS 会在用户更改数据之前发生。此外,除非我做错了什么,否则我似乎无法在用户输入的数据仍在编辑时读取它(单元格的编辑器已打开)。
  • 作为一种解决方法,您可以在 on_actionSave_triggered() 槽中启动一个单发计时器(0 ms 应该足够了,槽应该排队),并将数据保存在计时器槽中
  • 一种选择是创建自己的项目委托类,并在函数createEditor()中使用编辑器的textChanged()信号
  • @NgocMinhNguyen 这似乎可行,但让我想知道其他人是否遇到同样的问题,或者我从根本上使用了错误的方法。如果您将其写为包含createEditor 代码的答案,我会接受。

标签: c++ qt qt5 qtablewidget


【解决方案1】:

在您的情况下,当触发保存操作时,您可以首先将焦点设置到 QTableWidget 对象。编辑器将失去焦点并提交数据。我认为这比我在评论中建议的方法更好(因为它假定编辑器是QLineEdit 对象并使用dynamic_cast)。

#include "mainwindow.h"

#include <QDebug>
#include <QDialog>
#include <QMenuBar>
#include <QPushButton>
#include <QTableWidget>
#include <QVBoxLayout>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    QTableWidget *table_widget = new QTableWidget{this};
    table_widget->setRowCount(1);
    table_widget->setColumnCount(1);
    table_widget->setItem(0, 0, new QTableWidgetItem(QString{"Some text"}));
    connect(table_widget, &QTableWidget::itemChanged, [](){qDebug() << "Item changed";});

    QDialog *dialog = new QDialog{this};
    connect(dialog, &QDialog::accepted, [](){qDebug() << "Dialog accepted";});
    connect(dialog, &QDialog::rejected, [](){qDebug() << "Dialog rejected";});

    QMenuBar *menu_bar = new QMenuBar(this);
    QMenu *file_menu = new QMenu("File", menu_bar);
    QAction *save_action = new QAction("Save", this);
    file_menu->addAction(save_action);
    menu_bar->addMenu(file_menu);
    connect(save_action, &QAction::triggered, table_widget, QOverload<>::of(&QWidget::setFocus));
    connect(save_action, &QAction::triggered, [=](){qDebug() << "Save triggered";});

    QPushButton *save_button = new QPushButton{"Save", this};
    connect(save_button, &QPushButton::clicked, [](){qDebug() << "Save clicked";});
    QVBoxLayout *layout = new QVBoxLayout{dialog};
    layout->addWidget(menu_bar);
    layout->addWidget(table_widget);
    layout->addWidget(save_button);

    QPushButton *open_button = new QPushButton{"Open dialog", this};
    connect(open_button, &QPushButton::clicked, dialog, &QDialog::show);
    this->setCentralWidget(open_button);
}

MainWindow::~MainWindow()
{
}

我还注意到,如果您使用按钮保存数据,焦点会自动更改为按钮,从而产生相同的行为。我想用QToolBarQToolButton 代替QMenuBar 也可以。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-31
    • 1970-01-01
    • 2018-05-14
    • 1970-01-01
    • 1970-01-01
    • 2018-11-01
    • 1970-01-01
    相关资源
    最近更新 更多