研究開発日誌

CG研究・開発のちょっとしたメモ書き

FileMonitor

2014-12-24 Qt Utility

Qtで開発したシンプルなファイル監視クラス.

独自にGLSLシェーダーを使うプログラムを書いていた際,シェーダーのファイルを書き換えた時に変更結果をインタラクティブに確認したくなりました.UIとしてリロードボタンを作るという手法も考えられますが,ここではシェーダーのソースコードが変更されたかどうかを監視して,変更された場合にGLSLシェーダーのプログラムをコンパイルし直すという自動的な手法を採用します.

それを実現するためにシンプルなファイル監視クラスを作りました.元々の目的であるGLSLシェーダーとは完全に処理を切り離してありますので色々な用途に使えると思います.

class FileMonitor : public QObject
{
    Q_OBJECT
public :
    //! Constructor.
    FileMonitor(const QString& filePath, int timerMSec = 24);

public slots:
    void start(int msec);
    void start();
    void stop();

signals:
    void fileModified();

private slots:
    void monitor();

private:
    QFileInfo fileInfo();

private:
    QString     _filePath;
    QDateTime   _modifiedTime;
    QTimer*     _timer;
    int         _timerMSec;

};

実装はいたってシンプルです.timerMSecで決められた時間毎にファイルの更新を確認し,もしファイルスタンプが更新されていれば,fileModifiedのシグナルを通知します.fileModifiedのシグナルにファイルが更新された時に呼びたいスロット関数を指定すれば,指定ファイルが更新される度にスロット関数が呼び出されるようになります.

以下,実装の関連部分を抜粋します.

FileMonitor::FileMonitor(const QString& filePath, int timerMSec)
    : _filePath(filePath), _timerMSec(timerMSec)
{
    if ( fileInfo().exists() )
    {
        _modifiedTime = fileInfo().lastModified();
    }

    _timer = new QTimer(this);
    connect(_timer,SIGNAL(timeout()), this, SLOT(monitor()));
}

void FileMonitor::start(int msec)
{
    _timerMSec = msec;
    start();
}

void FileMonitor::start()
{
    _timer->start(_timerMSec);
}

void FileMonitor::stop()
{
    _timer->stop();
}

void FileMonitor::monitor()
{
    if ( !fileInfo().exists() ) return;

    QDateTime modifiedTime = fileInfo().lastModified();

    if ( modifiedTime > _modifiedTime)
    {
        DebugLogger::debug( "FileMonitor", "filePath", _filePath );
        DebugLogger::debug( "FileMonitor", "  previous", _modifiedTime.toString() );
        DebugLogger::debug( "FileMonitor", "  current", modifiedTime.toString() );

        emit fileModified();
        _modifiedTime = modifiedTime;
    }
}

QFileInfo FileMonitor::fileInfo()
{
    return QFileInfo( _filePath);
}

QFileInfoを利用することで,lastModified関数でQDateTime型のタイムスタンプを簡単に取得できます.QDateTimeには>比較演算子が予め用意されているので,modifiedTime > _modifiedTimeで修正されたかどうかを比較することができます.もし,ファイルが更新されていた場合は,fileModifiedのシグナルを発信する流れです.

以下は個人的な用途ですが,GLSLシェーダーの自動更新を行う例です.

    _vertexShaderMonitor = new FileMonitor(vertexShaderFilePath);
    QObject::connect(_vertexShaderMonitor, &FileMonitor::fileModified, this, &GLSLShaderProgram::init );

    _fragmentShaderMonitor = new FileMonitor(fragmentShaderFilePath);
    QObject::connect(_fragmentShaderMonitor, &FileMonitor::fileModified, this, &GLSLShaderProgram::init );

vertex shaderのFileMonitor,fragment shader (pixel shader)のFileMonitorのそれぞれおのfileModifiedシグナルにGLSLShaderProgramのinitスロットをつなぎ,シェーダープログラムの更新を自動化しています.