处理QClipboard:: datachchanged()信号时出现问题

Trouble handling QClipboard::dataChanged() signal

本文关键字:信号 问题 QClipboard datachchanged 处理      更新时间:2023-10-16

我正在尝试制作一个Qt应用程序,监控系统剪贴板的变化。每当用户将一些文本复制到剪贴板(在应用程序之外)时,我的应用程序应该以某种方式更改该文本并将更改后的文本复制到剪贴板。

问题是剪贴板只会变成空的!

下面是我的代码:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(QApplication::clipboard(), SIGNAL(dataChanged()), 
            this, SLOT(processClipboardChange()));
}
void MainWindow::processClipboardChange()
{
    qDebug() << "dataChanged() signal emitted.";
    QClipboard * clipboard = QApplication::clipboard();
    static bool dontProcessSignal = false;
    if (!dontProcessSignal) {
        QString text = clipboard->text();
        text = "CLIPBOARD CONTENTS CHANGED."; // example.
        qDebug() << "Setting clipboard contents...";
        dontProcessSignal = true;
        clipboard->setText(text); // will trigger another dataChanged() signal.
        dontProcessSignal = false;
        qDebug() << "Copied " << clipboard->text() << " to clipboard.";
    }
    else {
        qDebug() << "Did not process dataChanged() signal.";
        return;
    }
}

现在,当我运行这个程序,并通过用Ctrl+C复制一些文本来改变剪贴板的内容时,我得到了这样的输出:

dataChanged() signal emitted.
Setting clipboard contents...
dataChanged() signal emitted.
Did not process dataChanged() signal.
Copied  "CLIPBOARD CONTENTS CHANGED."  to clipboard.

但是当我键入Ctrl+V粘贴剪贴板内容时,没有粘贴任何内容。由于某种原因,剪贴板的内容似乎被设置为一个空字符串。

QClipboard::setText()在其他代码中工作得很好(例如在QPushButton::clicked插槽中调用时)。

我很茫然。请帮助。

我成功地使用了fxam上面描述的QTimer方法来解决这个问题。

这是我所有的代码供将来的读者使用:

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = 0);
private slots:
    void clipboardChanged();
    void setClipboard();
private:
    QString clipboardText;
private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

mainwindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QClipboard>
#include <QTimer>
#include <QRegularExpression>
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(QApplication::clipboard(), SIGNAL(dataChanged()),
            this, SLOT(clipboardChanged()));
}
void MainWindow::clipboardChanged()
{
    // we need to ignore every other dataChanged() signal because
    // those signals are triggered by us calling QClipboard::setText()
    static bool ignoreSignal = false;
    if (ignoreSignal == false) {
        clipboardText = QApplication::clipboard()->text();
        // will trigger another dataChanged() signal
        // (after our method exits)
        QTimer::singleShot(50, this, SLOT(setClipboard()));
        // ignore the next dataChanged() signal
        ignoreSignal = true;
    }
    else {
        // We're ignoring this signal. Don't ignore the next signal.
        ignoreSignal = false;
    }
}
void MainWindow::setClipboard()
{
    static QRegularExpression regex("x+"); // example...
    static QString replacement("a");
    QString newClipboardText = clipboardText.replace(regex, replacement);
    QApplication::clipboard()->setText(newClipboardText);
}

似乎Qt删除剪贴板数据,如果你乱搞setText()内的datachchanged()。如果在应用程序内部进行复制,它不会删除剪贴板。您可以使用QTimer或invokeMethod与QueuedConnection来解决这个问题:

void MainWindow::processClipboardChange()
{
  if (!dontProcessSignal) {
    // Solution 1, unable to pass argument
    QTimer::singleShot(1, this, SLOT(setClipboard1()));
    // Solution 2, able to pass argument
    QString newText = "CLIPBOARD CONTENTS CHANGED.";
    QMetaObject::invokeMethod(this, "setClipboard2", Qt::QueuedConnection, Q_ARG(QString, newText)); 
  }
}
void MainWindow::setClipboard1() {
  dontProcessSignal = true;
  QClipboard * clipboard = QApplication::clipboard();
  clipboard->setText("CLIPBOARD CONTENTS CHANGED.");
  dontProcessSignal = false;
}
void MainWindow::setClipboard2(QString s) {
  dontProcessSignal = true;
  QClipboard * clipboard = QApplication::clipboard();
  clipboard->setText(s);
  dontProcessSignal = false;
}