Fraxinus  17.12-rc4
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) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 #include "cxLayoutEditorWidget.h"
33 #include <iostream>
34 #include <QtWidgets>
35 
36 #include "cxTypeConversions.h"
37 #include "cxDefinitionStrings.h"
38 #include "cxUtilHelpers.h"
39 #include "cxViewService.h"
40 #include "cxBoolProperty.h"
41 #include "cxHelperWidgets.h"
42 
43 namespace cx
44 {
45 
46 
48  QWidget(parent),
49  mViewService(viewService)
50 {
51  mTopLayout = new QVBoxLayout(this);
52  QHBoxLayout* nameLayout = new QHBoxLayout;
53  mTopLayout->addLayout(nameLayout);
54  mRCLayout = new QHBoxLayout;
55  mTopLayout->addLayout(mRCLayout);
56  mLayout = new QGridLayout;
57  mLayout->setMargin(0);
58  mLayout->setSpacing(2);
59  mTopLayout->addLayout(mLayout);
60 
61  mNameEdit = new QLineEdit;
62  connect(mNameEdit, SIGNAL(editingFinished()), this, SLOT(nameChanged()));
63  nameLayout->addWidget(new QLabel("Name"));
64  nameLayout->addWidget(mNameEdit);
65 
66 
67  // create the row/column bar
68  mRowsEdit = new QSpinBox;
69  mRowsEdit->setRange(1,LayoutData::MaxGridSize);
70  mColsEdit = new QSpinBox;
71  mColsEdit->setRange(1,LayoutData::MaxGridSize);
72  connect(mRowsEdit, SIGNAL(valueChanged(int)), this, SLOT(rowsColumnsChangedSlot()));
73  connect(mColsEdit, SIGNAL(valueChanged(int)), this, SLOT(rowsColumnsChangedSlot()));
74  mRCLayout->addWidget(new QLabel("Rows"));
75  mRCLayout->addWidget(mRowsEdit);
76  mRCLayout->addWidget(new QLabel("Columns"));
77  mRCLayout->addWidget(mColsEdit);
78  mRCLayout->addStretch();
79 
80  for (int i=ptNOPLANE+1; i<ptCOUNT; ++i)
81  {
82  PLANE_TYPE type = static_cast<PLANE_TYPE>(i);
83  mPlaneNames[type] = qstring_cast(type);
84  mViewNames.push_back(ViewNamesType(type,View::VIEW_2D, qstring_cast(type)));
85  }
86  mViewNames.push_back(ViewNamesType(ptNOPLANE,View::VIEW_3D, "3D"));
87  mViewNames.push_back(ViewNamesType(ptNOPLANE,View::VIEW_REAL_TIME, "RT"));
88 // mPlaneNames[ptNOPLANE] = "3D";
89 // mPlaneNames[static_cast<PLANE_TYPE>(-1)] = "RT";
90 
91  mSelection = LayoutRegion(-1,-1);
92  initCache();
93 
94  mOffScreenRendering = BoolProperty::initialize("Offscreeen Render", "",
95  "Render the layout to memory only.\n"
96  "This will cause the displayed area to be black,\n"
97  "but the application can access the rendering programatically.",
98  false);
99  mTopLayout->addWidget(sscCreateDataWidget(this, mOffScreenRendering));
100  connect(mOffScreenRendering.get(), &BoolProperty::changed, this, &LayoutEditorWidget::onOffScreenRenderingChanged);
101 
102 
103  this->updateGrid();
104 }
105 
107 {
108  mViewData = data;
109  this->updateGrid();
110 }
111 
113 {
114  return mViewData;
115 }
116 
117 void LayoutEditorWidget::onOffScreenRenderingChanged()
118 {
119  mViewData.setOffScreenRendering(mOffScreenRendering->getValue());
120 }
121 
122 void LayoutEditorWidget::nameChanged()
123 {
124  mViewData.setName(mNameEdit->text());
125 }
126 
127 void LayoutEditorWidget::contextMenuSlot(const QPoint& point)
128 {
129  //QWidget* sender = dynamic_cast<QWidget*>(this->sender());
130  QPoint pointGlobal = this->mapToGlobal(point);
131  QMenu menu(this);
132 
133  LayoutViewData viewData = this->getViewData(point);
134 
135  QAction* mergeAction = new QAction("merge view", &menu);
136  mergeAction->setEnabled(this->getSelectedViews().size()>1);
137  connect(mergeAction, SIGNAL(triggered()), this, SLOT(mergeActionSlot()));
138  menu.addAction(mergeAction);
139 
140  QAction* splitAction = new QAction("split view", &menu);
141  splitAction->setEnabled(mSelection.span.row!=1 || mSelection.span.col!=1);
142  connect(splitAction, SIGNAL(triggered()), this, SLOT(splitActionSlot()));
143  menu.addAction(splitAction);
144 
145  menu.addSeparator();
146 
147  // actions for view group
148 // int viewGroupCount = static_cast<int>(viewService()->getViewGroups().size());
149  int viewGroupCount = mViewService->groupCount();
150  QActionGroup* groupActions = new QActionGroup(this);
151  for (int i=0; i<viewGroupCount; ++i)
152  {
153  QAction* action = new QAction(QString("Group %1").arg(i), groupActions);
154  action->setData(QVariant(i));
155  action->setCheckable(true);
156  connect(action, SIGNAL(triggered()), this, SLOT(groupActionSlot()));
157  action->setChecked(viewData.mGroup==i);
158 // menu.addAction(action);
159  }
160 
161  //menu.addMenu("View Group")->addActions(groupActions->actions());
162  menu.addActions(groupActions->actions());
163  menu.addSeparator();
164 
165  // actions for view type
166  QActionGroup* typeActions = new QActionGroup(this);
167 // for (std::map<PLANE_TYPE, QString>::iterator iter=mPlaneNames.begin(); iter!=mPlaneNames.end(); ++iter)
168  for (unsigned i=0; i<mViewNames.size(); ++i)
169  {
170  ViewNamesType current = mViewNames[i];
171 // PLANE_TYPE type = static_cast<PLANE_TYPE>(i);
172 // PLANE_TYPE type = iter->first;
173 // QString name = iter->second;
174 
175  QAction* action = new QAction(QString("%1").arg(current.mName), typeActions);
176 // action->setData(QVariant(t));
177  action->setCheckable(true);
178  connect(action, SIGNAL(triggered()), this, SLOT(typeActionSlot()));
179  action->setChecked(viewData.mPlane==current.mPlane && viewData.mType==current.mView);
180  }
181 // for (int i=ptNOPLANE; i<ptCOUNT; ++i)
182 // {
183 // PLANE_TYPE type = static_cast<PLANE_TYPE>(i);
184 //
185 // QAction* action = new QAction(QString("%1").arg(mPlaneNames[type]), typeActions);
186 // action->setData(QVariant(i));
187 // action->setCheckable(true);
188 // connect(action, SIGNAL(triggered()), this, SLOT(typeActionSlot()));
189 // action->setChecked(viewData.mPlane==type);
190 // //menu.addAction(action);
191 // }
192 
193  //menu.addMenu("View Plane Type")->addActions(typeActions->actions());
194  menu.addActions(typeActions->actions());
195 
196  menu.exec(pointGlobal);
197 }
198 
199 void LayoutEditorWidget::splitActionSlot()
200 {
201  mViewData.split(mSelection);
202  this->updateGrid();
203 }
204 
205 void LayoutEditorWidget::mergeActionSlot()
206 {
207  mViewData.merge(mSelection);
208  this->updateGrid();
209 }
210 
211 void LayoutEditorWidget::groupActionSlot()
212 {
213  QAction* sender = dynamic_cast<QAction*>(this->sender());
214  if (!sender)
215  return;
216  int group = sender->data().toInt();
217 
218  std::set<LayoutData::iterator> selection = this->getSelectedViews();
219  for (std::set<LayoutData::iterator>::iterator iter=selection.begin(); iter!=selection.end(); ++iter)
220  (*iter)->mGroup = group;
221 
222  this->updateGrid();
223 }
224 
225 void LayoutEditorWidget::typeActionSlot()
226 {
227  QAction* sender = dynamic_cast<QAction*>(this->sender());
228  if (!sender)
229  return;
230 // PLANE_TYPE type = static_cast<PLANE_TYPE>(sender->data().toInt());
231  ViewNamesType type;
232  for (unsigned i=0; i<mViewNames.size(); ++i)
233  if (mViewNames[i].mName == sender->text())
234  type=mViewNames[i];
235 
236  std::set<LayoutData::iterator> selection = this->getSelectedViews();
237  for (std::set<LayoutData::iterator>::iterator iter=selection.begin(); iter!=selection.end(); ++iter)
238  {
239  (*iter)->mPlane = type.mPlane;
240  (*iter)->mType = type.mView;
241  }
242 
243  this->updateGrid();
244 }
245 
246 void LayoutEditorWidget::mouseMoveEvent(QMouseEvent* event)
247 {
248  this->updateSelection(event->pos());
249 }
250 
251 void LayoutEditorWidget::updateSelection(QPoint pos)
252 {
253  LayoutViewData start = this->getViewData(mClickPos);
254  LayoutViewData stop = this->getViewData(pos);
255  mSelection = merge(start.mRegion, stop.mRegion);
256  this->colorRegion(mSelection, "dimgrey", "lightgrey");
257 }
258 
259 /* Return a set of unique iterators into the layout data,
260  * representing the selected region.
261  */
262 std::set<LayoutData::iterator> LayoutEditorWidget::getSelectedViews()
263 {
264  std::set<LayoutData::iterator> retval;
265  for (int r=mSelection.pos.row; r<mSelection.pos.row+mSelection.span.row; ++r)
266  for (int c=mSelection.pos.col; c<mSelection.pos.col+mSelection.span.col; ++c)
267  retval.insert(mViewData.find(LayoutPosition(r,c)));
268  return retval;
269 }
270 
271 void LayoutEditorWidget::mousePressEvent(QMouseEvent* event)
272 {
273  mClickPos = event->pos();
274 
275  if (event->button()==Qt::RightButton)
276  {
277  // reselect if click is outside old selection
278  if (!mSelection.contains(this->getViewData(mClickPos).mRegion.pos))
279  this->updateSelection(event->pos());
280 
281  //std::cout << "mouse press context" << std::endl;
282  this->contextMenuSlot(event->pos());
283  }
284  else
285  {
286  //std::cout << "mouse press clean" << std::endl;
287  this->updateSelection(event->pos());
288  }
289 }
290 
291 void LayoutEditorWidget::colorRegion(LayoutRegion region, QString selectColor, QString backColor)
292 {
293  for (LayoutData::iterator iter=mViewData.begin(); iter!=mViewData.end(); ++iter)
294  {
295  LayoutPosition pos = iter->mRegion.pos;
296  QString color;
297 
298  if (region.contains(pos))
299  color = selectColor;
300  else
301  color = backColor;
302 
303  mViewDataCache[pos.row][pos.col].mFrame->setStyleSheet(QString("QFrame { background-color: %1 }").arg(color));
304  }
305 }
306 
310 LayoutViewData LayoutEditorWidget::getViewData(QPoint pt)
311 {
312  for (LayoutData::iterator iter=mViewData.begin(); iter!=mViewData.end(); ++iter)
313  {
314  LayoutPosition pos = iter->mRegion.pos;
315  if (!mViewDataCache[pos.row][pos.col].mFrame->geometry().contains(pt))
316  continue;
317 
318  return *iter;
319  }
320 
321  return LayoutViewData();
322 }
323 
327 void LayoutEditorWidget::rowsColumnsChangedSlot()
328 {
329  mViewData.resize(mRowsEdit->value(), mColsEdit->value());
330  this->setSaneGroupIDs();
331  this->updateGrid();
332 }
333 
334 QString LayoutEditorWidget::getViewName(LayoutViewData data) const
335 {
336  for (unsigned i=0; i<mViewNames.size(); ++i)
337  {
338  if (mViewNames[i].mPlane==data.mPlane && mViewNames[i].mView==data.mType)
339  return mViewNames[i].mName;
340  }
341  return "NA";
342 }
343 
347 void LayoutEditorWidget::setSaneGroupIDs()
348 {
349  for (LayoutData::iterator iter=mViewData.begin(); iter!=mViewData.end(); ++iter)
350  {
351  if (iter->mGroup<0)
352  iter->mGroup = 0;
353  }
354 }
355 
360 void LayoutEditorWidget::updateGrid()
361 {
362  //std::cout << "pre update:" << streamXml2String(mViewData) << std::endl;
363 
364  this->clearDisplay();
365 
366  //std::cout << "update start" << std::endl;
367  for (LayoutData::iterator iter=mViewData.begin(); iter!=mViewData.end(); ++iter)
368  {
369  LayoutRegion region = iter->mRegion;
370  GridElement gridData = mViewDataCache[region.pos.row][region.pos.col];
371 
372  // add and show frame in correct position
373  mLayout->addWidget(gridData.mFrame, region.pos.row, region.pos.col, region.span.row, region.span.col);
374  gridData.mFrame->show();
375 
376  // set view text
377  QString name = this->getViewName(*iter);
378  if (iter->mGroup<0 && name.isEmpty())
379  gridData.mLabel->setText("NA");
380  else
381  gridData.mLabel->setText(QString("%1/%2").arg(iter->mGroup).arg(name));
382  }
383 
384  mNameEdit->setText(mViewData.getName());
385  mOffScreenRendering->setValue(mViewData.getOffScreenRendering());
386 // mRowsEdit->setText(qstring_cast(mViewData.size().row));
387 // mColsEdit->setText(qstring_cast(mViewData.size().col));
388 
389  mRowsEdit->blockSignals(true);
390  mRowsEdit->setValue(mViewData.size().row);
391  mRowsEdit->blockSignals(false);
392 
393  mColsEdit->blockSignals(true);
394  mColsEdit->setValue(mViewData.size().col);
395  mColsEdit->blockSignals(false);
396 
397 // this->colorRegion(LayoutRegion(-1,-1,1,1), "lightgrey", "lightgrey");
398  this->colorRegion(mSelection, "dimgrey", "lightgrey");
399 
400  //this->updateGeometry();
401  //QSize msize = mLayout->minimumSize();
402 // QSize rsize = mGridWidget->size();
403  //std::cout << "minsize: " << msize.width() << "," << msize.height() << std::endl;
404 // std::cout << "real size: " << rsize.width() << "," << rsize.height() << std::endl;
405 
406 // QString color("QFrame { background-color: red }");
407 // mViewData[0][0].mFrame->setStyleSheet(color);
408 
409 // mGridWidget->resize(msize);
410 // this->resize(400,700);
411 // QTimer::singleShot(0, this, SLOT(setNiceSize()));
412 
413  // std::cout << "post update:" << streamXml2String(mViewData) << std::endl;
414 
415 }
416 
417 //void LayoutEditorWidget::setNiceSize()
418 //{
419 // //this->resize(400,700);
420 //}
421 
422 void LayoutEditorWidget::clearDisplay()
423 {
424  for (unsigned r = 0; r < mViewDataCache.size(); ++r)
425  {
426  for (unsigned c = 0; c < mViewDataCache[r].size(); ++c)
427  {
428  mViewDataCache[r][c].mFrame->hide();
429  }
430  }
431 }
432 
433 void LayoutEditorWidget::initCache()
434 {
435  int maxRows = LayoutData::MaxGridSize;
436  int maxCols = LayoutData::MaxGridSize;
437  mViewDataCache.resize(maxRows);
438 
439  for (int r = 0; r < maxRows; ++r)
440  {
441  mViewDataCache[r].resize(maxCols);
442 
443  for (int c = 0; c < maxCols; ++c)
444  {
445  QFrame* frame = new QFrame(this);
446  //frame->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
447  frame->setFrameStyle(QFrame::Panel | QFrame::Sunken);
448  frame->setLineWidth(3);
449  frame->setLayout(new QVBoxLayout);
450  QLabel* label = new QLabel("NA", frame);
451  frame->layout()->addWidget(label);
452 
453  mViewDataCache[r][c].mFrame = frame;
454  mViewDataCache[r][c].mLabel = label;
455  }
456  }
457 }
458 
459 
460 } // namespace cx
QString qstring_cast(const T &val)
int mGroup
what group to connect to. -1 means not set.
Definition: cxLayoutData.h:84
static BoolPropertyPtr initialize(const QString &uid, QString name, QString help, bool value, QDomNode root=QDomNode())
ViewDataContainer::iterator iterator
Definition: cxLayoutData.h:101
bool contains(LayoutPosition p) const
Definition: cxLayoutData.h:69
boost::shared_ptr< class ViewService > ViewServicePtr
LayoutPosition span
size of region
Definition: cxLayoutData.h:67
bool merge(LayoutRegion region)
iterator begin()
Definition: cxLayoutData.h:118
void setName(const QString &name)
Definition: cxLayoutData.h:113
PLANE_TYPE mPlane
ptNOPLANE means 3D
Definition: cxLayoutData.h:85
LayoutRegion mRegion
Definition: cxLayoutData.h:87
LayoutData getLayoutData() const
iterator find(LayoutPosition pos)
void split(iterator iter)
void resize(int rows, int cols)
LayoutPosition size() const
Definition: cxLayoutData.h:129
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:56
QString getName() const
Definition: cxLayoutData.h:110
static const int MaxGridSize
Definition: cxLayoutData.h:103
void setLayoutData(const LayoutData &data)
LayoutPosition pos
start position of region
Definition: cxLayoutData.h:66
QWidget * sscCreateDataWidget(QWidget *parent, PropertyPtr data, QGridLayout *gridLayout, int row)
Create a widget capable of displaying the input data.
iterator end()
Definition: cxLayoutData.h:119
View::Type mType
Definition: cxLayoutData.h:86
void setOffScreenRendering(bool val)
Definition: cxLayoutData.h:112
LayoutRegion merge(LayoutRegion a, LayoutRegion b)
LayoutEditorWidget(cx::ViewServicePtr viewService, QWidget *parent)
bool getOffScreenRendering() const
Definition: cxLayoutData.h:111
Namespace for all CustusX production code.