无法处理QClipboard :: dataChanged()信号

时间:2014-06-29 16:36:07

标签: c++ qt

我正在尝试创建一个监视系统剪贴板更改的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广告位内调用时)。

我不知所措。请帮忙。

2 个答案:

答案 0 :(得分:2)

如果你在dataChanged()中搞乱setText(),似乎Qt会删除剪贴板数据。如果您在应用程序内部进行复制,它不会删除剪贴板。您可以使用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;
}

答案 1 :(得分:2)

我成功地使用了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);
}