Qtのボタンに表示する文字の装飾について


Qt(C++)でQPushButtonの文字を装飾する方法はないでしようか

1．ラベルだと"aaa\nbbb"とすると

[code]

    aaa
    bbb
    
[/code]

こんな感じでラベルの文字が改行されるのですが  
ボタンのテキストに同じように改行されませんでした

しょうが無いのでボタンの上にラベルを配置して改行した文字を表示するようにしたのですが  
そうすると今度はクリックしてもラベルをクリックしたことになってしまいボタンクリックが発生しません

QPushButtonで文字を改行して改行幅を調整できるようにするか  
ラベルを上にかぶせたら、ラベルを透過してボタンクリックすることに出来ないでしょうか

2．ボタンを縦長にしたら文字も縦書きに見えるような方法はないでしょうか

[code]

    あ
    い
    う
    え
    お
    
[/code]

というような感じの縦長ボタンを作りたいのですが




ボタンを自前で描画する必要があります。

`QProxyStyle`クラスを継承した`MyStyle`クラスを作ります。

[code]

    #ifndef MYSTYLE_H
    #define MYSTYLE_H
    
    #include <QPainter>
    #include <QProxyStyle>
    #include <QStyleOption>
    
    class MyStyle : public QProxyStyle {
    public:
        void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p, const QWidget *w) const
        {
            if (element == CE_PushButton) {
                drawControl(CE_PushButtonBevel, opt, p, w);
                drawControl(CE_PushButtonLabel, opt, p, w);
                return;
            }
            QProxyStyle::drawControl(element, opt, p, w);
        }
    };
    
    #endif // MYSTYLE_H
    
[/code]

このインスタンスを親ウィンドウ（`MainWindow`等）に持たせます。

[code]

    private:
        MyStyle style_;
    
[/code]

親ウィンドウのコンストラクタで、ボタンに対してスタイルを設定します。

[code]

        ui->pushButton->setStyle(&style_);
    
[/code]

これで、ボタンの描画を自由にカスタマイズすることができるようになりました。

自前スタイルクラスの`drawControl`関数の中で好きなように描画できます。`drawControl(CE_PushButtonBevel,
...)`でボタンの枠を描画し、`drawControl(CE_PushButtonLabel,
...)`でテキストを描画します。このうち後者を書き換えればテキストを自由に描画できます。

次のようにすると、テキストの代わりに、円が描画されます。

[code]

            if (element == CE_PushButton) {
                drawControl(CE_PushButtonBevel, opt, p, w);
                p->drawEllipse(opt->rect);
                return;
            }
    
[/code]

同じことを次のようにも書けます。

[code]

            if (element == CE_PushButton) {
                drawControl(CE_PushButtonBevel, opt, p, w);
                drawControl(CE_PushButtonLabel, opt, p, w);
                return;
            }
            if (element == CE_PushButtonLabel) {
                p->drawEllipse(opt->rect);
                return;
            }
    
[/code]

描画の仕組みは以上のとおりですが、円ではなく、装飾テキストを描画したいので、`drawEllipse`の代わりに`drawText`にします。

[code]

            if (element == CE_PushButtonLabel) {
                QTextOption textopt;
                textopt.setAlignment(Qt::AlignCenter | Qt::AlignVCenter);
                p->drawText(opt->rect, "Hello,\n world", textopt);
                return;
            }
    
[/code]

上記の例では、説明のために`"Hello,\n
world"`と直書きしてしまいましたので、これしか描画されませんから、本来のボタンに設定されているテキストを取得する必要があります。

`QStyleOption *`を`QStyleOptionButton *`にキャストします。

[code]

            if (auto const *o = qstyleoption_cast<QStyleOptionButton const *>(opt)) {
    
[/code]

こうすると、`o->text`で描画すべきテキストを取得できるようになります。

変更の結果、`drawControl`関数は次のようになります。

[code]

        void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p, const QWidget *w) const
        {
            if (element == CE_PushButton) {
                drawControl(CE_PushButtonBevel, opt, p, w);
                drawControl(CE_PushButtonLabel, opt, p, w);
                return;
            }
            if (element == CE_PushButtonLabel) {
                if (auto const *o = qstyleoption_cast<QStyleOptionButton const *>(opt)) {
                    QTextOption textopt;
                    textopt.setAlignment(Qt::AlignCenter | Qt::AlignVCenter);
                    p->drawText(opt->rect, o->text, textopt);
                }
            }
            QProxyStyle::drawControl(element, opt, p, w);
        }
    
[/code]

親ウィンドウ（`MainWindow`等）で、ボタンのテキストを設定します。

[code]

        ui->pushButton->setText("Push\nButton");
    
[/code]

おまけ。

リッチテキスト（簡易HTML）を描画することもできます。

[code]

            if (element == CE_PushButtonLabel) {
                if (auto const *o = qstyleoption_cast<QStyleOptionButton const *>(opt)) {
                    QTextDocument td;
                    td.setHtml(o->text);
                    td.drawContents(p);
                    return;
                }
            }
    
[/code]

こうすると、次のようなことができます。

[code]

        ui->pushButton->setText("<i>Push</i><br><b>Button</b>");
    
[/code]

ただし、このやり方だと、縦方向の描画サイズの計算と、描画座標の設定が面倒です。縦中央揃えなどを実現するには、さらにたくさんのコードを書かないといけません。大掛かりになるので割愛します。




ボタンを自前で描画する必要があります。

`QProxyStyle`クラスを継承した`MyStyle`クラスを作ります。

[code]

    #ifndef MYSTYLE_H
    #define MYSTYLE_H
    
    #include <QPainter>
    #include <QProxyStyle>
    #include <QStyleOption>
    
    class MyStyle : public QProxyStyle {
    public:
        void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p, const QWidget *w) const
        {
            if (element == CE_PushButton) {
                drawControl(CE_PushButtonBevel, opt, p, w);
                drawControl(CE_PushButtonLabel, opt, p, w);
                return;
            }
            QProxyStyle::drawControl(element, opt, p, w);
        }
    };
    
    #endif // MYSTYLE_H
    
[/code]

このインスタンスを親ウィンドウ（`MainWindow`等）に持たせます。

[code]

    private:
        MyStyle style_;
    
[/code]

親ウィンドウのコンストラクタで、ボタンに対してスタイルを設定します。

[code]

        ui->pushButton->setStyle(&style_);
    
[/code]

これで、ボタンの描画を自由にカスタマイズすることができるようになりました。

自前スタイルクラスの`drawControl`関数の中で好きなように描画できます。`drawControl(CE_PushButtonBevel,
...)`でボタンの枠を描画し、`drawControl(CE_PushButtonLabel,
...)`でテキストを描画します。このうち後者を書き換えればテキストを自由に描画できます。

次のようにすると、テキストの代わりに、円が描画されます。

[code]

            if (element == CE_PushButton) {
                drawControl(CE_PushButtonBevel, opt, p, w);
                p->drawEllipse(opt->rect);
                return;
            }
    
[/code]

同じことを次のようにも書けます。

[code]

            if (element == CE_PushButton) {
                drawControl(CE_PushButtonBevel, opt, p, w);
                drawControl(CE_PushButtonLabel, opt, p, w);
                return;
            }
            if (element == CE_PushButtonLabel) {
                p->drawEllipse(opt->rect);
                return;
            }
    
[/code]

描画の仕組みは以上のとおりですが、円ではなく、装飾テキストを描画したいので、`drawEllipse`の代わりに`drawText`にします。

[code]

            if (element == CE_PushButtonLabel) {
                QTextOption textopt;
                textopt.setAlignment(Qt::AlignCenter | Qt::AlignVCenter);
                p->drawText(opt->rect, "Hello,\n world", textopt);
                return;
            }
    
[/code]

上記の例では、説明のために`"Hello,\n
world"`と直書きしてしまいましたので、これしか描画されませんから、本来のボタンに設定されているテキストを取得する必要があります。

`QStyleOption *`を`QStyleOptionButton *`にキャストします。

[code]

            if (auto const *o = qstyleoption_cast<QStyleOptionButton const *>(opt)) {
    
[/code]

こうすると、`o->text`で描画すべきテキストを取得できるようになります。

変更の結果、`drawControl`関数は次のようになります。

[code]

        void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p, const QWidget *w) const
        {
            if (element == CE_PushButton) {
                drawControl(CE_PushButtonBevel, opt, p, w);
                drawControl(CE_PushButtonLabel, opt, p, w);
                return;
            }
            if (element == CE_PushButtonLabel) {
                if (auto const *o = qstyleoption_cast<QStyleOptionButton const *>(opt)) {
                    QTextOption textopt;
                    textopt.setAlignment(Qt::AlignCenter | Qt::AlignVCenter);
                    p->drawText(opt->rect, o->text, textopt);
                }
            }
            QProxyStyle::drawControl(element, opt, p, w);
        }
    
[/code]

親ウィンドウ（`MainWindow`等）で、ボタンのテキストを設定します。

[code]

        ui->pushButton->setText("Push\nButton");
    
[/code]

おまけ。

リッチテキスト（簡易HTML）を描画することもできます。

[code]

            if (element == CE_PushButtonLabel) {
                if (auto const *o = qstyleoption_cast<QStyleOptionButton const *>(opt)) {
                    QTextDocument td;
                    td.setHtml(o->text);
                    td.drawContents(p);
                    return;
                }
            }
    
[/code]

こうすると、次のようなことができます。

[code]

        ui->pushButton->setText("<i>Push</i><br><b>Button</b>");
    
[/code]

ただし、このやり方だと、縦方向の描画サイズの計算と、描画座標の設定が面倒です。縦中央揃えなどを実現するには、さらにたくさんのコードを書かないといけません。大掛かりになるので割愛します。

