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つのコードにより、基本的な機能が実装可能です。