カテゴリー別アーカイブ: Qt

QOpenGLWidget 入門 (1)

Qt で OpenGL を使う場合、従来は QGLWidget を使っていたが、
Qt 5.4(?) で QOpenGLWidget が導入され、QGLWidget は非推奨となった。
なので、本稿ではその QOpenGLWidget を試してみることにする。

なお、QGLWidget を使う場合のコードは Qt + OpenGL 入門 を参照してほしい。

再実装が必要なメソッドは以下の3つ。これは QGLWidget の場合と同じだ。

  • void initializeGL()
  • void resizeGL(int w, int h)
  • void paintGL()

QGLWIdget との違いは、OpenGL のコマンドを直接実行するのではなく、

QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();

で、OpenGL コマンドを実行するためのオブジェクトを取得し、それに対してコマンドを実行するという点だ。

以下に、最も簡単なサンプルとして、背景を明るい緑にするだけのコードを示しておく。

class MyGLWidget : public QOpenGLWidget
{
public:
  MyGLWidget(QWidget *parent) : QOpenGLWidget(parent) { }
protected:
  void initializeGL()
  {
    // Set up the rendering context, load shaders and other resources, etc.:
    QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
    f->glClearColor(0.75f, 1.0f, 0.75f, 1.0f);  // 背景色指定
  }
  void resizeGL(int w, int h)
  {
  }
  void paintGL()
  {
  }
};

実行結果は以下のとおり。

temp

■ 演習問題:

  1. 上記サンプルが正しく動作することを確認しなさい
  2. 背景色をグレイに変えてみなさい
  3. 背景色を好きな色に変えてみなさい

 

Qt5 のIME(InputMethod)周り

実は「さくさくエディタ」は最初、Qt5 の勉強もかねて Qt5.0 で開発し始めたのだが、IME(InputMethod)周りが期待したようには動作せず(いったいどうい問題があったのかは覚えてない)、情報が少なく対処も難しそうだったので Qt4.x に変更した経緯がある。

が、もうバージョンも 5.5 になってて、さすがにいろいろ治ってるだろうと、Qt 5.5 でIME周りの動作確認をしてみたところ、以下のコードでちゃんと動作した。
めでたし、めでたし。

void MainWindow::inputMethodEvent(QInputMethodEvent * event)
{
	QWidget::inputMethodEvent(event);
	auto pre = event->preeditString();
	auto txt = event->commitString();
	qDebug() << "inputMethodEvent(): pre = " << pre;
	qDebug() << "inputMethodEvent(): txt = " << txt;
}
QVariant MainWindow::inputMethodQuery( Qt::InputMethodQuery query ) const
{
	if( query == Qt::ImCursorRectangle ) {
		qDebug() << "query == Qt::ImCursorRectangle";
		return QVariant(QRect(m_px, m_py, 1, 1));
	}
	return QMainWindow::inputMethodQuery(query);
}

まとめ:

  • 1文字入力は、keyPressEvent ( QKeyEvent * event ); を再実装する。
  • IME イベントを処理したい場合は setAttribute(Qt::WA_InputMethodEnabled); を実行しておく
  • IME イベント処理は inputMethodEvent(QInputMethodEvent * event); を再実装する。
    • preeditString() で現候補文字を取得
    • commitString() で確定文字を取得
  • IME 候補ウィンド位置は inputMethodQuery( Qt::InputMethodQuery query ) を再実装する
    • query が Qt::ImCursorRectangle の時に、ウィンドウ位置を返す
    • 矩形サイズは無視されるようだが、0, 0 を指定すると左上に表示されてしまう
    • 実際に表示されるのは文字高さ?分だけ下にずれるようだ

QScrollArea 派生クラスを直接描画する方法

スクロール可能なウィジェットを実装するには QScrollArea を使用するとよい。
通常は、中身のウィジェットを生成し、setWidget() すれば、自動的にスクロールしてくれるようになる。

ただし、ウィジェットが ScrollArea に比べてかなり大きいと、クリッピングを自前で行わないと描画処理に不必要に時間を食う場合がある。だが、中身のウィジェットが親が持つスクロール情報を参照するのはあまり好ましくない。
で、ひとつの解決方法として、QScrollArea の派生クラスを作り、その中で直接描画する、という方法がある。

実はそのようなコードを以前にも書いたことがあるのだが、いざ書こうとすると、その具体的な方法をすっかり忘れていたし、ぐぐってもなかなか目的とする情報に行き着かなかったので、ここにメモしておく。

描画

描画は void paintEvent(QPaintEvent *); で行う。
ポイントは、自分自身に対して描画するのではなく、viewport() に対して描画するという点だ。

void ScrollWidget::paintEvent(QPaintEvent *)
{
  QPainter painter(viewport());
  painter に対して描画;
}

 スクロールバー情報の設定

setWidget() せず、自前で描画する場合は、スクロール範囲などを自前で設定しなくてはいけない。
そのための関数を用意しておき、resize() イベントが発生した場合は、それをコールする。

void ScrollWidget::resizeEvent(QResizeEvent *event)
{
  QScrollArea::resizeEvent(event);
  updateScrollInfo();
}
void ScrollWidget::updateScrollInfo()
{
  QScrollBar *hScrollBar = horizontalScrollBar();
  hScrollBar->setMinimum(0);
  hScrollBar->setMaximum(qMax(0, WIDTH - vprct.width()));
  hScrollBar->setPageStep(vprct.width());
  hScrollBar->setSingleStep(10);
  .....
}

 スクロール位置を考慮した描画

スクロール位置を考慮した描画を行うには、描画ハンドラ内で、horizontalScrollBar()->value(); にてスクロール位置を取得し、それを考慮した描画を行うとよい。

具体的には座標を上記値でそのつど補正するか、以下のように座標原点を移動するようにする。

void ScrollWidget::paintEvent(QPaintEvent *)
{
  int hpos = horizontalScrollBar()->value();
  QPainter painter(viewport());
  QTransform tf;
  tf.translate(-hpos, 0);		//	原点移動
  painter.setTransform(tf);
  .....
}