65525


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

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

[code]

    aaa
    bbb
    
[/code]

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

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

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

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

[code]

    あ
    い
    う
    え
    お
    
[/code]

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


CC BY-SA 4.0
2020-04-10T11:01:46.973
65519
2020-04-11T15:08:05.880
10098
question
qt qt-creator
Qtのボタンに表示する文字の装飾について


1.ボタンに配置したラベルのクリック透過

該当ラベルのクリック処理で`event->ignore()`すれば良いのでは？ 試してみてください。

[QMouseEvent Class](https://doc.qt.io/qt-5/qmouseevent.html)

> A mouse event contains a special accept flag that indicates whether the
> receiver wants the event. You should call ignore() if the mouse event is not
> handled by your widget. A mouse event is propagated up the parent widget
> chain until a widget accepts it with accept(), or an event filter consumes
> it.  
>  Note: If a mouse event is propagated to a widget for which
> Qt::WA_NoMousePropagation has been set, that mouse event will not be
> propagated further up the parent widget chain.
>
> マウスイベントには、受信者がイベントを必要とするかどうかを示す特別な受け入れフラグが含まれています。
> マウスイベントがウィジェットによって処理されない場合は、ignore()を呼び出す必要があります。
> マウスイベントは、ウィジェットがaccept()でそれを受け入れるか、イベントフィルターがそれを消費するまで、親ウィジェットチェーンに伝達されます。  
>  注：Qt ::
> WA_NoMousePropagationが設定されているウィジェットにマウスイベントが伝達される場合、そのマウスイベントは親ウィジェットチェーンの上方には伝達されません。

[void QEvent::ignore()](https://doc.qt.io/qt-5/qevent.html#ignore)

> Clears the accept flag parameter of the event object, the equivalent of
> calling setAccepted(false).  
>  Clearing the accept parameter indicates that the event receiver does not
> want the event. Unwanted events might be propagated to the parent widget.
>
> イベントオブジェクトの受け入れフラグパラメーターをクリアします。これは、setAccepted(false)を呼び出すのと同等です。  
>  acceptパラメーターをクリアすると、イベントレシーバーがイベントを望まないことを示します。
> 不要なイベントが親ウィジェットに伝播される可能性があります。

[■QtのQTouchEventのページの解説(Qt 4.8)](http://tarodev.web.fc2.com/nancyatte.html)

> イベントの配信と伝達
>
> QEvent::TouchBeginイベントは受信側がイベントを必要とするかどうかを示す特別な受け入れフラグを含んでいます。  
>  デフォルトでは、イベントは受け入れられます。  
>  あなたのウィジェットでタッチイベントを処理しない場合はignore()を呼び出すべきです。  
>
> QEvent::TouchBeginイベントはウィジェットがaccept()でそれを受け入れるか、イベントフィルターで処理されるまで親のウィジェットを伝って伝達されます。  
>
> QGraphicsItemsでは、QEvent::TouchBeginイベントはマウスの下のアイテムに伝達されます(QGraphicsItemsにマウスイベントが伝達されるのに似ています)。

* * *

2.縦書き

古い記事ですがサポートしていないとのこと。  
[日本語の縦書きについて](https://forum.qt.io/topic/55440/%E6%97%A5%E6%9C%AC%E8%AA%9E%E3%81%AE%E7%B8%A6%E6%9B%B8%E3%81%8D%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)

> Qt は現時点では文字の縦書き表示をサポートしていません。  
>  [[QTBUG-529] Add support for vertical text
> writing](https://bugreports.qt.io/browse/QTBUG-529)

作業した人は居るようですが、却下されたようです。  
[Abandoned|73319:WIP:Support vertical text layout](https://codereview.qt-
project.org/c/qt/qtbase/+/73319/)

ただ、この記事の @soramimi さんの回答で Qt で縦書きを試したような記述があります。  
[テキストエディタ作成に強いプログラミング言語は？](https://ja.stackoverflow.com/q/42300/26370)

ラベルくらいなら何とか対処できるのかもしれません。  
[@soramimiさんの回答](https://ja.stackoverflow.com/a/42320/26370)


CC BY-SA 4.0
2020-04-10T11:52:20.610
65521
2020-04-10T11:52:20.610
2020-06-17T08:14:45.997
-1
26370
65519
answer


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

`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]

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


CC BY-SA 4.0
2020-04-10T13:17:01.163
65525
2020-04-10T13:17:01.163
3337
65519
answer


お二人ともありがとうございます  
まず、labelにはclickイベントがないのでigoreは使えませんでした  
また、proxyもボタン一つのためにそこまでするか・・・と言うのがあって  
今回は超インチキをして回避しました

●ボタンを上にして透明にする  
ボタンに対して以下の関数を実行

[code]

        ui->btnA->setWindowOpacity(0);
        ui->btnA->setAttribute(Qt::WA_TranslucentBackground, false);
    
[/code]

デザイナーのプロパティのstylesheetのところにしてクリックされたら半透明にする

[code]

    QPushButton:!pressed{background-color: rgba( 0, 0, 255,   0);border:0px none;
    QPushButton:pressed{background-color: rgba( 0, 0, 255,   0.5);border:0px none;
    
[/code]

●ラベルを下にして改行幅を調整  
デザイナーのプロパティのtextのところに

[code]

    <p style=\"line-height:80%\">あいうえお<br />かきくけこ</p>
    
[/code]

としてみたら見た目は本来のクリックとは違いますがとりあえずOKになりました(背景を黒にしたらクリックしたらなんとなくピカッと光るような感じになりました)  
縦書きはもう上の文字の「あいうえお」のところをプログラムでひたすら一文字ずつ<br />入れるという解決法を使いました


CC BY-SA 4.0
2020-04-11T15:08:05.880
65545
2020-04-11T15:08:05.880
10098
65519
answer