12

はい、非 GUI スレッドから GUI を使用できないことはわかっています。ただし、QWidget オブジェクトを作成し、それを GUI スレッドに送信してからシグナルを送信できるようにすることは合理的と思われます。ただし、そうしようとすると、ウィジェットを移動できないというエラーが表示されます。ただし、これは機能するようです:

#include <iostream>

#include <QApplication>
#include <QtConcurrentRun>
#include <QDialog>

class BasicViewer : public QDialog
{
Q_OBJECT

public:
  void Function(const float a)
  {
    std::cout << a << std::endl;
  }
};

struct BasicViewerWrapper : public QObject
{
Q_OBJECT
public:
  BasicViewer WrappedBasicViewer;

  void Function(const float a)
  {
    WrappedBasicViewer.Function(a);
  }
};

#include "main.moc" // For CMake's automoc

void Function2()
{
  BasicViewerWrapper basicViewerWrapper;
  basicViewerWrapper.moveToThread(QCoreApplication::instance()->thread());

  basicViewerWrapper.Function(2.0f);
}

void Function1()
{
  Function2();
}

int main(int argc, char *argv[])
{
  QApplication app(argc, argv);

  QtConcurrent::run(Function1);

  std::cout << "End" << std::endl;
  return app.exec();
}

直接作成したい QWidget のインスタンスを格納する QWidget と同じ API を持つラッパー クラスを作成しました。そのラッパーを作成し、それを GUI スレッドに移動してから使用することが許可されています。私の質問は、このラッパーを書かなくてもこれを行う方法はありますか? それは非常に退屈に思えます。コンセプトは機能するので、直接実行できない理由がわかりません。何かご意見は?

- - - - - - 編集 - - - - - - - -

最初の例は悪い例でした。GUI 要素に対して何もしようとしなかったからです。この例では、実際に「別のスレッドにある親の子を作成できません」が生成されます。

#include <iostream>

#include <QApplication>
#include <QtConcurrentRun>
#include <QMessageBox>

class BasicViewer : public QMessageBox
{
Q_OBJECT

public:

};

struct BasicViewerWrapper : public QObject
{
Q_OBJECT
public:
  BasicViewer WrappedBasicViewer;

  void exec()
  {
    WrappedBasicViewer.exec();
  }
};

#include "main.moc" // For CMake's automoc

void Function2()
{
  BasicViewerWrapper basicViewerWrapper;
  basicViewerWrapper.moveToThread(QCoreApplication::instance()->thread());
  basicViewerWrapper.exec();
}

void Function1()
{
  Function2();
}

int main(int argc, char *argv[])
{
  QApplication app(argc, argv);

  QtConcurrent::run(Function1);

  return app.exec();
}

----------- 編集 2 ----------------

Wrapper のスレッドが移動された後にメンバー オブジェクトが作成されるため、これでうまくいくと思いました。

#include <iostream>

#include <QApplication>
#include <QtConcurrentRun>
#include <QMessageBox>

class BasicViewer : public QMessageBox
{
Q_OBJECT

public:

};

struct BasicViewerWrapper : public QObject
{
Q_OBJECT
public:
  BasicViewer* WrappedBasicViewer;

  void exec()
  {
    WrappedBasicViewer->exec();
  }

  void create()
  {
    WrappedBasicViewer = new BasicViewer;
  }
};

#include "main.moc" // For CMake's automoc

void Function2()
{
  BasicViewerWrapper basicViewerWrapper;
  basicViewerWrapper.moveToThread(QCoreApplication::instance()->thread());
  basicViewerWrapper.create();
  basicViewerWrapper.exec();
}

void Function1()
{
  Function2();
}

int main(int argc, char *argv[])
{
  QApplication app(argc, argv);

  QtConcurrent::run(Function1);

  return app.exec();
}

残念ながら、そうではありません。誰でも理由を説明できますか?

--------------- 編集 3 --------------------

なぜこれが機能するのかわかりませんか?信号を使用して GUI コンポーネントをトリガーしますが、GUI オブジェクト (QDialog) は非 GUI スレッドでまだ作成されていませんか?

#include <iostream>

#include <QApplication>
#include <QtConcurrentRun>
#include <QMessageBox>

class DialogHandler : public QObject
{
Q_OBJECT

signals:
  void MySignal(int* returnValue);

public:
  DialogHandler()
  {
    connect( this, SIGNAL( MySignal(int*) ), this, SLOT(MySlot(int*)), Qt::BlockingQueuedConnection );
  }

  void EmitSignal(int* returnValue)
  {
    emit MySignal(returnValue);
  }

public slots:
  void MySlot(int* returnValue)
  {
    std::cout << "input: " << *returnValue << std::endl;
    QMessageBox* dialog = new QMessageBox;
    dialog->addButton(QMessageBox::Yes);
    dialog->addButton(QMessageBox::No);
    dialog->setText("Test Text");
    dialog->exec();
    int result = dialog->result();
    if(result == QMessageBox::Yes)
    {
      *returnValue = 1;
    }
    else
    {
      *returnValue = 0;
    }

    delete dialog;
  }
};

#include "main.moc" // For CMake's automoc

void MyFunction()
{
  DialogHandler* dialogHandler = new DialogHandler;
  dialogHandler->moveToThread(QCoreApplication::instance()->thread());

  int returnValue = -1;
  dialogHandler->EmitSignal(&returnValue);

  std::cout << "returnValue: " << returnValue << std::endl;
}

int main(int argc, char *argv[])
{
  QApplication app(argc, argv);

  QtConcurrent::run(MyFunction);

  std::cout << "End" << std::endl;
  return app.exec();
}
4

1 に答える 1

6

Qt は、GUI スレッド内でウィジェットを作成することを主張しています。ウィジェットを別のスレッドに移動することを無効にして、ウィジェットが GUI スレッドの外に存在するのを防ぎます。上記の例は、実際には別のスレッドに移動しません。別のスレッドにBasicViewer移動するだけです。andBasicViewerWrapper内の含まれているスレッドへのポインターを出力すると、これを確認できます。BasicViewerWrapper::FunctionBasicViewer::Function

std::cout << std::hex << thread() << std::endl;

GUI スレッドの外部からウィジェットの作成を本当にトリガーしたい場合は、他のスレッドが GUI スレッドに通知して、必要なウィジェットを作成することをお勧めします。ウィジェットを作成する GUI スレッド内のスロットに接続する非 GUI スレッドから信号を送信するか、GUI スレッド内で関数を呼び出して を使用してウィジェットを作成することができますQMetaObject::invokeMethod

編集

残念ながら、別のスレッドでメソッドを呼び出す方法はありませQMetaObject::invokeMethodQObject。これまで、メソッド呼び出しを別のクラスまたは関数に配置することで可読性に取り組んできましたが、完全ではありません。

QObject::moveToThread同期していないため、3番目の例は機能していません。オブジェクトが実際に宛先スレッドに移動される前に、制御が宛先スレッドのイベント ループに戻る必要があります。そのため、おそらく sleep ステートメントとQCoreApplication::processEventsafter callingへの呼び出しを組み合わせる必要がありますmoveToThread。これらの呼び出しの後、おそらくbasicViewerWrapper::create()およびbasicViewerWrapper::exec()経由で呼び出す必要がありますQMetaObject::invokeMethod

于 2012-11-01T20:22:31.867 に答える