nativeEventFilter ってどうやってハンドルするの?

Windowsで、ウィンドウメッセージ (WM_KEYDOWN とか) みたいなネイティブメッセージをハンドルしたい時、どうするのか調べてみました。nativeEventFilter( ... ) を TMainWindow や QApplication でオーバーライドしてもメッセージが飛んでこなかったので挫折しそうになりましたが、こちらを参考にしたら上手く行きました。

http://www.qtcentre.org/threads/56438-help-with-QAbstractNativeEventFilter

よく理解してなくて申し訳ないですが、念のためコードをメモっときます。

mainwindow.h

#include <QAbstractNativeEventFilter>

class usbworker : public QAbstractNativeEventFilter
{
public:
    usbworker();
    virtual bool nativeEventFilter( const QByteArray &eventType, void *message, long *result );
};

MainWindow に usbworker usb; プロパティを追加

mainwindow.cpp

usbworker::usbworker()
{
}

bool usbworker::nativeEventFilter( const QByteArray &eventType, void *message, long *result )
Q_DECL_OVERRIDE
{
    qDebug("i came here");

    if (eventType == "windows_generic_MSG")
    {
      MSG *msg = static_cast<MSG *>(message);
      if (msg->message == WM_KEYDOWN)
      {
        // キーボード押したら飛んでくる
        return false;
      }
    }

    return false;
}

main.cpp

  QApplication a(argc, argv);
  MainWindow w;
  a.installNativeEventFilter( &w.usb );
  w.show();

Q_DECL_OVERRIDE って初めて知りましたが、Javaでいう@Override アノテーションでしょうか?

Qt5.3.0 (Windows) の TabletEvent がおかしい

Qtではペンタブレットの使用時、

  • QEvent::TabletEnterProximity (ペンを認識した)
  • QEvent::TabletLeaveProximity (ペンを認識できなくなった)

というイベントが発生しますが、Qt5.3.0 (Windows) だと、QEvent::TabletLeaveProximity が発生しません。QEvent::TabletEnterProximity は発生してます。これは致命的です。Macでは問題なさそうな気がします(追っかけてはいませんが)。どうしよう…。

補足

Qt5.2.1でもダメでした…。

補足2

Qt5.3.0 (Mac) だと、QEvent::TabletLeaveProximity は来ました。

Qt5.3.0 (Mac版) で、単体キーのショートカットを受け付けない

FireAlpacaでは、前景色・背景色の入れ替えを、Xキーに割り当てています。

ui->action_Hoge->setShortcut( tr("X") );
ui->action_Hoge->setShortcut( QKeySequence( Qt::Key_X ) ); // これもダメ

Qt4.7までは、Windows版もMac版も上手く動作していました。
が、Qt5.3.0 (Qt5.2.1でも) だと、Mac版でイベントを受け付けなっています (Windows版はOK)。
画面上のウィジェットのコントロール (RGBの数値のQLineEditとか) が悪さしてるのかも…?

ui->action_Hoge->setShortcuts( QKeySequence::Undo );
ui->action_Hoge->setShortcut( tr("Ctrl+L") );

などは問題なく受け付けます。

補足

https://bugreports.qt-project.org/browse/QTBUG-38256
バグレポートにも上がってるようです。凄腕Macプログラマのみんな…助けて!

pthreadの代わりに、QtConcurrentRunを使う

pthreadを使う方法が分からなかったんですが、「QtConcurrentを使うと良い」というアドバイスを頂いたので、試してみました(実戦投入はまだです)。Debugモードじゃないと最適化されて一瞬で終わる模様。

#include <QtConcurrentRun>
#include <QElapsedTimer>

struct TParam
{
  int threadIndex;
  int value;
};

// テストしたマシンでは、約1000ms
void thread_func( TParam* param )
{
  int sum = 0;
  for (int i=0; i<10000000; i++)
  {
    for (int j=0; j<45; j++) sum += 1;
  }
  param->value = param->threadIndex;
  param->value += sum;
}

スレッド処理したい関数と、関数に渡したいパラメータを定義します。

    QElapsedTimer et;
    et.start();

    // 最大スレッド数
    const int threadNum = 16;
    TParam param[threadNum];
    for (int i=0; i<threadNum; i++)
    {
      param[i].threadIndex = i;
      param[i].value = 0;
    }

#if 1
    // 1〜4、どれにしても同じ時間かかる (8だと2倍、12だと3倍 (4スレッドマシンなので))
    const int processNum = 4;

    QFuture<void> future[threadNum];
    for (int i=0; i<processNum; i++)
    {
      future[i] = QtConcurrent::run( &thread_func, &param[i] );
    }
    for (int i=0; i<processNum; i++)
    {
      future[i].waitForFinished();
    }
#else
    // 4倍処理に時間がかかる
    const int processNum = 4;

    for (int i=0; i<processNum; i++)
    {
      thread_func( &param[i] );
    }
#endif

    qint64 t = et.elapsed();
    setWindowTitle( QString::number( param[3].value ) );
    setWindowTitle( QString::number(t) );

pthreadで言うところの "pthread_join" が waitForFinished に相当するようです。概ね想像通りの処理時間になっているので、恐らく正しく動作しているのでしょう。

Qt4.8.4 の tabletEvent (Mac版)

Qt4.8.4 (Mac版) では、Qt4.7.4 では飛んでくる QWidget::tabletEvent が飛んで来ません。どうやら放置されてるようですね……困った(涙)。

QWebView が止まらない

    QUrl url( "http://www.google.co.jp" );

    QWebView* view = new QWebView();
    view->setUrl( url );
    delete view;

というコードでQWebViewを作成・削除すると、バックグラウンドでネットワーク関連のレジストリにアクセスをし続けます (Process Monitor - Sysinternals で確認)。
http://technet.microsoft.com/ja-jp/sysinternals/bb842062.aspx (Sysinternals Suite

    QWebView* view = new QWebView();
    //view->setUrl( url );
    delete view;

と、setUrlをコメントアウトすると、バックグランドの動作は行われません。これは、最初のコードだと QWebPage( view->page() ) が生き続けてるという事なんでしょうか? 完全に止める方法が分からず困っています…。

QtSDK 1.2.1 と macdeployqt

Kenjiさんにご指摘いただいた後で申し訳ないのですが、QtSDK 1.2.1 (Qt4.8.1) が公開されていたので、Mac版を試してみました。QtDesktopで問題なくビルドできたのは良かったんですが、やはりビルドして macdeployqt したアプリケーションが Leopard で起動できない事が確認できました。Lionだと動くんですが......。
ちなみに、Leopardでのレポートは、こんな感じです。

Dyld Error Message:
  Symbol not found: _OBJC_CLASS_$_CALayerHost
  Referenced from: /Users/xxxxx/Desktop/XxxxXxxx.app/Contents/MacOS/../Frameworks/QtWebKit.framework/Versions/4/QtWebKit
  Expected in: /System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore

QtSDK 1.1.3 で生成したアプリだと、問題なく起動します。