スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

さらにQPainterについて

前項QPainter::begin: Widget painting can only begin as a result of a paintEventで、ウィジェットにQPainterで描画を行うにはそのウィジェットのpaintEvent()内で行わなければならないと書いたが、描画デバイスがウィジェットでない場合はこの限りでない。

O'Reillyの「入門Qt4プログラミング」第5章では、あらかじめPixmapを生成しておき、このPixmapに対してQPainterで描画を行っている。この場合は対象がウィジェットではないので、paintEvent()内に限らず任意のタイミングで描画操作を行ってかまわない。そして、最終的な描画対象となるウィジェットのpaintEvent()内で、QPainterのdrawPixmap()メソッドを使用して既に生成済みのPixmapをウィジェットにコピーすればいいわけである。

この方法を応用すれば実質的な描画のタイミングをpaintEvent()と切り離せるので、覚えておく価値はあるだろう。
スポンサーサイト

QPainter::begin: Widget painting can only begin as a result of a paintEvent

標題のエラーメッセージ、

QPainter::begin: Widget painting can only begin as a result of a paintEvent

であるが、アプリケーション内で子ウィジェットを生成してそれにQPainterで描画を行おうとすると遭遇する。たとえばこんな具合のコードで発生する。

QWidget child = new QWidget;
QPainter painter(child);
QPen pen(Qt::white, 2);
painter.setPen(pen);
painter.drawLine(0.0, 0.0, 100.0, 0.0);

調べてみたところ、あまりドキュメントや手持ちの本に明確に書かれていないのだが、どうやらQPainterを使ってウィジェットに描画を行う場合、かならずそのウィジェットのpaintEvent、あるいはそこから呼び出される関数内で行う必要がある。つまり、QPainterによる描画が行われるウィジェットは、必ずpaintEventをオーバーライドする必要がある。さらにいうと、あるウィジェットなりアプリケーションなりのpaintEvent内で、他のウィジェットに描画しようとしてもだめなのである。必ず、そのウィジェット自身のpaintEvent内で描画が行わなければならない。別の言い方をすれば、あるウィジェットに対してQPainterを使って描画を行いたいならば、既製ウィジェットを継承して独自のクラスとして定義する必要があるということだ。

描きたいものがテキストやイメージデータなら、場合によっては、継承によって扱うクラスを増やす代わりに、そのテキストなりイメージなりを貼り付けたQLabelを生成し、それを描画したいウィジェットの任意の位置に子ウィジェットとして載せるという方法でもいいかもしれない。

続きを読む

Qtウィジエットの背景色の変更とレイアウトへの動的追加

いやはや、シロウトはこんな簡単なところでもはまれる。ハズいが忘れるといやなのでやっぱり書いておく。このサンプルは以下の二つの点を実現するものである。

1. QWidgetの背景色を変更する。
2. 順次QWidgetを生成しながら、レイアウトに追加して表示を更新していく。


#include "widgettest.h"
#include "ui_widgettest.h"
#include

WidgetTest::WidgetTest(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::WidgetTest)
{
ui->setupUi(this);
QLayout *layout = new QHBoxLayout;
ui->panelBase->setLayout(layout);

QWidget *panels[6];
QColor colors[6] = {Qt::red, Qt::blue, Qt::green, Qt::yellow, Qt::magenta, Qt::cyan};

for(int i = 0; i < 6; i++){
panels[i] = new QWidget(ui->panelBase);
QPalette palette = panels[i]->palette();
palette.setColor(QPalette::Window, colors[i]);
panels[i]->setPalette(palette);
panels[i]->setAutoFillBackground(true);
panels[i]->setMinimumSize(100, 100);
layout->addWidget(panels[i]);
panels[i]->show();
repaint();
sleep(1);
}
}


QtCreatorで作成したフォームはQtCreatorで作成したフォーム

のように極めて適当なもの。QMainWindowの上に、動的に生成して配置するウィジェットの親になるQWidget(panelBase)を一つ置いただけである。panelBaseには子ウィジェットが最初は何もないので、Designer画面ではpanelBase上にレイアウトを指定することができない。

そこで、ソース中では水平ボックスレイアウトを新規に作成して、これをpanelBaseに設定している。もしあらかじめDesigner画面の方で仮に子ウィジェットを一つ以上配置して、レイアウトを指定済みだと、後からレイアウトを追加することはできない。

各ウィジェットはデフォルトで親から引き継いだカラーパレット(QPalette)を持っており、パレット内では抽象的な役割(ColorRole)と、その役割に対する実際の色(QColor)のペアがいくつか登録されていて、実際に使用する場合は役割の方を指定して使うことになる。

ColorRoleの各enumの意味
ColorRoleのenum値説明
QPalette::Window一般的な背景色
QPalette::BackgroundObsolete。代わりにWindowを使用すべき。
QPalette::WindowText一般的なフォアグラウンドカラー
QPalette::ForegroundObsolete。代わりWindowTextを使うべき。
QPalette::Base主にテキスト入力ウィジェットの背景色として使われるが、コンボボックスの背景やツールバーハンドルの背景色としても使われる。通常は白か他の明るい色が使われる。
QPalette::AlternateBase表計算などで見られる、一行おきに背景色を変える場合に使われる第2の背景色。
QPalette::ToolTipBaseツールチップや、"What is this?"の表示の背景色に使われる。ツールチップの場合は、パレット自体が、inactive用のものが用いられる。
QPalette::ToolTipTextツールチップや、"What is this?"の表示の文字色に使われる。
以下は、GUIに3D効果を加えるため等に用いられる色。
QPalette::Buttonボタン用の背景色。スタイルによってはWindowとは異なる。
QPalette::ButtonTextボタン用の描画色。
QPalette::BrightTextボタンを押している間などに用いられる、WindowTextとははっきりと違い、かつDark等とははっきりコントラストの差がつく色。
QPalette::LightButtonよりは明るい色。
QPalette::MidlightButtonとLightの中間の明るさ。
QPalette::DarkButtonよりは暗い色。
QPalette::MidButtonとDarkの中間。
QPalette::Shadow非常に暗い色。デフォルトでは黒。
以下はテキスト表示に使用される色。
QPalette::Highlight現在選択されている項目を表す色。デフォルトではダークブルー。
QPalette::HighlightedTextハイライト表示されているテキストの色。HighLightとはっきりコントラストのある色。デフォルトでは白。
QPalette::Linkまだたどっていないハイパーリンク。デフォルトでは青。
QPalette::LinkVisitedすでにたどったハイパーリンク。デフォルトではマゼンタ。

 
 ということで、QWidgetの場合は、背景色を変えるということは、パレット中のWindowロールの色を変更すればよいことになる。このサンプルでは、まず各ウィジェットから現在のパレットを取得し、そのWindowロールの色だけをcolors[i]に変更して、そのパレットをウィジェットに設定し直している。

あとは、実は動的にウィジェットをレイアウトに追加といっても、最初にまとめて生成するのとほとんど違いはない。単に、一個ウィジェットを新しく追加するたびに、repaint()で表示を更新している。update()だと、描画リクエストが次回のイベント処理までバッファされてしまうので数個分まとめて表示されることになる。

表示されるウィンドウ

Qt Creatorでのカスタムプラグインの追加

自分で作ったウィジェットをQt Creator内のDesigner画面でウィジェット一覧に表示させて、GUIでアプリケーションに組み込めるようにする方法はCreating Custom Widgets for Qt Designerに述べられているし、またO'Reillyの「入門Qt4プログラミング」の5章にも書かれている。これらの方法は、実際には、Qt Creatorでプロジェクトの新規作成ウィザードから、「他のプロジェクト」の「Qt カスタム デザイナ ウィジェット」を選択して作成されるスケルトンプロジェクトと同じことをしている。だからこのウィザードを使用してカスタムウィジェットを作ってもいいのだが、最初からこのウィザードを使ってプロジェクトを作成すると、アプリケーション作成の場合と違って若干不便な点がある。それは

1. フォームが作成されないので、UIをGUIで作成できない。
2. デフォルトのアプリケーション(main.cpp)が作成されないので、開発中即座に実行してテストできない。

そこでとりあえず、まずカスタムウィジェット自体は、先に別途「Qt GUIアプリケーション」として作成しておき、ウィジェットが完成した時点で、別に「Qt カスタム デザイナ ウィジェット」としてスケルトンプロジェクトを作成する(名前は両者一致させておく。フォルダは別のところにまずは作成)。「Qt カスタム デザイナ ウィジェット」として作成されたプロジェクトは、フォルダ構成を見ると、ウィジェット本体のソースを格納するフォルダと、プラグインとしてのインターフェースを提供するためのフォルダに分かれている。ウィジェット本体の方のソースフォルダに、先に別途作成しておいた完成済みのウィジェットのソースをコピーしてくればとりえあずは良いわけである。なんかいまいちスマートでない気がするが、とりあえずよしとする。

コピーしてくるのは、該当ウィジェットのクラス名をMyWidgetとすると

MyWidgetフォルダの方に
mywidget.cpp
mywidget.h

MyWidget-build-desktopフォルダの方に
ui_mywidget.h

だけでよい。mywidget.uiファイルも要るかと思ったのだが、このファイルに含まれる内容はビルド時点でXMLからui_mywidget.hにすべて移されているので必要ないようだ。

これで後は問題なくプロジェクトをビルドできるはずである。ただこのカスタムウィジェットを実際にQtCreator/Desginerに認識させて既製ウィジェットと同じように使用するには、2点やっておかなければならないことがある。

1. ビルドディレクトリに作成されたlibmywidgetplugin.soをシステムのQt Desginer用プラグインフォルダ(Fedora 64bitなら /usr/lib64/qt4/plugins/desginer)にインストール。これはroot権限で、ビルドディレクトリ(Makefileとかが生成されているMyWidget-build-desktopフォルダ)でmake installを行えば良い。

2. 上記を行うということは、システムに新しいライブラリがインストールされるということだから…/etc/ld.so.conf.d/qt-x86.64.confを編集、/usr/lib64/qt4/plugins/desginerを追加した上で、

ldconfig

をroot権限で実行して変更を反映させておく。これをやっておかないとこのウィジェットを利用したアプリケーションが実行時にプラグインをロードできない。

本当は、何でもかんでも自作ウィジェットをシステムフォルダにインストールするのも気が進まないので、ホームディレクトリ以下に個人のプラグインフォルダを作成して、これをQt Creatorに認識させたいところなのだが、できるかどうかまだ不明。ツール→オプションの「デザイナー」の項にも追加でプラグインフォルダを指定する項目はなさそうだし、Qt Creator自体をソースからビルドしないとダメな気もしなくもない。

Qt Creatorのプロジェクトに外部ライブラリを追加

手動でプロジェクトファイルやMakefileを編集しないで外部プロジェクトを追加します。

Qt Creatorの編集モードで、画面左側のプロジェクトペインを見ると、ツリーの一番上は、プロジェクトのフォルダのすぐ下に、プロジェクトファイル(*.pro)が表示されていると思います。それをダブルクリックして、編集画面で開きます。

そして、この編集画面で右クリックしてコンテキストメニューを出すと、「ライブラリを追加…」という項目があるので、これを使用します。

「ライブラリを追加」ダイアログ


たとえば私の場合、Fedora 14にNVIDIAのCUDA Toolkit 3.2をインストールして、OpenCLを使用しているのでヘッダの位置は/usr/local/cuda/include、ライブラリファイルは/usr/lib64/libOpenCL.soになります。ホームディレクトリ上のソースツリーとは別の場所にあるので、2番目の「外部ライブラリ」を選択します。

なお、スクリーンショットを見ると分かるように、一番目の「システムライブラリ」の説明がかなり意味不明になっていますが、これは誤訳で、「ライブラリのパスもインクルードパスも.proファイルには追加されません」の意です。

当然、ライブラリファイルとヘッダファイルの位置は把握している必要があります。findコマンドとかrpm -qlとかで確認しておいてください。

「ライブラリを追加」ダイアログその2


これで、.proファイルに対応するエントリが正しく追加されます。
今の例だと、このようになりました。

win32:CONFIG(release, debug|release): LIBS += -L$$PWD/../../../../../usr/lib64/release/ -lOpenCL
else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/../../../../../usr/lib64/debug/ -lOpenCL
else:symbian: LIBS += -lOpenCL
else:unix: LIBS += -L$$PWD/../../../../../usr/lib64/ -lOpenCL

INCLUDEPATH += $$PWD/../../../../../usr/local/cuda/include
DEPENDPATH += $$PWD/../../../../../usr/local/cuda/include

出典
Adding Libraries to qmake Projects
プロフィール

GM3D

Author:GM3D
FC2ブログへようこそ!

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
FC2カウンター
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QR
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。