KDDockWidgets API Documentation  1.5
ObjectViewer.cpp
Go to the documentation of this file.
1 /*
2  This file is part of KDDockWidgets.
3 
4  SPDX-FileCopyrightText: 2019-2022 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
5  Author: SĂ©rgio Martins <sergio.martins@kdab.com>
6 
7  SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
8 
9  Contact KDAB at <info@kdab.com> for commercial licensing options.
10 */
11 
19 #include "ObjectViewer_p.h"
20 
21 #include <QStandardItem>
22 #include <QApplication>
23 #include <QHBoxLayout>
24 #include <QMenu>
25 #include <QContextMenuEvent>
26 #include <QItemSelectionModel>
27 #include <QPainter>
28 #include <QDebug>
29 #include <QMetaProperty>
30 #include <QWindow>
31 #include <QToolBar>
32 #include <QShortcut>
33 #include <QDir>
34 
35 #ifdef Q_OS_WIN
36 #include <windows.h>
37 #endif
38 
39 using namespace KDDockWidgets::Debug;
40 
41 enum Role
42 {
44 };
45 
46 
47 ObjectViewer::ObjectViewer(QWidget *parent)
48  : QWidget(parent)
49 {
50  resize(600, 600);
51 
52  auto lay = new QHBoxLayout(this);
53  lay->addWidget(&m_treeView);
54  m_treeView.setModel(&m_model);
55  connect(m_treeView.selectionModel(), &QItemSelectionModel::selectionChanged,
56  this, &ObjectViewer::onSelectionChanged);
57 
58  QAction *action = m_menu.addAction(QStringLiteral("Refresh"));
59  connect(action, &QAction::triggered, this, &ObjectViewer::refresh);
60  action = m_menu.addAction(QStringLiteral("Dump Windows"));
61  connect(action, &QAction::triggered, this, &ObjectViewer::dumpWindows);
62 
63  action = m_menu.addAction(QStringLiteral("Update"));
64  connect(action, &QAction::triggered, this, &ObjectViewer::updateSelectedWidget);
65 
66  action = m_menu.addAction(QStringLiteral("Print to png"));
67  connect(action, &QAction::triggered, this, &ObjectViewer::dumpSelectedWidgetToPng);
68 
69  action = m_menu.addAction(QStringLiteral("Toggle Visible"));
70  connect(action, &QAction::triggered, this, &ObjectViewer::toggleVisible);
71 
72 #ifdef Q_OS_WIN
73  action = m_menu.addAction(QStringLiteral("Send WM_NCHITTEST"));
74  connect(action, &QAction::triggered, this, &ObjectViewer::sendHitTest);
75 #endif
76 
77  refresh();
78  setWindowTitle(QStringLiteral("ObjectViewer"));
79 }
80 
81 void ObjectViewer::refresh()
82 {
83  const auto hashCopy = m_itemMap;
84  for (auto it = hashCopy.cbegin(), e = hashCopy.cend(); it != e; ++it)
85  remove(it.key());
86 
87  m_model.clear();
88 
89  const auto &topLevelWidgets = qApp->topLevelWidgets();
90  for (QWidget *window : topLevelWidgets) {
91  add(window, m_model.invisibleRootItem());
92  }
93 }
94 
95 void ObjectViewer::dumpSelectedWidgetToPng()
96 {
97  if (auto w = selectedWidget()) {
98  QPixmap px(w->size());
99  w->render(&px);
100  px.save(QStringLiteral("px.png"));
101  qDebug() << QDir::currentPath();
102  }
103 }
104 
105 void ObjectViewer::updateSelectedWidget()
106 {
107  if (auto w = selectedWidget())
108  w->update();
109 }
110 
111 void ObjectViewer::toggleVisible()
112 {
113  if (auto w = selectedWidget())
114  w->setVisible(!w->isVisible());
115 }
116 
117 #ifdef Q_OS_WIN
118 void ObjectViewer::sendHitTest()
119 {
120  if (auto w = selectedWidget()) {
121  qDebug() << "Sending hit test to" << w;
122  ::SendMessage(HWND(w->winId()), WM_NCHITTEST, 0, 0);
123  }
124 }
125 #endif
126 
127 void ObjectViewer::dumpWindows()
128 {
129  qDebug() << "Top Level QWidgets:";
130  const auto &topLevelWidgets = qApp->topLevelWidgets();
131  for (QWidget *w : topLevelWidgets) {
132  if (qobject_cast<QMenu *>(w))
133  continue;
134 
135  qDebug() << " QWidget=" << w;
136  }
137 
138  qDebug() << "Top Level Windows:";
139  const auto &topLevelWindows = qApp->topLevelWindows();
140  for (QWindow *w : topLevelWindows) {
141  qDebug() << " QWindow=" << w << "; parent=" << w->parent() << "; transientParent=" << w->transientParent() << "; hwnd=" << w->winId();
142  }
143 }
144 
145 QString ObjectViewer::nameForObj(QObject *o) const
146 {
148  if (!o->objectName().isEmpty())
149  name += QStringLiteral("(%1)").arg(o->objectName());
150 
151  if (auto w = qobject_cast<QWidget *>(o)) {
152  name += QStringLiteral(" - %1,%2 %3x%4").arg(w->x()).arg(w->y()).arg(w->width()).arg(w->height());
153 
154  if (w->isWindow())
155  name += QStringLiteral(" ;W");
156  if (w->windowHandle() != nullptr)
157  name += QStringLiteral(" ;N");
158  }
159 
160  return name;
161 }
162 
163 void ObjectViewer::add(QObject *obj, QStandardItem *parent)
164 {
165  if (obj == this || obj == &m_menu || obj == parentWidget() || !obj) // Ignore our stuff
166  return;
167 
168  if (m_ignoreMenus && qobject_cast<QMenu *>(obj))
169  return;
170 
171  if (m_ignoreShortcuts && qobject_cast<QShortcut *>(obj))
172  return;
173 
174  if (m_ignoreToolBars && qobject_cast<QToolBar *>(obj))
175  return;
176 
177  connect(obj, &QObject::destroyed, this, &ObjectViewer::remove);
178  obj->installEventFilter(this);
179  auto item = new QStandardItem(nameForObj(obj));
180  item->setData(QVariant::fromValue(obj), ObjRole);
181  m_itemMap.insert(obj, item);
182  parent->appendRow(item);
183  updateItemAppearence(item);
184 
185  for (auto child : obj->children())
186  add(child, item);
187 }
188 
189 void ObjectViewer::remove(QObject *obj)
190 {
191  Q_ASSERT(obj);
192  obj->removeEventFilter(this);
193  m_itemMap.remove(obj);
194 }
195 
196 void ObjectViewer::onSelectionChanged()
197 {
198  QObject *o = selectedObject();
199  if (m_selectedObject == o)
200  return;
201 
202  if (m_selectedObject) {
203  m_selectedObject->removeEventFilter(this);
204  if (auto w = qobject_cast<QWidget *>(m_selectedObject))
205  w->update();
206  }
207 
208  m_selectedObject = o;
209 
210  if (m_selectedObject) {
211  printProperties(o);
212  m_selectedObject->installEventFilter(this);
213  if (m_highlightsWidget) {
214  if (auto w = qobject_cast<QWidget *>(o))
215  w->update();
216  }
217  }
218 }
219 
220 void ObjectViewer::printProperties(QObject *obj) const
221 {
222  qDebug() << "Printing properties for" << obj;
223 
224  auto mo = obj->metaObject();
225  const int count = mo->propertyCount();
226  for (int i = 0; i < count; ++i) {
227  QMetaProperty prop = mo->property(i);
228  qDebug() << " " << prop.name() << prop.read(obj);
229  }
230 
231  if (auto w = qobject_cast<QWidget *>(obj)) {
232  qDebug() << "Is a widget!";
233  qDebug() << "Window=" << w->window();
234  qDebug() << "flags=" << w->windowFlags();
235  qDebug() << "is native?" << (w->windowHandle() != nullptr);
236  }
237 }
238 
239 QObject *ObjectViewer::selectedObject() const
240 {
241  auto indexes = m_treeView.selectionModel()->selectedIndexes();
242  if (indexes.isEmpty())
243  return nullptr;
244  QModelIndex index = indexes.first();
245  QObject *obj = index.data(ObjRole).value<QObject *>();
246  return obj;
247 }
248 
249 QWidget *ObjectViewer::selectedWidget() const
250 {
251  return qobject_cast<QWidget *>(selectedObject());
252 }
253 
254 void ObjectViewer::updateItemAppearence(QStandardItem *item)
255 {
256  Q_ASSERT(item);
257  QWidget *widget = widgetForItem(item);
258  if (!widget)
259  return;
260 
261  if (widget->isVisible()) {
262  item->setForeground(Qt::black);
263  } else {
264  item->setForeground(Qt::gray);
265  }
266 }
267 
268 QObject *ObjectViewer::objectForItem(QStandardItem *item) const
269 {
270  return item->data(ObjRole).value<QObject *>();
271 }
272 
273 QWidget *ObjectViewer::widgetForItem(QStandardItem *item) const
274 {
275  return qobject_cast<QWidget *>(objectForItem(item));
276 }
277 
278 void ObjectViewer::contextMenuEvent(QContextMenuEvent *ev)
279 {
280  m_menu.exec(ev->globalPos());
281 }
282 
283 bool ObjectViewer::eventFilter(QObject *watched, QEvent *event)
284 {
285  auto widget = static_cast<QWidget *>(watched);
286  if (event->type() == QEvent::Show || event->type() == QEvent::Hide) {
287  updateItemAppearence(m_itemMap.value(watched));
288  return false;
289  }
290 
291  if (m_selectedObject != watched)
292  return false;
293 
294  if (event->type() != QEvent::Paint || !m_highlightsWidget)
295  return false;
296 
297  QPainter p(widget);
298  p.fillRect(widget->rect(), QBrush(QColor(0, 0, 255, 128)));
299 
300  return true;
301 }
QObject::removeEventFilter
void removeEventFilter(QObject *obj)
QColor
QItemSelectionModel::selectionChanged
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
Qt::UserRole
UserRole
QEvent::Show
Show
QContextMenuEvent::globalPos
const QPoint & globalPos() const const
ObjRole
@ ObjRole
Definition: ObjectViewer.cpp:43
QVariant::fromValue
QVariant fromValue(const T &value)
QMetaProperty
QWidget::rect
rect
QVariant::value
T value() const const
QWindow
QWidget
QStandardItem::setForeground
void setForeground(const QBrush &brush)
QPainter
QObject::destroyed
void destroyed(QObject *obj)
QWidget::isVisible
bool isVisible() const const
QModelIndex::data
QVariant data(int role) const const
QPixmap
QObject
QString
QDir::currentPath
QString currentPath()
QContextMenuEvent
QObject::installEventFilter
void installEventFilter(QObject *filterObj)
QStandardItem
QMetaObject::propertyCount
int propertyCount() const const
QMetaProperty::read
QVariant read(const QObject *object) const const
QBrush
QObject::metaObject
virtual const QMetaObject * metaObject() const const
QMetaObject::className
const char * className() const const
Role
Role
Definition: ObjectViewer.cpp:42
QAction::triggered
void triggered(bool checked)
QMetaProperty::name
const char * name() const const
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString::fromLatin1
QString fromLatin1(const char *str, int size)
QEvent::type
QEvent::Type type() const const
QEvent
QObject::objectName
objectName
QHBoxLayout
QStandardItem::data
virtual QVariant data(int role) const const
QStandardItem::appendRow
void appendRow(const QList< QStandardItem * > &items)
QAction
QModelIndex
Qt::black
black
QObject::children
const QObjectList & children() const const

© 2019-2022 Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
KDDockWidgets
Advanced Dock Widget Framework for Qt
https://www.kdab.com/development-resources/qt-tools/kddockwidgets/
Generated on Mon Mar 7 2022 02:01:20 for KDDockWidgets API Documentation by doxygen 1.8.20