Qt プロセスとスレッド: プロセスの実行とプロセス間通信

Qt は、プロセスをサポートするためにプラットフォームに依存しない QProcess クラスを提供します。このセクションでは、Qt アプリケーションで外部プログラム プロセスを開始する方法と、一般的に使用されるいくつかのプロセス間通信方法について説明します。

アプリケーションを設計するとき、その関数が現在設計されているアプリケーションとの関連性が低いため、またはその関数が既存のプログラムですでに十分に実装できるため、関連性の低い関数をプログラムに組み込みたくない場合があります。このとき、現在のアプリケーションで外部プログラムを呼び出して、そのプロセスを使用する関数を実装することができます。Qt アプリケーションは外部アプリケーションを簡単に起動でき、Qt はさまざまなプロセス間通信方法も提供します。

1. プロセスを実行する

Qt の QProcess クラスを使用して、外部プログラムを起動し、外部プログラムと通信することができます。Qt コードでプロセスを開始する方法を見てみましょう。

プロセスを実行する メモ帳を開く

最初に QtGui アプリケーションを作成します。プロジェクト名は「myProcess」です。他のオプションはデフォルトのままでかまいません。まず mainwindow.h ファイルを入力してコードを追加し、プライベート オブジェクト定義: QProcess myProcess を追加します。次に、プッシュ ボタン コンポーネントをデザイン モードのインターフェイスにドラッグし、その表示テキストを「プロセスを開始する」に変更します。ボタン上でマウスの右ボタンをクリックし、clicked() 信号に対応するスロットに移動し、次のように変更します。

void MainWindow::on_pushButton_clicked()
{
     myProcess.start("notepad.exe");
}

ここでは、QProcess オブジェクトを使用して、Windows システムでメモ帳プログラム (つまり、notepad.exe プログラム) を実行します。プログラムはシステム ディレクトリにあるため、ここでそのパスを指定する必要はありません。特定のパスを指定するだけで、他のプログラムを実行することもできます。start() を使用してプログラムを起動できることを見てきましたが、プログラムの起動時に起動パラメータを指定する必要がある場合があります。これは、コマンド ラインからプログラムを起動する場合に非常に一般的です。

プログラムを実行すると、インターフェース上のボタンをクリックするとメモ帳プログラムがポップアップ表示されます。

QProcess は、イベント ループの外側で使用できる一連の関数も提供します。これにより、特定のシグナルが発行されるまで呼び出し元のスレッドが一時停止されます。

  • waitForStarted() はプロセスが開始されるまでブロックします。
  • waitForReadyRead() は、現在の読み取りチャネルに読み取り可能なデータが存在するまでブロックします。
  • waitForBytesWritten() は、データのペイロードがプロセスに書き込まれるまでブロックします。
  • waitForFinished() はプロセスが完了するまでブロックします。

この記事のメリット、Qt 開発学習教材パッケージの受け取り料金、技術ビデオ、コンテンツには (C++ 言語基礎、Qt プログラミング入門、QT シグナルとスロットのメカニズム、QT インターフェイス開発イメージ図、QT ネットワーク、QT データベース プログラミング、QT プロジェクト戦闘、QSS、OpenCV、Quick モジュール、インタビューの質問など) ↓↓↓↓↓↓ 下記を参照

2、プロセス間通信

Qt は、Qt アプリケーションでプロセス間通信 IPC (Inter-Process Communication) を実装するためのさまざまなメソッドを提供します。簡単な紹介は次のとおりです。

TCP/IP

クロスプラットフォームの Qt Network モジュールによって提供されるクラスにより、ネットワーク プログラミングがより移植可能で便利になります。特定のアプリケーションレベルのプロトコルを使用した通信のための高レベルのクラス (例: QNetworkAccessManager、QFtp) と、プロトコルを実装するための下位レベルのクラス (例: QTcpSocket、QTcpServer、QSslSocket) を提供します。

共有メモリ

クロスプラットフォームの QSharedMemory 共有メモリ クラスは、オペレーティング システムの共有メモリの実装を提供します。これにより、複数のスレッドとプロセスが共有メモリ セグメントに安全にアクセスできるようになります。さらに、QSystemSemaphore を使用して、システムによって共有されるリソースへのアクセスやプロセス間の通信を制御できます。

Dバス

Qt の D-Bus モジュールは、D-Bus プロトコルを使用して IPC を実装するために利用できる唯一の Unix ライブラリです。Qt のシグナルとスロットのメカニズムを IPC レベルまで拡張し、あるプロセスによって発行されたシグナルを別のプロセスのスロットに接続できるようにします。Qt の D-Bus ドキュメントには、Qt で D-Bus モジュールを使用する方法の詳細な手順が記載されています。

Qプロセス

前述の QProcess クラスを使用します。クロスプラットフォーム クラス QProcess を使用すると、外部プログラムを子プロセスとして起動し、それらと通信することができます。子プロセスの状態を監視および制御するための API を提供します。さらに、QProcess は、QIODevice から継承されたサブプロセスの入出力チャネルを提供します。

セッション管理

Linux/X11 プラットフォームでは、Qt はセッション管理サポートを提供します。セッションを使用すると、シャットダウンが検出されたときなどにイベントをプロセスに伝播できます。プロセスとアプリケーションは、開いているドキュメントの保存など、必要なアクションを実行できます。

3. 共有メモリのプロセス間通信の例

共有メモリの使用例を見てみましょう。これが実装する機能は、まずダイアログ ボックスの共有メモリ セグメントにピクチャを書き込み、次に別のダイアログ ボックスで共有メモリ セグメントからピクチャを読み取ります。

mylPC という名前、Dialog というクラス名で新しい Qt Gui アプリケーションを作成し、基本クラスとして QDialog を選択します。完了したら、デザイン モードに入り、2 つのプッシュ ボタン コンポーネントと 1 つのラベル コンポーネントをインターフェイスに配置します。一方のボタンの表示テキストを「ファイルから画像をロード」に変更し、その objectName プロパティをloadFromFileButtonに変更し、もう一方のボタンの表示テキストを「共有メモリから画像を表示」に変更し、その objectName プロパティをloadFromSharedMemoryButtonに変更します。次に、dialog.h ファイルを入力し、次のように変更します。

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QSharedMemory>

namespace Ui {
    class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = nullptr);
    ~Dialog();

public slots:
    //从文件中加载图片到共享内存
    void loadFromFile();

    //从共享内存中加载图片
    void loadFromMemory();

private slots:
    //从文件中加载图片按钮
    void on_loadFromFileButton_clicked();

    //从共享内存加载并显示图片按钮
    void on_loadFromSharedMemoryButton_clicked();

private:
    //将进程与共享内存段进行分离,如果失败则进行提示
    void detach();

private:
    Ui::Dialog *ui;

    QSharedMemory sharedMemory; //定义一个共享内存对象
};

#endif // DIALOG_H

次に、dialog.cpp ファイルを入力し、次のように変更します。

#include "dialog.h"
#include "ui_dialog.h"

#include <QFileDialog>
#include <QBuffer>
#include <QDebug>

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

    //在使用共享内存以前,需要设置key,系统用它作为底层共享内存段的标识
    sharedMemory.setKey("QSharedMemoryExample");
}

Dialog::~Dialog()
{
    delete ui;
}

//从文件中加载图片到共享内存
void Dialog::loadFromFile()
{
    //判断该进程是否已经连接到共享内存段,连接成功返回true
    if (sharedMemory.isAttached())
        detach();

    ui->label->setText(tr("选择一个图片文件!"));
    //使用文件对话框获得打开图片路径
    QString fileName = QFileDialog::getOpenFileName(nullptr, QString(), QString(),
                                                    tr("Images (*.png *.jpg)"));

    //label标签显示图片
    QImage image;
    if (!image.load(fileName))
    {
        ui->label->setText(tr("选择的文件不是图片,请选择图片文件!"));
        return;
    }
    ui->label->setPixmap(QPixmap::fromImage(image));

    //将图片加载到共享内存
    //使用QBuffer来暂存图片,这样便可以获得图片的大小,还可以获得图片数据的指针
    QBuffer buffer;
    buffer.open(QBuffer::ReadWrite);
    QDataStream out(&buffer);
    out << image;
    int size = static_cast<int>(buffer.size());
    //使用create()函数创建指定大小的共享内存段,该函数还会自动将共享内存段连接到本进程上。
    if (!sharedMemory.create(size))
    {
        ui->label->setText(tr("无法创建共享内存段!"));
        return;
    }

    //在进行共享内存段的操作前,需要先进行加锁
    sharedMemory.lock();
    char *to = static_cast<char*>(sharedMemory.data());
    const char *from = buffer.data().data();
    //使用memcpy()函数将图片数据复制到共享内存
    memcpy(to, from, static_cast<size_t>(qMin(sharedMemory.size(), size)));
    //等操作完成后,再进行解锁。
    sharedMemory.unlock();
}

//从共享内存中加载图片
void Dialog::loadFromMemory()
{
    //使用attache()函数将进程连接到共享内存段
    if (!sharedMemory.attach())
    {
        ui->label->setText(tr("无法连接到共享内存段,\n"
                              "请先加载一张图片!"));
        return;
    }

    QBuffer buffer;
    QDataStream in(&buffer);
    QImage image;

    //使用QBuffer来读取共享内存段中的数据
    sharedMemory.lock();
    buffer.setData((char*)sharedMemory.constData(), sharedMemory.size());
    buffer.open(QBuffer::ReadOnly);
    in >> image;
    sharedMemory.unlock();

    //将进程与共享内存段进行分离,如果失败则进行提示
    sharedMemory.detach();
    ui->label->setPixmap(QPixmap::fromImage(image));
}

//将进程与共享内存段进行分离,如果失败则进行提示
void Dialog::detach()
{
    if (!sharedMemory.detach())
        ui->label->setText(tr("无法从共享内存中分离!"));
}

//从文件中加载图片按钮
void Dialog::on_loadFromFileButton_clicked()
{
    loadFromFile();
}

//从共享内存加载并显示图片按钮
void Dialog::on_loadFromSharedMemoryButton_clicked()
{
    loadFromMemory();
}

実行中のインスタンスで「ファイルからイメージをロード」ボタンをクリックし、イメージを選択します。2 番目に実行中のインスタンスで [共有メモリから画像を表示] ボタンをクリックすると、最初のインスタンスにロードされた画像が表示されます。その効果は次のとおりです。

 

4. プログラムの分析

(1)loadFromFile()

  • ここでは、まず isAttached() 関数を使用してプロセスが共有メモリ セグメントに接続されているかどうかを確認し、接続されている場合は detach() を呼び出して、最初にプロセスを共有メモリ セグメントから分離します。
  • 次に、QFileDialog クラスを使用して画像ファイルを開き、それをラベルに表示します。画像を共有メモリにロードするには、QBuffer を使用して画像を一時的に保存することで、画像のサイズを取得し、画像データのポインタも取得できます。
  • その後、create() 関数を使用して、指定されたサイズの共有メモリ セグメント (サイズの単位はバイト) を作成します。この関数は、共有メモリ セグメントをプロセスに自動的に接続します。
  • 共有メモリセグメントを操作する場合は、lock()でロックし、memcpy()関数でバッファに相当するデータセグメントを共有メモリセグメントにコピーし、操作完了後にunlock()でロックを解除します。このようにして、同時に、1 つのプロセスのみが共有メモリセグメントを操作できるようになります。

(2)loadFromMemory()

  • ここではまず、attach() 関数を使用して、プロセスを共有メモリ セグメントに接続します。
  • 共有メモリセグメントを操作する場合は、lock()を使用して共有メモリセグメントをロックし、QBufferを使用して共有メモリセグメントのデータを読み取り、操作完了後にunlock()を使用してロックを解除します。
  • ここでは共有メモリを使用する必要がないため、detach() 関数を呼び出してプロセスを共有メモリ セグメントから分離します。最後に、ラベルに画像を表示します。

記事はブログガーデン(fengMisaka)から転載しました: Qt プロセスとスレッド 1: プロセスの実行とプロセス間通信

この記事のメリット、Qt 開発学習教材パッケージの受け取り料金、技術ビデオ、コンテンツには (C++ 言語基礎、Qt プログラミング入門、QT シグナルとスロットのメカニズム、QT インターフェイス開発イメージ図、QT ネットワーク、QT データベース プログラミング、QT プロジェクト戦闘、QSS、OpenCV、Quick モジュール、インタビューの質問など) ↓↓↓↓↓↓ 下記を参照

 

おすすめ

転載: blog.csdn.net/QtCompany/article/details/131859748