Qtウィンドウのカスタマイズ手法

QtにおけるQWidgetはウィンドウの描画をサポートしていますが、タイトルバーの描画はサポートしていません。美しいインターフェースを実現するためには、独自のカスタマイズが必要です。以下にウィンドウカスタマイズの方法を紹介します。

ウィンドウは基本的には3つの要素で構成されます:タイトルバー、メインコンテンツ領域、ステータスバー。ここではステータスバーを省略したカスタムウィンドウを例に説明します。必要に応じて、この構造に従ってステータスバーを追加することも可能です。要するに、ウィンドウのカスタマイズとは、全体を3つの部分に分割し、それぞれを個別にカスタマイズすることです。各部分は独立したQtウィンドウとして扱われます。

カスタムウィンドウの表示例を以下に示します。ユーザーの好みに応じて、さまざまな見た目を実現できます。

図1 カスタムウィンドウ 図1のように、このウィンドウはタイトルバーとメインコンテンツ領域の2つの部分から構成されています。これらはどちらもタイトルバーを持たないQWidgetであり、それぞれの実装方法について順に説明します。

タイトルバーの実装

コンストラクタのコードは以下の通りです:

 1 setAutoFillBackground(true); // 背景色の自動塗りつぶし、親ウィンドウの背景色を継承しない
 2 
 3 setStyleSheet(QStringLiteral("background:blue;")); // 背景色の設定
 4 
 5 setFixedHeight(30); // 高さを固定(好みに応じて調整可能)
 6 
 7 _p->minimize = new QToolButton(this);
 8 
 9 _p->maximize = new QToolButton(this);
10 
11 _p->close = new QToolButton(this);

コード例システムボタンのアイコン設定

  1 QPixmap pix = style()->standardPixmap(QStyle::SP_TitleBarCloseButton);
  2 
  3 _p->close->setIcon(pix);
  4 
  5 _p->maxPix = style()->standardPixmap(QStyle::SP_TitleBarMaxButton);
  6 
  7 _p->maximize->setIcon(_p->maxPix);
  8 
  9 pix = style()->standardPixmap(QStyle::SP_TitleBarMinButton);
 10 
 11 _p->minimize->setIcon(pix);
 12 
 13 _p->restorePix = style()->standardPixmap(QStyle::SP_TitleBarNormalButton);
 14 
 15 _p->minimize->setMinimumHeight(20);
 16 
 17 _p->close->setMinimumHeight(20);
 18 
 19 _p->maximize->setMinimumHeight(20);
 20 
 21 _p->label = new QLabel(this);
 22 
 23 _p->label->setAttribute(Qt::WA_TransparentForMouseEvents, true); // マウス透過
 24 
 25 _p->label->setStyleSheet(QStringLiteral("font-size:12px; font-weight:Bold"));
 26 
 27 _p->label->setMargin(0);
 28 
 29 SetWindowTitle("title");
 30 
 31 QHBoxLayout *hbox = new QHBoxLayout;
 32 
 33 hbox->addWidget(_p->label);
 34 
 35 hbox->addWidget(_p->minimize);
 36 
 37 hbox->addWidget(_p->maximize);
 38 
 39 hbox->addWidget(_p->close);
 40 
 41 //_p->maximize->setVisible(false);
 42 
 43 hbox->insertStretch(1, 500);
 44 
 45 hbox->setSpacing(0);
 46 
 47 connect(_p->minimize, &QToolButton::clicked, this, [this]{
 48 
 49     BaseWidget * widget = qobject_cast(parent());
 50 
 51     if (widget)
 52 
 53     {
 54 
 55         widget->SetWindowMovePos(widget->pos());
 56 
 57         emit WindowDockedSignal();
 58 
 59     }
 60 
 61 });
 62 
 63 connect(_p->maximize, &QToolButton::clicked, this, &TitleBar::showMaxRestore);
 64 
 65 connect(_p->close, &QToolButton::clicked, this, [this]{emit WindowCloseSignal(_p->label->text()); });
 66 
 67 hbox->setMargin(1);
 68 
 69 hbox->setSpacing(2);
 70 
 71 setLayout(hbox);
 72 
 73 void TitleBar::showMaxRestore() // 最大化または通常状態の切り替え
 74 
 75 {
 76 
 77     if (_p->maxNormal)
 78 
 79     {
 80 
 81         _p->maxNormal = !_p->maxNormal;
 82 
 83         _p->maximize->setIcon(_p->maxPix);
 84 
 85     }
 86 
 87     else
 88 
 89     {
 90 
 91         _p->maxNormal = !_p->maxNormal;
 92 
 93         _p->maximize->setIcon(_p->restorePix);
 94 
 95     }
 96 
 97     emit WindowMaxRestoreSignal(_p->maxNormal);
 98 
 99 }
100 
101 void TitleBar::SetWindowTitle(const QString & title) // ウィンドウタイトルの設定
102 
103 {
104 
105     if (_p)
106 
107     {
108 
109         _p->label->setText(title);
110 
111     }
112 
113 }
114 
115 void TitleBar::SetWindowMaxable(bool isMax) // 最大化可能状態の設定
116 
117 {
118 
119     if (_p)
120 
121     {
122 
123         _p->maxNormal = isMax;
124 
125         if (_p->maxNormal)
126 
127         {
128 
129             _p->maximize->setIcon(_p->maxPix);
130 
131         }
132 
133         else
134 
135         {
136 
137             _p->maximize->setIcon(_p->restorePix);
138 
139         }
140 
141     }
142 
143 }
144 
145 void TitleBar::mousePressEvent(QMouseEvent * event) // ウィンドウ移動処理
146 
147 {
148 
149     QPoint mousePos = _p->minimize->mapFromParent(event->pos());
150 
151     if (_p->minimize->rect().contains(mousePos)) // 最小化ボタンクリック時は移動不可
152 
153     {
154 
155         return;
156 
157     }
158 
159     if (_p->maximize->rect().contains(mousePos)) // 最大化ボタンクリック時は移動不可
160 
161     {
162 
163         return;
164 
165     }
166 
167     _p->leftButtonPressed = true;
168 
169     _p->startPos = event->globalPos();
170 
171     _p->clickPos = mapToParent(event->pos());
172 
173 }
174 
175 void TitleBar::mouseReleaseEvent(QMouseEvent * event)
176 
177 {
178 
179     _p->leftButtonPressed = false;
180 
181 }
182 
183 void TitleBar::mouseMoveEvent(QMouseEvent * event)
184 
185 {
186 
187     if (_p->maxNormal)
188 
189     {
190 
191         return;
192 
193     }
194 
195     if (_p->leftButtonPressed)
196 
197     {
198 
199         parentWidget()->move(event->globalPos() - _p->clickPos); // 親ウィンドウを移動
200 
201     }
202 
203 }
204 
205 void TitleBar::mouseDoubleClickEvent(QMouseEvent * event)
206 
207 {
208 
209     showMaxRestore();
210 
211 }

コード例メインコンテンツ領域のカスタマイズ

以下に実装コードを示します:

C++コード:

 1 setFrameShape(Panel);
 2 
 3 setMouseTracking(true);
 4 
 5 setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::Tool); // タイトルバーを非表示
 6 
 7 setFixedSize(150, 190);
 8 
 9 _p->m_mouse_down = false;
10 
11 _p->m_titleBar = new TitleBar(this);
12 
13 _p->m_titleBar->installEventFilter(this);
14 
15 connect(_p->m_titleBar, &TitleBar::WindowDockedSignal, this, [this]{
16 
17 setFixedSize(_p->m_content->size());
18 
19 setMinimumSize(0, 0);
20 
21 setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
22 
23 _p->m_titleBar->setVisible(false);
24 
25 });
26 
27 connect(_p->m_titleBar, &TitleBar::WindowMaxRestoreSignal, this, [this](bool isMax){
28 
29 if (isMax)
30 
31 {
32 
33 showMaximized();
34 
35 }
36 
37 else
38 
39 {
40 
41 showNormal();
42 
43 }
44 
45 });
46 
47 _p->m_content = new QWidget(this);
48 
49 QPalette plt = _p->m_content->palette();
50 
51 plt.setColor(QPalette::Window, QColor("#000000"));
52 
53 _p->m_content->setPalette(plt); // 背景色を黒に設定
54 
55 _p->m_content->setAutoFillBackground(true);
56 
57 _p->m_content->setAttribute(Qt::WA_PaintOnScreen, true);
58 
59 QVBoxLayout *vbox = new QVBoxLayout(this);
60 
61 vbox->addWidget(_p->m_titleBar);
62 
63 vbox->setStretchFactor(_p->m_titleBar, 0);
64 
65 vbox->setMargin(0);
66 
67 vbox->setSpacing(0);
68 
69 QVBoxLayout *layout = new QVBoxLayout(this);
70 
71 layout->addWidget(_p->m_content);
72 
73 layout->setMargin(0);
74 
75 layout->setSpacing(0);
76 
77 vbox->addLayout(layout);
78 
79 vbox->setStretchFactor(layout, 1);
80 
81 TitleBar * BaseWidget::GetTitleBar() const
82 
83 {
84 
85      if (_p)
86 
87      {
88 
89           return _p->m_titleBar;
90 
91       }
92 
93 }
94 
95 QWidget * BaseWidget::GetContentWidget() const
96 
97 {
98 
99      if (_p)
100 
101       {
102 
103             return _p->m_content;
104 
105        }
106 
107 }
108 
109 BaseWidget::State BaseWidget::GetWindowState() const
110 
111 {
112 
113     if (_p)
114 
115     {
116 
117           return _p->state;
118 
119      } 
120 
121 }
122 
123 void BaseWidget::SetWindowState(BaseWidget::State state)
124 
125 {
126 
127      if (_p)
128 
129       {
130 
131              _p->state = state;
132 
133              if (_p->state == _floating)
134 
135              {
136 
137                   _p->m_titleBar->setVisible(true);
138 
139                }
140 
141                else
142 
143               {
144 
145                      _p->m_titleBar->setVisible(false);
146 
147                }
148 
149        }
150 
151 }
152 
153 QPoint BaseWidget::GetWindowFloatPos() const
154 
155 {
156 
157     if (_p)
158 
159     {
160 
161           return _p->floatPostion;
162 
163     }  
164 
165 }
166 
167 void BaseWidget::SetWindowFloatPos(const QPoint & point)
168 
169 {
170 
171      if (_p)
172 
173      {
174 
175            _p->floatPostion = point;
176 
177      }
178 
179 }
180 
181 QPoint BaseWidget::GetWindowMovePos() const
182 
183 {
184 
185     if (_p)
186 
187     {
188 
189     return _p->m_restorePos;
190 
191     }
192 
193 }
194 
195 void BaseWidget::SetWindowMovePos(const QPoint & pos)
196 
197 {
198 
199     if (_p)
200 
201     {
202 
203           _p->m_restorePos = pos;
204 
205      }
206 
207 }
208 
209 bool BaseWidget::eventFilter(QObject * object, QEvent * event)
210 
211 {
212 
213 if (object == _p->m_titleBar)
214 
215 {
216 
217      if (event->type() == QEvent::MouseMove)
218 
219      {
220 
221         QMouseEvent * mouseEvent = static_cast(event);
222 
223         if (mouseEvent && mouseEvent->button() & Qt::LeftButton)
224 
225          {
226 
227                 return true; // 処理をスキップ
228 
229             }
230 
231         }
232 
233     }
234 
235      return QWidget::eventFilter(object, event);
236 
237 }
238 
239 void BaseWidget::mousePressEvent(QMouseEvent * event)
240 
241 {
242 
243      if (event->button() == Qt::LeftButton)
244 
245      {
246 
247         _p->m_mouse_down = true;
248 
249          _p->dragPostion = event->globalPos() - frameGeometry().topLeft();
250 
251          event->accept();
252 
253      }
254 
255 }
256 
257 void BaseWidget::mouseMoveEvent(QMouseEvent * event)
258 
259 {
260 
261     if (_p->state != _floating || isMaximized()) // フローティング状態でなく、最大化されていない場合のみドラッグ可能
262 
263     {
264 
265          return;
266 
267     }
268 
269     if (_p->m_mouse_down)
270 
271     {
272 
273         move(event->globalPos() - _p->dragPostion);
274 
275         event->accept();
276 
277     }
278 
279 }
280 
281 void BaseWidget::mouseReleaseEvent(QMouseEvent * event)
282 
283 {
284 
285     _p->m_mouse_down = false; 
286 
287 }
288 
289 void BaseWidget::mouseDoubleClickEvent(QMouseEvent * event)
290 
291 {
292 
293  if (_p->state == _dock)
294 
295  {
296 
297          _p->state = _floating;
298 
299          _p->m_titleBar->setVisible(true);
300 
301          setParent(nullptr);
302 
303          setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::Tool);
304 
305          move(GetWindowMovePos());
306 
307          show();
308 
309     }
310 
311 }

コード例以上の2つのコードにより、基本的な機能が実装可能です。

タグ: Qt ウィンドウカスタマイズ GUI タイトルバー Widget

6月28日 16:35 投稿