Fraxinus  18.10
An IGT application
cxLayoutEditorWidget.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 #include "cxLayoutEditorWidget.h"
12 #include <iostream>
13 #include <QtWidgets>
14 
15 #include "cxTypeConversions.h"
16 #include "cxDefinitionStrings.h"
17 #include "cxUtilHelpers.h"
18 #include "cxViewService.h"
19 #include "cxBoolProperty.h"
20 #include "cxHelperWidgets.h"
21 
22 namespace cx
23 {
24 
25 
27  QWidget(parent),
28  mViewService(viewService)
29 {
30  mTopLayout = new QVBoxLayout(this);
31  QHBoxLayout* nameLayout = new QHBoxLayout;
32  mTopLayout->addLayout(nameLayout);
33  mRCLayout = new QHBoxLayout;
34  mTopLayout->addLayout(mRCLayout);
35  mLayout = new QGridLayout;
36  mLayout->setMargin(0);
37  mLayout->setSpacing(2);
38  mTopLayout->addLayout(mLayout);
39 
40  mNameEdit = new QLineEdit;
41  connect(mNameEdit, SIGNAL(editingFinished()), this, SLOT(nameChanged()));
42  nameLayout->addWidget(new QLabel("Name"));
43  nameLayout->addWidget(mNameEdit);
44 
45 
46  // create the row/column bar
47  mRowsEdit = new QSpinBox;
48  mRowsEdit->setRange(1,LayoutData::MaxGridSize);
49  mColsEdit = new QSpinBox;
50  mColsEdit->setRange(1,LayoutData::MaxGridSize);
51  connect(mRowsEdit, SIGNAL(valueChanged(int)), this, SLOT(rowsColumnsChangedSlot()));
52  connect(mColsEdit, SIGNAL(valueChanged(int)), this, SLOT(rowsColumnsChangedSlot()));
53  mRCLayout->addWidget(new QLabel("Rows"));
54  mRCLayout->addWidget(mRowsEdit);
55  mRCLayout->addWidget(new QLabel("Columns"));
56  mRCLayout->addWidget(mColsEdit);
57  mRCLayout->addStretch();
58 
59  for (int i=ptNOPLANE+1; i<ptCOUNT; ++i)
60  {
61  PLANE_TYPE type = static_cast<PLANE_TYPE>(i);
62  mPlaneNames[type] = qstring_cast(type);
63  mViewNames.push_back(ViewNamesType(type,View::VIEW_2D, qstring_cast(type)));
64  }
65  mViewNames.push_back(ViewNamesType(ptNOPLANE,View::VIEW_3D, "3D"));
66  mViewNames.push_back(ViewNamesType(ptNOPLANE,View::VIEW_REAL_TIME, "RT"));
67 // mPlaneNames[ptNOPLANE] = "3D";
68 // mPlaneNames[static_cast<PLANE_TYPE>(-1)] = "RT";
69 
70  mSelection = LayoutRegion(-1,-1);
71  initCache();
72 
73  mOffScreenRendering = BoolProperty::initialize("Offscreeen Render", "",
74  "Render the layout to memory only.\n"
75  "This will cause the displayed area to be black,\n"
76  "but the application can access the rendering programatically.",
77  false);
78  mTopLayout->addWidget(sscCreateDataWidget(this, mOffScreenRendering));
79  connect(mOffScreenRendering.get(), &BoolProperty::changed, this, &LayoutEditorWidget::onOffScreenRenderingChanged);
80 
81 
82  this->updateGrid();
83 }
84 
86 {
87  mViewData = data;
88  this->updateGrid();
89 }
90 
92 {
93  return mViewData;
94 }
95 
96 void LayoutEditorWidget::onOffScreenRenderingChanged()
97 {
98  mViewData.setOffScreenRendering(mOffScreenRendering->getValue());
99 }
100 
101 void LayoutEditorWidget::nameChanged()
102 {
103  mViewData.setName(mNameEdit->text());
104 }
105 
106 void LayoutEditorWidget::contextMenuSlot(const QPoint& point)
107 {
108  //QWidget* sender = dynamic_cast<QWidget*>(this->sender());
109  QPoint pointGlobal = this->mapToGlobal(point);
110  QMenu menu(this);
111 
112  LayoutViewData viewData = this->getViewData(point);
113 
114  QAction* mergeAction = new QAction("merge view", &menu);
115  mergeAction->setEnabled(this->getSelectedViews().size()>1);
116  connect(mergeAction, SIGNAL(triggered()), this, SLOT(mergeActionSlot()));
117  menu.addAction(mergeAction);
118 
119  QAction* splitAction = new QAction("split view", &menu);
120  splitAction->setEnabled(mSelection.span.row!=1 || mSelection.span.col!=1);
121  connect(splitAction, SIGNAL(triggered()), this, SLOT(splitActionSlot()));
122  menu.addAction(splitAction);
123 
124  menu.addSeparator();
125 
126  // actions for view group
127 // int viewGroupCount = static_cast<int>(viewService()->getViewGroups().size());
128  int viewGroupCount = mViewService->groupCount();
129  QActionGroup* groupActions = new QActionGroup(this);
130  for (int i=0; i<viewGroupCount; ++i)
131  {
132  QAction* action = new QAction(QString("Group %1").arg(i), groupActions);
133  action->setData(QVariant(i));
134  action->setCheckable(true);
135  connect(action, SIGNAL(triggered()), this, SLOT(groupActionSlot()));
136  action->setChecked(viewData.mGroup==i);
137 // menu.addAction(action);
138  }
139 
140  //menu.addMenu("View Group")->addActions(groupActions->actions());
141  menu.addActions(groupActions->actions());
142  menu.addSeparator();
143 
144  // actions for view type
145  QActionGroup* typeActions = new QActionGroup(this);
146 // for (std::map<PLANE_TYPE, QString>::iterator iter=mPlaneNames.begin(); iter!=mPlaneNames.end(); ++iter)
147  for (unsigned i=0; i<mViewNames.size(); ++i)
148  {
149  ViewNamesType current = mViewNames[i];
150 // PLANE_TYPE type = static_cast<PLANE_TYPE>(i);
151 // PLANE_TYPE type = iter->first;
152 // QString name = iter->second;
153 
154  QAction* action = new QAction(QString("%1").arg(current.mName), typeActions);
155 // action->setData(QVariant(t));
156  action->setCheckable(true);
157  connect(action, SIGNAL(triggered()), this, SLOT(typeActionSlot()));
158  action->setChecked(viewData.mPlane==current.mPlane && viewData.mType==current.mView);
159  }
160 // for (int i=ptNOPLANE; i<ptCOUNT; ++i)
161 // {
162 // PLANE_TYPE type = static_cast<PLANE_TYPE>(i);
163 //
164 // QAction* action = new QAction(QString("%1").arg(mPlaneNames[type]), typeActions);
165 // action->setData(QVariant(i));
166 // action->setCheckable(true);
167 // connect(action, SIGNAL(triggered()), this, SLOT(typeActionSlot()));
168 // action->setChecked(viewData.mPlane==type);
169 // //menu.addAction(action);
170 // }
171 
172  //menu.addMenu("View Plane Type")->addActions(typeActions->actions());
173  menu.addActions(typeActions->actions());
174 
175  menu.exec(pointGlobal);
176 }
177 
178 void LayoutEditorWidget::splitActionSlot()
179 {
180  mViewData.split(mSelection);
181  this->updateGrid();
182 }
183 
184 void LayoutEditorWidget::mergeActionSlot()
185 {
186  mViewData.merge(mSelection);
187  this->updateGrid();
188 }
189 
190 void LayoutEditorWidget::groupActionSlot()
191 {
192  QAction* sender = dynamic_cast<QAction*>(this->sender());
193  if (!sender)
194  return;
195  int group = sender->data().toInt();
196 
197  std::set<LayoutData::iterator> selection = this->getSelectedViews();
198  for (std::set<LayoutData::iterator>::iterator iter=selection.begin(); iter!=selection.end(); ++iter)
199  (*iter)->mGroup = group;
200 
201  this->updateGrid();
202 }
203 
204 void LayoutEditorWidget::typeActionSlot()
205 {
206  QAction* sender = dynamic_cast<QAction*>(this->sender());
207  if (!sender)
208  return;
209 // PLANE_TYPE type = static_cast<PLANE_TYPE>(sender->data().toInt());
210  ViewNamesType type;
211  for (unsigned i=0; i<mViewNames.size(); ++i)
212  if (mViewNames[i].mName == sender->text())
213  type=mViewNames[i];
214 
215  std::set<LayoutData::iterator> selection = this->getSelectedViews();
216  for (std::set<LayoutData::iterator>::iterator iter=selection.begin(); iter!=selection.end(); ++iter)
217  {
218  (*iter)->mPlane = type.mPlane;
219  (*iter)->mType = type.mView;
220  }
221 
222  this->updateGrid();
223 }
224 
225 void LayoutEditorWidget::mouseMoveEvent(QMouseEvent* event)
226 {
227  this->updateSelection(event->pos());
228 }
229 
230 void LayoutEditorWidget::updateSelection(QPoint pos)
231 {
232  LayoutViewData start = this->getViewData(mClickPos);
233  LayoutViewData stop = this->getViewData(pos);
234  mSelection = merge(start.mRegion, stop.mRegion);
235  this->colorRegion(mSelection, "dimgrey", "lightgrey");
236 }
237 
238 /* Return a set of unique iterators into the layout data,
239  * representing the selected region.
240  */
241 std::set<LayoutData::iterator> LayoutEditorWidget::getSelectedViews()
242 {
243  std::set<LayoutData::iterator> retval;
244  for (int r=mSelection.pos.row; r<mSelection.pos.row+mSelection.span.row; ++r)
245  for (int c=mSelection.pos.col; c<mSelection.pos.col+mSelection.span.col; ++c)
246  retval.insert(mViewData.find(LayoutPosition(r,c)));
247  return retval;
248 }
249 
250 void LayoutEditorWidget::mousePressEvent(QMouseEvent* event)
251 {
252  mClickPos = event->pos();
253 
254  if (event->button()==Qt::RightButton)
255  {
256  // reselect if click is outside old selection
257  if (!mSelection.contains(this->getViewData(mClickPos).mRegion.pos))
258  this->updateSelection(event->pos());
259 
260  //std::cout << "mouse press context" << std::endl;
261  this->contextMenuSlot(event->pos());
262  }
263  else
264  {
265  //std::cout << "mouse press clean" << std::endl;
266  this->updateSelection(event->pos());
267  }
268 }
269 
270 void LayoutEditorWidget::colorRegion(LayoutRegion region, QString selectColor, QString backColor)
271 {
272  for (LayoutData::iterator iter=mViewData.begin(); iter!=mViewData.end(); ++iter)
273  {
274  LayoutPosition pos = iter->mRegion.pos;
275  QString color;
276 
277  if (region.contains(pos))
278  color = selectColor;
279  else
280  color = backColor;
281 
282  mViewDataCache[pos.row][pos.col].mFrame->setStyleSheet(QString("QFrame { background-color: %1 }").arg(color));
283  }
284 }
285 
289 LayoutViewData LayoutEditorWidget::getViewData(QPoint pt)
290 {
291  for (LayoutData::iterator iter=mViewData.begin(); iter!=mViewData.end(); ++iter)
292  {
293  LayoutPosition pos = iter->mRegion.pos;
294  if (!mViewDataCache[pos.row][pos.col].mFrame->geometry().contains(pt))
295  continue;
296 
297  return *iter;
298  }
299 
300  return LayoutViewData();
301 }
302 
306 void LayoutEditorWidget::rowsColumnsChangedSlot()
307 {
308  mViewData.resize(mRowsEdit->value(), mColsEdit->value());
309  this->setSaneGroupIDs();
310  this->updateGrid();
311 }
312 
313 QString LayoutEditorWidget::getViewName(LayoutViewData data) const
314 {
315  for (unsigned i=0; i<mViewNames.size(); ++i)
316  {
317  if (mViewNames[i].mPlane==data.mPlane && mViewNames[i].mView==data.mType)
318  return mViewNames[i].mName;
319  }
320  return "NA";
321 }
322 
326 void LayoutEditorWidget::setSaneGroupIDs()
327 {
328  for (LayoutData::iterator iter=mViewData.begin(); iter!=mViewData.end(); ++iter)
329  {
330  if (iter->mGroup<0)
331  iter->mGroup = 0;
332  }
333 }
334 
339 void LayoutEditorWidget::updateGrid()
340 {
341  //std::cout << "pre update:" << streamXml2String(mViewData) << std::endl;
342 
343  this->clearDisplay();
344 
345  //std::cout << "update start" << std::endl;
346  for (LayoutData::iterator iter=mViewData.begin(); iter!=mViewData.end(); ++iter)
347  {
348  LayoutRegion region = iter->mRegion;
349  GridElement gridData = mViewDataCache[region.pos.row][region.pos.col];
350 
351  // add and show frame in correct position
352  mLayout->addWidget(gridData.mFrame, region.pos.row, region.pos.col, region.span.row, region.span.col);
353  gridData.mFrame->show();
354 
355  // set view text
356  QString name = this->getViewName(*iter);
357  if (iter->mGroup<0 && name.isEmpty())
358  gridData.mLabel->setText("NA");
359  else
360  gridData.mLabel->setText(QString("%1/%2").arg(iter->mGroup).arg(name));
361  }
362 
363  mNameEdit->setText(mViewData.getName());
364  mOffScreenRendering->setValue(mViewData.getOffScreenRendering());
365 // mRowsEdit->setText(qstring_cast(mViewData.size().row));
366 // mColsEdit->setText(qstring_cast(mViewData.size().col));
367 
368  mRowsEdit->blockSignals(true);
369  mRowsEdit->setValue(mViewData.size().row);
370  mRowsEdit->blockSignals(false);
371 
372  mColsEdit->blockSignals(true);
373  mColsEdit->setValue(mViewData.size().col);
374  mColsEdit->blockSignals(false);
375 
376 // this->colorRegion(LayoutRegion(-1,-1,1,1), "lightgrey", "lightgrey");
377  this->colorRegion(mSelection, "dimgrey", "lightgrey");
378 
379  //this->updateGeometry();
380  //QSize msize = mLayout->minimumSize();
381 // QSize rsize = mGridWidget->size();
382  //std::cout << "minsize: " << msize.width() << "," << msize.height() << std::endl;
383 // std::cout << "real size: " << rsize.width() << "," << rsize.height() << std::endl;
384 
385 // QString color("QFrame { background-color: red }");
386 // mViewData[0][0].mFrame->setStyleSheet(color);
387 
388 // mGridWidget->resize(msize);
389 // this->resize(400,700);
390 // QTimer::singleShot(0, this, SLOT(setNiceSize()));
391 
392  // std::cout << "post update:" << streamXml2String(mViewData) << std::endl;
393 
394 }
395 
396 //void LayoutEditorWidget::setNiceSize()
397 //{
398 // //this->resize(400,700);
399 //}
400 
401 void LayoutEditorWidget::clearDisplay()
402 {
403  for (unsigned r = 0; r < mViewDataCache.size(); ++r)
404  {
405  for (unsigned c = 0; c < mViewDataCache[r].size(); ++c)
406  {
407  mViewDataCache[r][c].mFrame->hide();
408  }
409  }
410 }
411 
412 void LayoutEditorWidget::initCache()
413 {
414  int maxRows = LayoutData::MaxGridSize;
415  int maxCols = LayoutData::MaxGridSize;
416  mViewDataCache.resize(maxRows);
417 
418  for (int r = 0; r < maxRows; ++r)
419  {
420  mViewDataCache[r].resize(maxCols);
421 
422  for (int c = 0; c < maxCols; ++c)
423  {
424  QFrame* frame = new QFrame(this);
425  //frame->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
426  frame->setFrameStyle(QFrame::Panel | QFrame::Sunken);
427  frame->setLineWidth(3);
428  frame->setLayout(new QVBoxLayout);
429  QLabel* label = new QLabel("NA", frame);
430  frame->layout()->addWidget(label);
431 
432  mViewDataCache[r][c].mFrame = frame;
433  mViewDataCache[r][c].mLabel = label;
434  }
435  }
436 }
437 
438 
439 } // namespace cx
QString qstring_cast(const T &val)
int mGroup
what group to connect to. -1 means not set.
Definition: cxLayoutData.h:63
static BoolPropertyPtr initialize(const QString &uid, QString name, QString help, bool value, QDomNode root=QDomNode())
ViewDataContainer::iterator iterator
Definition: cxLayoutData.h:80
bool contains(LayoutPosition p) const
Definition: cxLayoutData.h:48
boost::shared_ptr< class ViewService > ViewServicePtr
LayoutPosition span
size of region
Definition: cxLayoutData.h:46
bool merge(LayoutRegion region)
iterator begin()
Definition: cxLayoutData.h:97
void setName(const QString &name)
Definition: cxLayoutData.h:92
PLANE_TYPE mPlane
ptNOPLANE means 3D
Definition: cxLayoutData.h:64
LayoutRegion mRegion
Definition: cxLayoutData.h:66
LayoutData getLayoutData() const
iterator find(LayoutPosition pos)
void split(iterator iter)
void resize(int rows, int cols)
LayoutPosition size() const
Definition: cxLayoutData.h:108
void changed()
emit when the underlying data value is changed: The user interface will be updated.
ptNOPLANE
a initial plane, if no yet set
Definition: cxDefinitions.h:38
QString getName() const
Definition: cxLayoutData.h:89
static const int MaxGridSize
Definition: cxLayoutData.h:82
void setLayoutData(const LayoutData &data)
LayoutPosition pos
start position of region
Definition: cxLayoutData.h:45
QWidget * sscCreateDataWidget(QWidget *parent, PropertyPtr data, QGridLayout *gridLayout, int row)
Create a widget capable of displaying the input data.
iterator end()
Definition: cxLayoutData.h:98
View::Type mType
Definition: cxLayoutData.h:65
void setOffScreenRendering(bool val)
Definition: cxLayoutData.h:91
LayoutRegion merge(LayoutRegion a, LayoutRegion b)
LayoutEditorWidget(cx::ViewServicePtr viewService, QWidget *parent)
bool getOffScreenRendering() const
Definition: cxLayoutData.h:90
Namespace for all CustusX production code.