Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qpixeltool.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "qpixeltool.h"
5
6#include <qapplication.h>
7#include <qdir.h>
8#include <qscreen.h>
9#if QT_CONFIG(clipboard)
10#include <qclipboard.h>
11#endif
12#include <qpainter.h>
13#include <qevent.h>
14#include <qfiledialog.h>
15#include <qmessagebox.h>
16#include <qsettings.h>
17#include <qmenu.h>
18#include <qactiongroup.h>
19#include <qimagewriter.h>
20#include <qstandardpaths.h>
21#include <qtextstream.h>
22#include <qwindow.h>
23#include <qmetaobject.h>
24#include <private/qhighdpiscaling_p.h>
25
26#include <qdebug.h>
27
28#include <cmath>
29
30QT_BEGIN_NAMESPACE
31
32using namespace Qt::StringLiterals;
33
34static constexpr auto settingsGroup = "QPixelTool"_L1;
35static constexpr auto organization = "QtProject"_L1;
36static constexpr auto autoUpdateKey = "autoUpdate"_L1;
37static constexpr auto gridSizeKey = "gridSize"_L1;
38static constexpr auto gridActiveKey = "gridActive"_L1;
39static constexpr auto zoomKey = "zoom"_L1;
40static constexpr auto initialSizeKey = "initialSize"_L1;
41static constexpr auto positionKey = "position"_L1;
42static constexpr auto lcdModeKey = "lcdMode"_L1;
43
44static QPoint initialPos(const QSettings &settings, QSize initialSize)
45{
46 const QPoint defaultPos = QGuiApplication::primaryScreen()->availableGeometry().topLeft();
47 const QPoint savedPos =
48 settings.value(positionKey, QVariant(defaultPos)).toPoint();
49 auto *savedScreen = QGuiApplication::screenAt(savedPos);
50 return savedScreen != nullptr
51 && savedScreen->availableGeometry().intersects(QRect(savedPos, initialSize))
52 ? savedPos : defaultPos;
53}
54
55QPixelTool::QPixelTool(QWidget *parent)
56 : QWidget(parent)
57{
58 setWindowTitle(QCoreApplication::applicationName());
59 QSettings settings(organization, settingsGroup);
60 m_autoUpdate = settings.value(autoUpdateKey, 0).toBool();
61 m_gridSize = settings.value(gridSizeKey, 1).toInt();
62 m_gridActive = settings.value(gridActiveKey, 1).toInt();
63 m_zoom = settings.value(zoomKey, 4).toInt();
64 m_initialSize = settings.value(initialSizeKey, QSize(250, 200)).toSize();
65 m_lcdMode = settings.value(lcdModeKey, 0).toInt();
66
67 move(initialPos(settings, m_initialSize));
68
69 setMouseTracking(true);
70 setAttribute(Qt::WA_OpaquePaintEvent);
71 m_updateId = startTimer(30);
72}
73
75{
76 QSettings settings(organization, settingsGroup);
77 settings.setValue(autoUpdateKey, int(m_autoUpdate));
78 settings.setValue(gridSizeKey, m_gridSize);
79 settings.setValue(gridActiveKey, m_gridActive);
80 settings.setValue(zoomKey, m_zoom);
81 settings.setValue(initialSizeKey, size());
82 settings.setValue(positionKey, pos());
83 settings.setValue(lcdModeKey, m_lcdMode);
84}
85
86void QPixelTool::setPreviewImage(const QImage &image)
87{
88 m_preview_mode = true;
89 m_preview_image = image;
90 m_freeze = true;
91}
92
93void QPixelTool::timerEvent(QTimerEvent *event)
94{
95 if (event->timerId() == m_updateId && !m_freeze) {
96 grabScreen();
97 } else if (event->timerId() == m_displayZoomId) {
98 killTimer(m_displayZoomId);
99 m_displayZoomId = 0;
100 setZoomVisible(false);
101 } else if (event->timerId() == m_displayGridSizeId) {
102 killTimer(m_displayGridSizeId);
103 m_displayGridSizeId = 0;
104 m_displayGridSize = false;
105 }
106}
107
108void render_string(QPainter *p, int w, int h, const QString &text, int flags)
109{
110 p->setBrush(QColor(255, 255, 255, 191));
111 p->setPen(Qt::black);
112 QRect bounds;
113 p->drawText(0, 0, w, h, Qt::TextDontPrint | flags, text, &bounds);
114
115 if (bounds.x() == 0) bounds.adjust(0, 0, 10, 0);
116 else bounds.adjust(-10, 0, 0, 0);
117
118 if (bounds.y() == 0) bounds.adjust(0, 0, 0, 10);
119 else bounds.adjust(0, -10, 0, 0);
120
121 p->drawRect(bounds);
122 p->drawText(bounds, flags, text);
123}
124
125static QImage imageLCDFilter(const QImage &image, int lcdMode)
126{
127 Q_ASSERT(lcdMode > 0 && lcdMode < 5);
128 const bool vertical = (lcdMode > 2);
129 QImage scaled(image.width() * (vertical ? 1 : 3),
130 image.height() * (vertical ? 3 : 1),
131 image.format());
132
133 const int w = image.width();
134 const int h = image.height();
135 if (!vertical) {
136 for (int y = 0; y < h; ++y) {
137 const QRgb *in = reinterpret_cast<const QRgb *>(image.scanLine(y));
138 QRgb *out = reinterpret_cast<QRgb *>(scaled.scanLine(y));
139 if (lcdMode == 1) {
140 for (int x = 0; x < w; ++x) {
141 *out++ = in[x] & 0xffff0000;
142 *out++ = in[x] & 0xff00ff00;
143 *out++ = in[x] & 0xff0000ff;
144 }
145 } else {
146 for (int x = 0; x < w; ++x) {
147 *out++ = in[x] & 0xff0000ff;
148 *out++ = in[x] & 0xff00ff00;
149 *out++ = in[x] & 0xffff0000;
150 }
151 }
152 }
153 } else {
154 for (int y = 0; y < h; ++y) {
155 const QRgb *in = reinterpret_cast<const QRgb *>(image.scanLine(y));
156 QRgb *out1 = reinterpret_cast<QRgb *>(scaled.scanLine(y * 3 + 0));
157 QRgb *out2 = reinterpret_cast<QRgb *>(scaled.scanLine(y * 3 + 1));
158 QRgb *out3 = reinterpret_cast<QRgb *>(scaled.scanLine(y * 3 + 2));
159 if (lcdMode == 2) {
160 for (int x = 0; x < w; ++x) {
161 out1[x] = in[x] & 0xffff0000;
162 out2[x] = in[x] & 0xff00ff00;
163 out3[x] = in[x] & 0xff0000ff;
164 }
165 } else {
166 for (int x = 0; x < w; ++x) {
167 out1[x] = in[x] & 0xff0000ff;
168 out2[x] = in[x] & 0xff00ff00;
169 out3[x] = in[x] & 0xffff0000;
170 }
171 }
172 }
173 }
174 return scaled;
175}
176
177void QPixelTool::paintEvent(QPaintEvent *)
178{
179 QPainter p(this);
180
181 if (m_preview_mode) {
182 QPixmap pixmap(40, 40);
183 QPainter pt(&pixmap);
184 pt.fillRect(0, 0, 20, 20, Qt::white);
185 pt.fillRect(20, 20, 20, 20, Qt::white);
186 pt.fillRect(20, 0, 20, 20, Qt::lightGray);
187 pt.fillRect(0, 20, 20, 20, Qt::lightGray);
188 pt.end();
189 p.fillRect(0, 0, width(), height(), pixmap);
190 }
191
192 int w = width();
193 int h = height();
194
195 p.save();
196 if (m_lcdMode == 0) {
197 p.scale(m_zoom, m_zoom);
198 p.drawPixmap(0, 0, m_buffer);
199 } else {
200 if (m_lcdMode <= 2)
201 p.scale(m_zoom / 3.0, m_zoom);
202 else
203 p.scale(m_zoom, m_zoom / 3.0);
204 p.drawImage(0, 0, imageLCDFilter(m_buffer.toImage(), m_lcdMode));
205 }
206 p.restore();
207
208 // Draw the grid on top.
209 if (m_gridActive) {
210 p.setPen(m_gridActive == 1 ? Qt::black : Qt::white);
211 int incr = m_gridSize * m_zoom;
212 if (m_lcdMode == 0 || m_lcdMode > 2) {
213 for (int x=0; x<w; x+=incr)
214 p.drawLine(x, 0, x, h);
215 }
216 if (m_lcdMode <= 2) {
217 for (int y=0; y<h; y+=incr)
218 p.drawLine(0, y, w, y);
219 }
220 }
221
222 QFont f(QStringList{u"courier"_s}, -1, QFont::Bold);
223 p.setFont(f);
224
225 if (m_displayZoom) {
226 render_string(&p, w, h,
227 "Zoom: x"_L1 + QString::number(m_zoom),
228 Qt::AlignTop | Qt::AlignRight);
229 }
230
231 if (m_displayGridSize) {
232 render_string(&p, w, h,
233 "Grid size: "_L1 + QString::number(m_gridSize),
234 Qt::AlignBottom | Qt::AlignLeft);
235 }
236
237 if (m_freeze) {
238 QString str = QString::asprintf("%8X (%3d,%3d,%3d,%3d)",
239 m_currentColor,
240 (0xff000000 & m_currentColor) >> 24,
241 (0x00ff0000 & m_currentColor) >> 16,
242 (0x0000ff00 & m_currentColor) >> 8,
243 (0x000000ff & m_currentColor));
244 render_string(&p, w, h,
245 str,
246 Qt::AlignBottom | Qt::AlignRight);
247 }
248
249 if (m_mouseDown && m_dragStart != m_dragCurrent) {
250 int x1 = (m_dragStart.x() / m_zoom) * m_zoom;
251 int y1 = (m_dragStart.y() / m_zoom) * m_zoom;
252 int x2 = (m_dragCurrent.x() / m_zoom) * m_zoom;
253 int y2 = (m_dragCurrent.y() / m_zoom) * m_zoom;
254 QRect r = QRect(x1, y1, x2 - x1, y2 - y1).normalized();
255 p.setBrush(Qt::NoBrush);
256 p.setPen(QPen(Qt::red, 3, Qt::SolidLine));
257 p.drawRect(r);
258 p.setPen(QPen(Qt::black, 1, Qt::SolidLine));
259 p.drawRect(r);
260
261 QString str = QString::asprintf("Rect: x=%d, y=%d, w=%d, h=%d",
262 r.x() / m_zoom,
263 r.y() / m_zoom,
264 r.width() / m_zoom,
265 r.height() / m_zoom);
266 render_string(&p, w, h, str, Qt::AlignBottom | Qt::AlignLeft);
267 }
268
269
270}
271
272void QPixelTool::keyPressEvent(QKeyEvent *e)
273{
274 switch (e->key()) {
275 case Qt::Key_Space:
277 break;
278 case Qt::Key_Plus:
280 break;
281 case Qt::Key_Minus:
283 break;
284 case Qt::Key_PageUp:
285 setGridSize(m_gridSize + 1);
286 break;
287 case Qt::Key_PageDown:
288 setGridSize(m_gridSize - 1);
289 break;
290 case Qt::Key_G:
292 break;
293 case Qt::Key_A:
294 m_autoUpdate = !m_autoUpdate;
295 break;
296#if QT_CONFIG(clipboard)
297 case Qt::Key_C:
298 if (e->modifiers().testFlag(Qt::ControlModifier))
299 copyToClipboard();
300 else
301 copyColorToClipboard();
302 break;
303#endif // QT_CONFIG(clipboard)
304 case Qt::Key_S:
305 if (e->modifiers().testFlag(Qt::ControlModifier)) {
306 releaseKeyboard();
308 }
309 break;
310 case Qt::Key_Control:
311 grabKeyboard();
312 break;
313 case Qt::Key_F1:
315 break;
316 }
317}
318
319void QPixelTool::keyReleaseEvent(QKeyEvent *e)
320{
321 switch(e->key()) {
322 case Qt::Key_Control:
323 releaseKeyboard();
324 break;
325 default:
326 break;
327 }
328}
329
330void QPixelTool::resizeEvent(QResizeEvent *)
331{
332 grabScreen();
333}
334
335void QPixelTool::mouseMoveEvent(QMouseEvent *e)
336{
337 if (m_mouseDown)
338 m_dragCurrent = e->pos();
339
340 const auto pos = e->position().toPoint();
341 const int x = pos.x() / m_zoom;
342 const int y = pos.y() / m_zoom;
343
344 QImage im = m_buffer.toImage().convertToFormat(QImage::Format_ARGB32);
345 if (x < im.width() && y < im.height() && x >= 0 && y >= 0) {
346 m_currentColor = im.pixel(x, y);
347 update();
348 }
349}
350
351void QPixelTool::mousePressEvent(QMouseEvent *e)
352{
353 if (!m_freeze)
354 return;
355 m_mouseDown = true;
356 m_dragStart = e->pos();
357}
358
359void QPixelTool::mouseReleaseEvent(QMouseEvent *)
360{
361 m_mouseDown = false;
362}
363
364static QAction *addCheckableAction(QMenu &menu, const QString &title,
365 bool value, const QKeySequence &key)
366{
367 QAction *result = menu.addAction(title);
368 result->setCheckable(true);
369 result->setChecked(value);
370 result->setShortcut(key);
371 return result;
372}
373
374static QAction *addCheckableAction(QMenu &menu, const QString &title,
375 bool value, const QKeySequence &key,
376 QActionGroup *group)
377{
378 QAction *result = addCheckableAction(menu, title, value, key);
379 result->setActionGroup(group);
380 return result;
381}
382
383void QPixelTool::contextMenuEvent(QContextMenuEvent *e)
384{
385 const bool tmpFreeze = m_freeze;
386 m_freeze = true;
387
388 QMenu menu;
389 menu.addAction("Qt Pixel Zooming Tool"_L1)->setEnabled(false);
390 menu.addSeparator();
391
392 // Grid color options...
393 auto *gridGroup = new QActionGroup(&menu);
394 addCheckableAction(menu, "White grid"_L1, m_gridActive == 2,
395 Qt::Key_W, gridGroup);
396 QAction *blackGrid = addCheckableAction(menu, "Black grid"_L1,
397 m_gridActive == 1, Qt::Key_B, gridGroup);
398 QAction *noGrid = addCheckableAction(menu, "No grid"_L1, m_gridActive == 0,
399 Qt::Key_N, gridGroup);
400 menu.addSeparator();
401
402 // Grid size options
403 menu.addAction("Increase grid size"_L1, Qt::Key_PageUp,
404 this, &QPixelTool::increaseGridSize);
405 menu.addAction("Decrease grid size"_L1, Qt::Key_PageDown,
406 this, &QPixelTool::decreaseGridSize);
407 menu.addSeparator();
408
409 auto *lcdGroup = new QActionGroup(&menu);
410 addCheckableAction(menu, "No subpixels"_L1, m_lcdMode == 0,
411 QKeySequence(), lcdGroup);
412 QAction *rgbPixels = addCheckableAction(menu, "RGB subpixels"_L1,
413 m_lcdMode == 1, QKeySequence(), lcdGroup);
414 QAction *bgrPixels = addCheckableAction(menu, "BGR subpixels"_L1,
415 m_lcdMode == 2, QKeySequence(), lcdGroup);
416 QAction *vrgbPixels = addCheckableAction(menu, "VRGB subpixels"_L1,
417 m_lcdMode == 3, QKeySequence(), lcdGroup);
418 QAction *vbgrPixels = addCheckableAction(menu, "VBGR subpixels"_L1,
419 m_lcdMode == 4, QKeySequence(), lcdGroup);
420 menu.addSeparator();
421
422 // Zoom options
423 menu.addAction("Zoom in"_L1, Qt::Key_Plus,
424 this, &QPixelTool::increaseZoom);
425 menu.addAction("Zoom out"_L1, Qt::Key_Minus,
426 this, &QPixelTool::decreaseZoom);
427 menu.addSeparator();
428
429 // Freeze / Autoupdate
430 QAction *freeze = addCheckableAction(menu, "Frozen"_L1,
431 tmpFreeze, Qt::Key_Space);
432 QAction *autoUpdate = addCheckableAction(menu, "Continuous update"_L1,
433 m_autoUpdate, Qt::Key_A);
434 menu.addSeparator();
435
436 // Copy to clipboard / save
437 menu.addAction("Save as image..."_L1, QKeySequence::SaveAs,
438 this, &QPixelTool::saveToFile);
439#if QT_CONFIG(clipboard)
440 menu.addAction("Copy to clipboard"_L1, QKeySequence::Copy,
441 this, &QPixelTool::copyToClipboard);
442 menu.addAction("Copy color value to clipboard"_L1, Qt::Key_C,
443 this, &QPixelTool::copyColorToClipboard);
444 // Screen
445 menu.addSeparator();
446 menu.addAction("Copy screen shot to clipboard"_L1, QKeySequence(),
447 this, &QPixelTool::copyScreenShotToClipboard);
448 menu.addAction("Copy screen info to clipboard"_L1, QKeySequence(),
449 this, &QPixelTool::copyScreenInfoToClipboard);
450#endif // QT_CONFIG(clipboard)
451
452 menu.addSeparator();
453 menu.addAction("About Qt"_L1, qApp, &QApplication::aboutQt);
454 menu.addAction("About Qt Pixeltool"_L1, this, &QPixelTool::aboutPixelTool);
455
456 menu.exec(mapToGlobal(e->pos()));
457
458 // Read out grid settings
459 if (noGrid->isChecked())
460 m_gridActive = 0;
461 else if (blackGrid->isChecked())
462 m_gridActive = 1;
463 else
464 m_gridActive = 2;
465
466 // Read out lcd settings
467 if (rgbPixels->isChecked())
468 m_lcdMode = 1;
469 else if (bgrPixels->isChecked())
470 m_lcdMode = 2;
471 else if (vrgbPixels->isChecked())
472 m_lcdMode = 3;
473 else if (vbgrPixels->isChecked())
474 m_lcdMode = 4;
475 else
476 m_lcdMode = 0;
477
478 m_autoUpdate = autoUpdate->isChecked();
479 m_freeze = freeze->isChecked();
480
481 // LCD mode looks off unless zoom is dividable by 3
482 if (m_lcdMode && (m_zoom % 3) != 0)
483 setZoom(qMax(3, (m_zoom + 1) / 3));
484}
485
487{
488 return m_initialSize;
489}
490
491static inline QString pixelToolTitle(QPoint pos, const QScreen *screen, const QColor &currentColor)
492{
493 if (screen != nullptr)
494 pos = QHighDpi::toNativePixels(pos, screen);
495 return QCoreApplication::applicationName() + " ["_L1
496 + QString::number(pos.x())
497 + ", "_L1 + QString::number(pos.y()) + "] "_L1
498 + currentColor.name();
499}
500
501void QPixelTool::grabScreen()
502{
503 if (m_preview_mode) {
504 int w = qMin(width() / m_zoom + 1, m_preview_image.width());
505 int h = qMin(height() / m_zoom + 1, m_preview_image.height());
506 m_buffer = QPixmap::fromImage(m_preview_image).copy(0, 0, w, h);
507 update();
508 return;
509 }
510
511 QPoint mousePos = QCursor::pos();
512 if (mousePos == m_lastMousePos && !m_autoUpdate)
513 return;
514
515 QScreen *screen = QGuiApplication::screenAt(mousePos);
516
517 if (m_lastMousePos != mousePos)
518 setWindowTitle(pixelToolTitle(mousePos, screen, m_currentColor));
519
520 const auto widgetDpr = devicePixelRatioF();
521 const auto screenDpr = screen != nullptr ? screen->devicePixelRatio() : widgetDpr;
522 // When grabbing from another screen, we grab an area fitting our size using our DPR.
523 const auto factor = widgetDpr / screenDpr / qreal(m_zoom);
524 const QSize size{int(std::ceil(width() * factor)), int(std::ceil(height() * factor))};
525 const QPoint pos = mousePos - QPoint{size.width(), size.height()} / 2;
526
527 const QBrush darkBrush = palette().color(QPalette::Dark);
528 if (screen != nullptr) {
529 const QPoint screenPos = pos - screen->geometry().topLeft();
530 m_buffer = screen->grabWindow(0, screenPos.x(), screenPos.y(), size.width(), size.height());
531 } else {
532 m_buffer = QPixmap(size);
533 m_buffer.fill(darkBrush.color());
534 }
535 m_buffer.setDevicePixelRatio(widgetDpr);
536
537 QRegion geom(QRect{pos, size});
538 QRect screenRect;
539 const auto screens = QGuiApplication::screens();
540 for (auto *screen : screens)
541 screenRect |= screen->geometry();
542 geom -= screenRect;
543 const auto rectsInRegion = geom.rectCount();
544 if (!geom.isEmpty()) {
545 QPainter p(&m_buffer);
546 p.translate(-pos);
547 p.setPen(Qt::NoPen);
548 p.setBrush(darkBrush);
549 p.drawRects(geom.begin(), rectsInRegion);
550 }
551
552 update();
553
554 m_currentColor = m_buffer.toImage().pixel(m_buffer.rect().center());
555 m_lastMousePos = mousePos;
556}
557
558void QPixelTool::startZoomVisibleTimer()
559{
560 if (m_displayZoomId > 0)
561 killTimer(m_displayZoomId);
562 m_displayZoomId = startTimer(5000);
564}
565
566void QPixelTool::startGridSizeVisibleTimer()
567{
568 if (m_gridActive) {
569 if (m_displayGridSizeId > 0)
570 killTimer(m_displayGridSizeId);
571 m_displayGridSizeId = startTimer(5000);
572 m_displayGridSize = true;
573 update();
574 }
575}
576
577void QPixelTool::setZoomVisible(bool visible)
578{
579 m_displayZoom = visible;
580 update();
581}
582
584{
585 m_freeze = !m_freeze;
586 if (!m_freeze)
587 m_dragStart = m_dragCurrent = QPoint();
588}
589
591{
592 if (!m_lcdMode)
593 setZoom(m_zoom + 1);
594 else
595 setZoom(m_zoom + 3);
596}
597
599{
600 if (!m_lcdMode)
601 setZoom(m_zoom - 1);
602 else
603 setZoom(m_zoom - 3);
604}
605
606void QPixelTool::setZoom(int zoom)
607{
608 if (zoom > 0) {
609 QPoint pos = m_lastMousePos;
610 m_lastMousePos = QPoint();
611 m_zoom = zoom;
612 grabScreen();
613 m_lastMousePos = pos;
614 m_dragStart = m_dragCurrent = QPoint();
615 startZoomVisibleTimer();
616 }
617}
618
620{
621 if (++m_gridActive > 2)
622 m_gridActive = 0;
623 update();
624}
625
626void QPixelTool::setGridSize(int gridSize)
627{
628 if (m_gridActive && gridSize > 0) {
629 m_gridSize = gridSize;
630 startGridSizeVisibleTimer();
631 update();
632 }
633}
634
635#if QT_CONFIG(clipboard)
636void QPixelTool::copyToClipboard()
637{
638 QGuiApplication::clipboard()->setPixmap(m_buffer);
639}
640
641void QPixelTool::copyColorToClipboard()
642{
643 QGuiApplication::clipboard()->setText(QColor(m_currentColor).name());
644}
645
646void QPixelTool::copyScreenShotToClipboard()
647{
648 QPixmap screenShot = screen()->grabWindow();
649 QGuiApplication::clipboard()->setImage(screenShot.toImage());
650}
651
652void QPixelTool::copyScreenInfoToClipboard()
653{
654 const auto *screen = this->screen();
655 QString text;
656 QTextStream str(&text);
657 const auto geom = screen->geometry();
658 const auto availGeom = screen->availableGeometry();
659 auto orientationMt = QMetaEnum::fromType<Qt::ScreenOrientation>();
660
661 str << "Model/name: \"" << screen->model() << "\"/\"" << screen->name() << '"'
662 << "\nGeometry: " << geom.width() << 'x' << geom.height() << Qt::forcesign
663 << geom.x() << geom.y() << Qt::noforcesign
664 << "\nAvailable geometry: " << availGeom.width() << 'x' << availGeom.height() << Qt::forcesign
665 << availGeom.x() << availGeom.y() << Qt::noforcesign
666 << "\nDevice pixel ratio: " << screen->devicePixelRatio()
667 << "\nLogical DPI: " << screen->logicalDotsPerInchX() << ','
668 << screen->logicalDotsPerInchY() << "DPI"
669 << "\nPhysical DPI: " << screen->physicalDotsPerInchX() << ','
670 << screen->physicalDotsPerInchY() << "DPI"
671 << "\nPhysical size: " << screen->physicalSize().width() << 'x'
672 << screen->physicalSize().height() << "mm";
673 if (const char *orientation = orientationMt.valueToKey(screen->orientation()))
674 str << "\nOrientation: " << orientation;
675 str << "\nRefresh rate: " << screen->refreshRate() << "Hz";
676 QGuiApplication::clipboard()->setText(text);
677}
678
679#endif // QT_CONFIG(clipboard)
680
682{
683 bool oldFreeze = m_freeze;
684 m_freeze = true;
685
686 QFileDialog fileDialog(this);
687 fileDialog.setWindowTitle("Save as image"_L1);
688 fileDialog.setAcceptMode(QFileDialog::AcceptSave);
689 fileDialog.setDirectory(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
690 QStringList mimeTypes;
691 const QByteArrayList supportedMimeTypes = QImageWriter::supportedMimeTypes();
692 for (const QByteArray &mimeTypeB : supportedMimeTypes)
693 mimeTypes.append(QString::fromLatin1(mimeTypeB));
694 fileDialog.setMimeTypeFilters(mimeTypes);
695 const QString pngType = "image/png"_L1;
696 if (mimeTypes.contains(pngType)) {
697 fileDialog.selectMimeTypeFilter(pngType);
698 fileDialog.setDefaultSuffix("png"_L1);
699 }
700
701 while (fileDialog.exec() == QDialog::Accepted
702 && !m_buffer.save(fileDialog.selectedFiles().constFirst())) {
703 QMessageBox::warning(this, "Unable to write image"_L1,
704 "Unable to write "_L1
705 + QDir::toNativeSeparators(fileDialog.selectedFiles().constFirst()));
706 }
707 m_freeze = oldFreeze;
708}
709
710QTextStream &operator<<(QTextStream &str, const QScreen *screen)
711{
712 const QRect geometry = screen->geometry();
713 str << '"' << screen->name() << "\" " << geometry.width()
714 << 'x' << geometry.height() << Qt::forcesign << geometry.x() << geometry.y()
715 << Qt::noforcesign << ", " << qRound(screen->logicalDotsPerInch()) << "DPI"
716 << ", Depth: " << screen->depth() << ", " << screen->refreshRate() << "Hz";
717 const qreal dpr = screen->devicePixelRatio();
718 if (!qFuzzyCompare(dpr, qreal(1)))
719 str << ", DPR: " << dpr;
720 return str;
721}
722
723QString QPixelTool::aboutText() const
724{
725 const QList<QScreen *> screens = QGuiApplication::screens();
726 const QScreen *windowScreen = windowHandle()->screen();
727
728 QString result;
729 QTextStream str(&result);
730 str << "<html><head></head><body><h2>Qt Pixeltool</h2><p>Qt " << QT_VERSION_STR
731 << "</p><p>Copyright (C) 2017 The Qt Company Ltd.</p><h3>Screens</h3><ul>";
732 for (const QScreen *screen : screens)
733 str << "<li>" << (screen == windowScreen ? "* " : " ") << screen << "</li>";
734 str << "</ul></body></html>";
735 return result;
736}
737
739{
740 QMessageBox aboutBox(QMessageBox::Information, tr("About Qt Pixeltool"), aboutText(),
741 QMessageBox::Close, this);
742 aboutBox.setWindowFlags(aboutBox.windowFlags() & ~Qt::WindowContextHelpButtonHint);
743 aboutBox.setTextInteractionFlags(Qt::TextBrowserInteraction);
744 aboutBox.exec();
745}
746
747QT_END_NAMESPACE
The QFileDialog class provides a dialog that allows users to select files or directories.
Definition qfiledialog.h:28
The QMenu class provides a menu widget for use in menu bars, context menus, and other popup menus.
Definition qmenu.h:26
The QMessageBox class provides a modal dialog for informing the user or for asking the user a questio...
Definition qmessagebox.h:22
QPainter(QPaintDevice *)
Constructs a painter that begins painting the paint device immediately.
friend class QWidget
Definition qpainter.h:421
void contextMenuEvent(QContextMenuEvent *event) override
This event handler, for event event, can be reimplemented in a subclass to receive widget context men...
void mousePressEvent(QMouseEvent *event) override
This event handler, for event event, can be reimplemented in a subclass to receive mouse press events...
void resizeEvent(QResizeEvent *event) override
This event handler can be reimplemented in a subclass to receive widget resize events which are passe...
void mouseReleaseEvent(QMouseEvent *event) override
This event handler, for event event, can be reimplemented in a subclass to receive mouse release even...
void keyPressEvent(QKeyEvent *event) override
This event handler, for event event, can be reimplemented in a subclass to receive key press events f...
void timerEvent(QTimerEvent *event) override
This event handler can be reimplemented in a subclass to receive timer events for the object.
void setZoomVisible(bool visible)
void aboutPixelTool()
QSize sizeHint() const override
void mouseMoveEvent(QMouseEvent *event) override
This event handler, for event event, can be reimplemented in a subclass to receive mouse move events ...
void toggleGrid()
void saveToFile()
void decreaseZoom()
void setGridSize(int gridSize)
void keyReleaseEvent(QKeyEvent *event) override
This event handler, for event event, can be reimplemented in a subclass to receive key release events...
void paintEvent(QPaintEvent *event) override
This event handler can be reimplemented in a subclass to receive paint events passed in event.
void toggleFreeze()
void increaseZoom()
\inmodule QtCore
Definition qsettings.h:30
QT_FORWARD_DECLARE_CLASS(QTextStream)
#define qApp
static constexpr auto settingsGroup
static constexpr auto autoUpdateKey
static constexpr auto zoomKey
static QAction * addCheckableAction(QMenu &menu, const QString &title, bool value, const QKeySequence &key)
QTextStream & operator<<(QTextStream &str, const QScreen *screen)
static constexpr auto gridSizeKey
static constexpr auto positionKey
static constexpr auto initialSizeKey
static constexpr auto lcdModeKey
static QPoint initialPos(const QSettings &settings, QSize initialSize)
static QImage imageLCDFilter(const QImage &image, int lcdMode)
void render_string(QPainter *p, int w, int h, const QString &text, int flags)
static constexpr auto organization
static constexpr auto gridActiveKey
static QString pixelToolTitle(QPoint pos, const QScreen *screen, const QColor &currentColor)