Advertisement
Guest User

Untitled

a guest
Jul 25th, 2017
123
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.05 KB | None | 0 0
  1. QString data = "{"
  2. ""test" : ["this", "is", "a", "
  3. "{"test" : ["with", "nested", "items"]}],"
  4. ""types" : [1337, 13.37, true, null]"
  5. "}";
  6. QJsonView *jsonView = new QJsonView(this);
  7. jsonView->setJsonValue(data);
  8.  
  9. jsonView->expand();
  10.  
  11. jsonView->setHoverEffects(true);
  12.  
  13. #ifndef QJSONVIEW_H
  14. #define QJSONVIEW_H
  15.  
  16. #include <QWidget>
  17. #include <QVariant>
  18. #include <QLabel>
  19.  
  20. /**
  21. Widget to display JSON or QVariant data.
  22. This widget will display any JSON-encoded string or a hierarchically nested QVariant tree in an expandable way.
  23. Per default, the whole data gets displayed in one single (non-wrapped) line, which can be expanded using a button
  24. if the JSON / QVariant data is of type JSON-array (QVariantList) or JSON-object (QVariantMap).
  25. */
  26. class QJsonView : public QWidget
  27. {
  28. Q_OBJECT
  29. Q_PROPERTY(bool hoverEffects READ hoverEffects WRITE setHoverEffects);
  30. Q_PROPERTY(bool expandable READ isExpandable);
  31. Q_PROPERTY(bool expanded READ isExpanded WRITE setExpanded);
  32.  
  33.  
  34. public:
  35. /**
  36. Constructor for QJsonView, taking the parent widget as a single argument.
  37. */
  38. explicit QJsonView(QWidget *parent = 0);
  39.  
  40. /**
  41. Static and public helper function returning the HTML code which will be used to visualize the data (by applying syntax highlighting rules).
  42. This function is kept public since you may want to use this to layout some other QVariant data the same way like QJsonView does.
  43. */
  44. static QString variantToHtml(QVariant data);
  45.  
  46.  
  47. signals:
  48. /**
  49. Emitted whenever this widget or one of its children has been expanded or collapsed.
  50. (The signal gets propagated to the root QJsonView object.)
  51. */
  52. void resized();
  53.  
  54.  
  55. public slots:
  56. /**
  57. Set the value to be displayed to a QVariant value. The only supported QVariant-types are Invalid, Bool, Int, LongLong, List, Map. Any other types are untested!
  58. */
  59. void setValue(QVariant value);
  60.  
  61. /**
  62. Set the value to be displayed to a JSON serialized string, which will be decoded before being viewed.
  63. */
  64. void setJsonValue(QString json);
  65.  
  66. /**
  67. Enables or disables hover effects.
  68. */
  69. void setHoverEffects(bool enabled = true);
  70.  
  71. /**
  72. Returns true if hover effects are enabled.
  73. */
  74. bool hoverEffects();
  75.  
  76. /**
  77. Returns true if this QJsonView is expandable.
  78. This is the case for JSON-objects and JSON-arrays having at least one entry.
  79. */
  80. bool isExpandable();
  81.  
  82. /**
  83. Returns true if this QJsonView is currently expanded.
  84. */
  85. bool isExpanded();
  86.  
  87. /**
  88. Expands or collapses this view (convenient slot for expand() or collapse(), depending on the argument).
  89. */
  90. void setExpanded(bool expanded);
  91.  
  92. /**
  93. Expands this view if it is expandable and not expanded.
  94. */
  95. void expand();
  96.  
  97. /**
  98. Collapses this view if it is expanded.
  99. */
  100. void collapse();
  101.  
  102.  
  103. protected:
  104. /**
  105. reimp
  106. */
  107. void mousePressEvent(QMouseEvent *);
  108. /**
  109. reimp
  110. */
  111. void paintEvent(QPaintEvent *);
  112. /**
  113. reimp
  114. */
  115. void contextMenuEvent(QContextMenuEvent *);
  116. /**
  117. reimp
  118. */
  119. void enterEvent(QEvent *);
  120. /**
  121. reimp
  122. */
  123. void leaveEvent(QEvent *);
  124.  
  125. /**
  126. Called by a child in order to inform this widget that the mouse cursor is now over the child instead of this widget.
  127. */
  128. void childEntered();
  129.  
  130. /**
  131. Called by a child in order to inform this widget that the mouse cursor isn't over the child anymore.
  132. */
  133. void childLeaved();
  134.  
  135.  
  136.  
  137.  
  138. private:
  139. // value to be displayed, as a QVariant
  140. QVariant v;
  141. // if this is no container type, this points to the QLabel representing the single value
  142. QLabel *lblSingle;
  143. // if this is a container type, these point to child widgets
  144. QList<QWidget*> childWidgets;
  145. // true if this is a container type and is currently in expanded view
  146. bool expanded;
  147. // true if hover effects are enabled
  148. bool hoverEffectsEnabled;
  149.  
  150. // apply hover effect
  151. void hover();
  152. // revert hover effect
  153. void unhover();
  154. };
  155.  
  156. #endif // QJSONVIEW_H
  157.  
  158. #include "qjsonview.h"
  159. #include "qjson.h"
  160. #include <QGridLayout>
  161. #include <QPainter>
  162. #include <QVariantMap>
  163. #include <QContextMenuEvent>
  164. #include <QMenu>
  165. #include <QClipboard>
  166. #include <QApplication>
  167. #include <QMouseEvent>
  168. #include <QTextDocument>
  169. #include <QDebug>
  170. #include <QToolTip>
  171.  
  172.  
  173. #define EXPANDABLE_MARGIN_LEFT 14
  174. #define EXPANDED_MARGIN_LEFT 21
  175.  
  176.  
  177. QJsonView::QJsonView(QWidget *parent) :
  178. QWidget(parent),
  179. lblSingle(new QLabel(this)),
  180. expanded(false),
  181. hoverEffectsEnabled(false)
  182. {
  183. //needed for hover effects
  184. setAutoFillBackground(true);
  185.  
  186. QGridLayout *layout = new QGridLayout;
  187. layout->setContentsMargins(0, 0, 0, 0);
  188. layout->setSpacing(0);
  189. setLayout(layout);
  190.  
  191. //default: show one single QLabel with the whole value as its content
  192. layout->addWidget(lblSingle);
  193. lblSingle->setAutoFillBackground(true);
  194. lblSingle->setCursor(Qt::ArrowCursor);
  195. setValue(QVariant());
  196. }
  197.  
  198. void QJsonView::setValue(QVariant value)
  199. {
  200. if(expanded) collapse();
  201.  
  202. v = value;
  203. lblSingle->setText(QString("<span style="font-family: monospace; overflow: hidden">%1</span>")
  204. .arg(variantToHtml(v)));
  205. layout()->setContentsMargins(isExpandable() ? EXPANDABLE_MARGIN_LEFT : 0, 0, 0, 0);
  206.  
  207. //show hand cursor if expandable
  208. Qt::CursorShape cursor;
  209. if(isExpandable())
  210. cursor = Qt::PointingHandCursor;
  211. else
  212. cursor = Qt::ArrowCursor;
  213. setCursor(cursor);
  214. lblSingle->setCursor(cursor);
  215.  
  216. update();
  217. emit resized();
  218. }
  219.  
  220. void QJsonView::setJsonValue(QString json)
  221. {
  222. setValue(QJson::decode(json));
  223. }
  224.  
  225. void QJsonView::setHoverEffects(bool enabled)
  226. {
  227. hoverEffectsEnabled = enabled;
  228. if(!hoverEffectsEnabled)
  229. unhover();
  230. }
  231.  
  232. bool QJsonView::hoverEffects()
  233. {
  234. //if my parent is also a QJsonView, return its property
  235. QJsonView *p = qobject_cast<QJsonView*>(parentWidget());
  236. if(p)
  237. return p->hoverEffects();
  238. else
  239. return hoverEffectsEnabled;
  240. }
  241.  
  242. QString QJsonView::variantToHtml(QVariant data)
  243. {
  244. if(data.type() == QVariant::String || data.type() == QVariant::ByteArray)
  245. return "<span style="color: #006000">"" + Qt::escape(data.toString()) + ""</span>";
  246. else if(data.type() == QVariant::Int || data.type() == QVariant::LongLong)
  247. return "<span style="color: #800000">" + Qt::escape(data.toString()) + "</span>";
  248. else if(data.type() == QVariant::Double)
  249. return "<span style="color: #800080">" + Qt::escape(data.toString()) + "</span>";
  250. else if(data.type() == QVariant::Bool || data.isNull() || !data.isValid())
  251. {
  252. QString str = "null";
  253. if(data.type() == QVariant::Bool)
  254. str = data.toBool() ? "true" : "false";
  255. return "<span style="color: #000080">" + str + "</span>";
  256. }
  257.  
  258. else if(data.type() == QVariant::List)
  259. {
  260. QString str = "<span style="color: #606060"><b>[</b></span>";
  261. bool first = true;
  262. foreach(QVariant e, data.toList())
  263. {
  264. if(!first)
  265. str += "<span style="color: #606060"><b>, </b></span>";
  266. first = false;
  267. str += variantToHtml(e);
  268. }
  269. str += "<span style="color: #606060"><b>]</b></span>";
  270. return str;
  271. }
  272.  
  273. else if(data.type() == QVariant::Map)
  274. {
  275. QString str = "<span style="color: #606060"><b>{</b></span>";
  276. QVariantMap map(data.toMap());
  277.  
  278. //special entry: "children" => tree view
  279. bool containsChildren = false;
  280. QVariant children;
  281. if(map.contains("children")) {
  282. children = map.take("children");
  283. containsChildren = true;
  284. }
  285.  
  286. //normal entries
  287. QVariantMap::iterator i;
  288. for(i = map.begin(); i != map.end(); ++i)
  289. {
  290. if(i != map.begin())
  291. str += "<span style="color: #606060"><b>, </b></span>";
  292. str += Qt::escape(i.key()) + ": " + variantToHtml(i.value());
  293. }
  294. //entry "children"
  295. if(containsChildren) {
  296. if(!map.isEmpty())
  297. str += "<span style="color: #606060"><b>, </b></span>";
  298. str += "children: " + variantToHtml(children);
  299. }
  300.  
  301. str += "<span style="color: #606060"><b>}</b></span>";
  302. return str;
  303. }
  304.  
  305. else
  306. return data.toString();
  307. }
  308.  
  309. void QJsonView::paintEvent(QPaintEvent *)
  310. {
  311. QPainter p(this);
  312.  
  313. // i designed the graphics using a pixel font size of 15, so this should be scalable now.
  314. qreal scale = fontMetrics().height() / 15.0;
  315. p.scale(scale, scale);
  316.  
  317. int h = height() / scale;
  318.  
  319. p.drawRect(2, 2, 10, 10);
  320. p.drawLine(5, 7, 9, 7);
  321. if(!expanded)
  322. p.drawLine(7, 5, 7, 9);
  323.  
  324. if(expanded)
  325. {
  326. QColor color(96, 96, 96);
  327. if(v.type() == QVariant::List)
  328. {
  329. p.fillRect(16, 2, 4, 1, color);
  330. p.fillRect(16, 3, 2, h - 6, color);
  331. p.fillRect(16, h - 3, 4, 1, color);
  332. }
  333. else
  334. {
  335. int mid = h / 2;
  336. p.fillRect(18, 2, 4, 1, color);
  337. p.fillRect(17, 3, 2, mid - 4, color);
  338. p.fillRect(16, mid - 1, 3, 1, color);
  339. p.fillRect(15, mid , 3, 1, color);
  340. p.fillRect(16, mid + 1, 3, 1, color);
  341. p.fillRect(17, mid + 2, 2, h - mid - 5, color);
  342. p.fillRect(18, h - 3, 4, 1, color);
  343. }
  344. }
  345. }
  346.  
  347. void QJsonView::mousePressEvent(QMouseEvent *e)
  348. {
  349. if(isExpandable()
  350. && e->button() == Qt::LeftButton
  351. && (!expanded || e->x() < EXPANDED_MARGIN_LEFT))
  352. {
  353. if(!expanded)
  354. expand();
  355. else
  356. collapse();
  357. }
  358. }
  359.  
  360. void QJsonView::contextMenuEvent(QContextMenuEvent *e)
  361. {
  362. QMenu menu(this);
  363.  
  364. //copy value to clipboard
  365. QAction *copy;
  366. if(v.type() == QVariant::List || v.type() == QVariant::Map)
  367. copy = menu.addAction(tr("Copy value (JSON encoded)"));
  368. else if(v.type() == QVariant::String || v.type() == QVariant::ByteArray)
  369. copy = menu.addAction(tr("Copy string value"));
  370. else
  371. copy = menu.addAction(tr("Copy value"));
  372.  
  373. //execute menu
  374. QAction *triggeredAction = menu.exec(e->globalPos());
  375.  
  376. if(triggeredAction == copy)
  377. {
  378. QClipboard *clipboard = QApplication::clipboard();
  379. if(v.type() == QVariant::List || v.type() == QVariant::Map || v.type() == QVariant::Bool || v.isNull() || !v.isValid())
  380. clipboard->setText(QJson::encode(v, QJson::EncodeOptions(QJson::Compact | QJson::EncodeUnknownTypesAsNull)));
  381. else
  382. clipboard->setText(v.toString());
  383. }
  384. }
  385.  
  386. void QJsonView::enterEvent(QEvent *)
  387. {
  388. hover();
  389.  
  390. //if my parent is also a QJsonView, i inform it that i have been entered
  391. QJsonView *p = qobject_cast<QJsonView*>(parentWidget());
  392. if(p) p->childEntered();
  393. }
  394.  
  395. void QJsonView::leaveEvent(QEvent *)
  396. {
  397. unhover();
  398.  
  399. //if my parent is also a QJsonView, i inform it that i have been leaved
  400. QJsonView *p = qobject_cast<QJsonView*>(parentWidget());
  401. if(p) p->childLeaved();
  402. }
  403.  
  404. bool QJsonView::isExpandable()
  405. {
  406. return (v.type() == QVariant::List && !v.toList().isEmpty()) ||
  407. (v.type() == QVariant::Map && !v.toMap().isEmpty());
  408. }
  409.  
  410. bool QJsonView::isExpanded()
  411. {
  412. return expanded;
  413. }
  414.  
  415. void QJsonView::setExpanded(bool expanded)
  416. {
  417. if(expanded)
  418. expand();
  419. else
  420. collapse();
  421. }
  422.  
  423. void QJsonView::expand()
  424. {
  425. if(isExpandable())
  426. {
  427. lblSingle->setVisible(false);
  428. layout()->removeWidget(lblSingle);
  429.  
  430. if(v.type() == QVariant::List)
  431. {
  432. foreach(QVariant e, v.toList())
  433. {
  434. QJsonView *w = new QJsonView(this);
  435. w->setValue(e);
  436. layout()->addWidget(w);
  437. childWidgets << w;
  438. //propagate signals to parent
  439. connect(w, SIGNAL(resized()), SIGNAL(resized()));
  440. }
  441. }
  442. else if(v.type() == QVariant::Map)
  443. {
  444. QVariantMap map(v.toMap());
  445.  
  446. //normal entries
  447. QVariantMap::iterator i;
  448. int index = 0;
  449. QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
  450. sizePolicy.setHorizontalStretch(0);
  451. sizePolicy.setVerticalStretch(0);
  452. for(i = map.begin(); i != map.end(); ++i)
  453. {
  454. QLabel *k = new QLabel(this);
  455. k->setText("<span style="font-family: monospace">" + Qt::escape(i.key()) + ": </span>");
  456. k->setAlignment(Qt::AlignTop | Qt::AlignLeft);
  457. k->setCursor(Qt::ArrowCursor);
  458. k->setAutoFillBackground(true);
  459. ((QGridLayout*)layout())->addWidget(k, index, 0);
  460. childWidgets << k;
  461.  
  462. QJsonView *w = new QJsonView(this);
  463. w->setValue(i.value());
  464. ((QGridLayout*)layout())->addWidget(w, index, 1);
  465. w->setSizePolicy(sizePolicy);
  466. childWidgets << w;
  467. //propagate signals to parent
  468. connect(w, SIGNAL(resized()), SIGNAL(resized()));
  469.  
  470. index++;
  471. }
  472. }
  473.  
  474. layout()->setContentsMargins(EXPANDED_MARGIN_LEFT, 0, 0, 0);
  475. expanded = true;
  476. update();
  477. emit resized();
  478. }
  479. }
  480.  
  481. void QJsonView::collapse()
  482. {
  483. if(isExpandable())
  484. {
  485. foreach(QWidget *w, childWidgets)
  486. {
  487. w->deleteLater();
  488. layout()->removeWidget(w);
  489. }
  490. childWidgets.clear();
  491.  
  492. lblSingle->setVisible(true);
  493. layout()->addWidget(lblSingle);
  494.  
  495. layout()->setContentsMargins(isExpandable() ? EXPANDABLE_MARGIN_LEFT : 0, 0, 0, 0);
  496. expanded = false;
  497. update();
  498. emit resized();
  499. }
  500. }
  501.  
  502. void QJsonView::childEntered()
  503. {
  504. unhover();
  505. }
  506.  
  507. void QJsonView::childLeaved()
  508. {
  509. hover();
  510. }
  511.  
  512. void QJsonView::hover()
  513. {
  514. if(hoverEffects())
  515. {
  516. QPalette pal = palette();
  517. pal.setColor(backgroundRole(), Qt::white);
  518. setPalette(pal);
  519. }
  520. }
  521.  
  522. void QJsonView::unhover()
  523. {
  524. setPalette(parentWidget()->palette());
  525. }
  526.  
  527. bool hoverEffects();
  528.  
  529. bool hoverEffects() const;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement