CustusX  16.12
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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 {
50  mTopLayout = new QVBoxLayout(this);
51  QHBoxLayout* nameLayout = new QHBoxLayout;
52  mTopLayout->addLayout(nameLayout);
53  mRCLayout = new QHBoxLayout;
54  mTopLayout->addLayout(mRCLayout);
55  mLayout = new QGridLayout;
56  mLayout->setMargin(0);
57  mLayout->setSpacing(2);
58  mTopLayout->addLayout(mLayout);
59 
60  mNameEdit = new QLineEdit;
61  connect(mNameEdit, SIGNAL(editingFinished()), this, SLOT(nameChanged()));
62  nameLayout->addWidget(new QLabel("Name"));
63  nameLayout->addWidget(mNameEdit);
64 
65 
66  // create the row/column bar
67  mRowsEdit = new QSpinBox;
68  mRowsEdit->setRange(1,LayoutData::MaxGridSize);
69  mColsEdit = new QSpinBox;
70  mColsEdit->setRange(1,LayoutData::MaxGridSize);
71  connect(mRowsEdit, SIGNAL(valueChanged(int)), this, SLOT(rowsColumnsChangedSlot()));
72  connect(mColsEdit, SIGNAL(valueChanged(int)), this, SLOT(rowsColumnsChangedSlot()));
73  mRCLayout->addWidget(new QLabel("Rows"));
74  mRCLayout->addWidget(mRowsEdit);
75  mRCLayout->addWidget(new QLabel("Columns"));
76  mRCLayout->addWidget(mColsEdit);
77  mRCLayout->addStretch();
78 
79  for (int i=ptNOPLANE+1; i<ptCOUNT; ++i)
80  {
81  PLANE_TYPE type = static_cast<PLANE_TYPE>(i);
82  mPlaneNames[type] = qstring_cast(type);
83  mViewNames.push_back(ViewNamesType(type,View::VIEW_2D, qstring_cast(type)));
84  }
85  mViewNames.push_back(ViewNamesType(ptNOPLANE,View::VIEW_3D, "3D"));
86  mViewNames.push_back(ViewNamesType(ptNOPLANE,View::VIEW_REAL_TIME, "RT"));
87 // mPlaneNames[ptNOPLANE] = "3D";
88 // mPlaneNames[static_cast<PLANE_TYPE>(-1)] = "RT";
89 
90  mSelection = LayoutRegion(-1,-1);
91  initCache();
92 
93  mOffScreenRendering = BoolProperty::initialize("Offscreeen Render", "",
94  "Render the layout to memory only.\n"
95  "This will cause the displayed area to be black,\n"
96  "but the application can access the rendering programatically.",
97  false);
98  mTopLayout->addWidget(sscCreateDataWidget(this, mOffScreenRendering));
99  connect(mOffScreenRendering.get(), &BoolProperty::changed, this, &LayoutEditorWidget::onOffScreenRenderingChanged);
100 
101 
102  this->updateGrid();
103 }
104 
106 {
107  mViewData = data;
108  this->updateGrid();
109 }
110 
112 {
113  return mViewData;
114 }
115 
116 void LayoutEditorWidget::onOffScreenRenderingChanged()
117 {
118  mViewData.setOffScreenRendering(mOffScreenRendering->getValue());
119 }
120 
121 void LayoutEditorWidget::nameChanged()
122 {
123  mViewData.setName(mNameEdit->text());
124 }
125 
126 void LayoutEditorWidget::contextMenuSlot(const QPoint& point)
127 {
128  //QWidget* sender = dynamic_cast<QWidget*>(this->sender());
129  QPoint pointGlobal = this->mapToGlobal(point);
130  QMenu menu(this);
131 
132  LayoutViewData viewData = this->getViewData(point);
133 
134  QAction* mergeAction = new QAction("merge view", &menu);
135  mergeAction->setEnabled(this->getSelectedViews().size()>1);
136  connect(mergeAction, SIGNAL(triggered()), this, SLOT(mergeActionSlot()));
137  menu.addAction(mergeAction);
138 
139  QAction* splitAction = new QAction("split view", &menu);
140  splitAction->setEnabled(mSelection.span.row!=1 || mSelection.span.col!=1);
141  connect(splitAction, SIGNAL(triggered()), this, SLOT(splitActionSlot()));
142  menu.addAction(splitAction);
143 
144  menu.addSeparator();
145 
146  // actions for view group
147 // int viewGroupCount = static_cast<int>(viewService()->getViewGroups().size());
148  int viewGroupCount = viewService()->groupCount();
149  QActionGroup* groupActions = new QActionGroup(this);
150  for (int i=0; i<viewGroupCount; ++i)
151  {
152  QAction* action = new QAction(QString("Group %1").arg(i), groupActions);
153  action->setData(QVariant(i));
154  action->setCheckable(true);
155  connect(action, SIGNAL(triggered()), this, SLOT(groupActionSlot()));
156  action->setChecked(viewData.mGroup==i);
157 // menu.addAction(action);
158  }
159 
160  //menu.addMenu("View Group")->addActions(groupActions->actions());
161  menu.addActions(groupActions->actions());
162  menu.addSeparator();
163 
164  // actions for view type
165  QActionGroup* typeActions = new QActionGroup(this);
166 // for (std::map<PLANE_TYPE, QString>::iterator iter=mPlaneNames.begin(); iter!=mPlaneNames.end(); ++iter)
167  for (unsigned i=0; i<mViewNames.size(); ++i)
168  {
169  ViewNamesType current = mViewNames[i];
170 // PLANE_TYPE type = static_cast<PLANE_TYPE>(i);
171 // PLANE_TYPE type = iter->first;
172 // QString name = iter->second;
173 
174  QAction* action = new QAction(QString("%1").arg(current.mName), typeActions);
175 // action->setData(QVariant(t));
176  action->setCheckable(true);
177  connect(action, SIGNAL(triggered()), this, SLOT(typeActionSlot()));
178  action->setChecked(viewData.mPlane==current.mPlane && viewData.mType==current.mView);
179  }
180 // for (int i=ptNOPLANE; i<ptCOUNT; ++i)
181 // {
182 // PLANE_TYPE type = static_cast<PLANE_TYPE>(i);
183 //
184 // QAction* action = new QAction(QString("%1").arg(mPlaneNames[type]), typeActions);
185 // action->setData(QVariant(i));
186 // action->setCheckable(true);
187 // connect(action, SIGNAL(triggered()), this, SLOT(typeActionSlot()));
188 // action->setChecked(viewData.mPlane==type);
189 // //menu.addAction(action);
190 // }
191 
192  //menu.addMenu("View Plane Type")->addActions(typeActions->actions());
193  menu.addActions(typeActions->actions());
194 
195  menu.exec(pointGlobal);
196 }
197 
198 void LayoutEditorWidget::splitActionSlot()
199 {
200  mViewData.split(mSelection);
201  this->updateGrid();
202 }
203 
204 void LayoutEditorWidget::mergeActionSlot()
205 {
206  mViewData.merge(mSelection);
207  this->updateGrid();
208 }
209 
210 void LayoutEditorWidget::groupActionSlot()
211 {
212  QAction* sender = dynamic_cast<QAction*>(this->sender());
213  if (!sender)
214  return;
215  int group = sender->data().toInt();
216 
217  std::set<LayoutData::iterator> selection = this->getSelectedViews();
218  for (std::set<LayoutData::iterator>::iterator iter=selection.begin(); iter!=selection.end(); ++iter)
219  (*iter)->mGroup = group;
220 
221  this->updateGrid();
222 }
223 
224 void LayoutEditorWidget::typeActionSlot()
225 {
226  QAction* sender = dynamic_cast<QAction*>(this->sender());
227  if (!sender)
228  return;
229 // PLANE_TYPE type = static_cast<PLANE_TYPE>(sender->data().toInt());
230  ViewNamesType type;
231  for (unsigned i=0; i<mViewNames.size(); ++i)
232  if (mViewNames[i].mName == sender->text())
233  type=mViewNames[i];
234 
235  std::set<LayoutData::iterator> selection = this->getSelectedViews();
236  for (std::set<LayoutData::iterator>::iterator iter=selection.begin(); iter!=selection.end(); ++iter)
237  {
238  (*iter)->mPlane = type.mPlane;
239  (*iter)->mType = type.mView;
240  }
241 
242  this->updateGrid();
243 }
244 
245 void LayoutEditorWidget::mouseMoveEvent(QMouseEvent* event)
246 {
247  this->updateSelection(event->pos());
248 }
249 
250 void LayoutEditorWidget::updateSelection(QPoint pos)
251 {
252  LayoutViewData start = this->getViewData(mClickPos);
253  LayoutViewData stop = this->getViewData(pos);
254  mSelection = merge(start.mRegion, stop.mRegion);
255  this->colorRegion(mSelection, "dimgrey", "lightgrey");
256 }
257 
258 /* Return a set of unique iterators into the layout data,
259  * representing the selected region.
260  */
261 std::set<LayoutData::iterator> LayoutEditorWidget::getSelectedViews()
262 {
263  std::set<LayoutData::iterator> retval;
264  for (int r=mSelection.pos.row; r<mSelection.pos.row+mSelection.span.row; ++r)
265  for (int c=mSelection.pos.col; c<mSelection.pos.col+mSelection.span.col; ++c)
266  retval.insert(mViewData.find(LayoutPosition(r,c)));
267  return retval;
268 }
269 
270 void LayoutEditorWidget::mousePressEvent(QMouseEvent* event)
271 {
272  mClickPos = event->pos();
273 
274  if (event->button()==Qt::RightButton)
275  {
276  // reselect if click is outside old selection
277  if (!mSelection.contains(this->getViewData(mClickPos).mRegion.pos))
278  this->updateSelection(event->pos());
279 
280  //std::cout << "mouse press context" << std::endl;
281  this->contextMenuSlot(event->pos());
282  }
283  else
284  {
285  //std::cout << "mouse press clean" << std::endl;
286  this->updateSelection(event->pos());
287  }
288 }
289 
290 void LayoutEditorWidget::colorRegion(LayoutRegion region, QString selectColor, QString backColor)
291 {
292  for (LayoutData::iterator iter=mViewData.begin(); iter!=mViewData.end(); ++iter)
293  {
294  LayoutPosition pos = iter->mRegion.pos;
295  QString color;
296 
297  if (region.contains(pos))
298  color = selectColor;
299  else
300  color = backColor;
301 
302  mViewDataCache[pos.row][pos.col].mFrame->setStyleSheet(QString("QFrame { background-color: %1 }").arg(color));
303  }
304 }
305 
309 LayoutViewData LayoutEditorWidget::getViewData(QPoint pt)
310 {
311  for (LayoutData::iterator iter=mViewData.begin(); iter!=mViewData.end(); ++iter)
312  {
313  LayoutPosition pos = iter->mRegion.pos;
314  if (!mViewDataCache[pos.row][pos.col].mFrame->geometry().contains(pt))
315  continue;
316 
317  return *iter;
318  }
319 
320  return LayoutViewData();
321 }
322 
326 void LayoutEditorWidget::rowsColumnsChangedSlot()
327 {
328  mViewData.resize(mRowsEdit->value(), mColsEdit->value());
329  this->setSaneGroupIDs();
330  this->updateGrid();
331 }
332 
333 QString LayoutEditorWidget::getViewName(LayoutViewData data) const
334 {
335  for (unsigned i=0; i<mViewNames.size(); ++i)
336  {
337  if (mViewNames[i].mPlane==data.mPlane && mViewNames[i].mView==data.mType)
338  return mViewNames[i].mName;
339  }
340  return "NA";
341 }
342 
346 void LayoutEditorWidget::setSaneGroupIDs()
347 {
348  for (LayoutData::iterator iter=mViewData.begin(); iter!=mViewData.end(); ++iter)
349  {
350  if (iter->mGroup<0)
351  iter->mGroup = 0;
352  }
353 }
354 
359 void LayoutEditorWidget::updateGrid()
360 {
361  //std::cout << "pre update:" << streamXml2String(mViewData) << std::endl;
362 
363  this->clearDisplay();
364 
365  //std::cout << "update start" << std::endl;
366  for (LayoutData::iterator iter=mViewData.begin(); iter!=mViewData.end(); ++iter)
367  {
368  LayoutRegion region = iter->mRegion;
369  GridElement gridData = mViewDataCache[region.pos.row][region.pos.col];
370 
371  // add and show frame in correct position
372  mLayout->addWidget(gridData.mFrame, region.pos.row, region.pos.col, region.span.row, region.span.col);
373  gridData.mFrame->show();
374 
375  // set view text
376  QString name = this->getViewName(*iter);
377  if (iter->mGroup<0 && name.isEmpty())
378  gridData.mLabel->setText("NA");
379  else
380  gridData.mLabel->setText(QString("%1/%2").arg(iter->mGroup).arg(name));
381  }
382 
383  mNameEdit->setText(mViewData.getName());
384  mOffScreenRendering->setValue(mViewData.getOffScreenRendering());
385 // mRowsEdit->setText(qstring_cast(mViewData.size().row));
386 // mColsEdit->setText(qstring_cast(mViewData.size().col));
387 
388  mRowsEdit->blockSignals(true);
389  mRowsEdit->setValue(mViewData.size().row);
390  mRowsEdit->blockSignals(false);
391 
392  mColsEdit->blockSignals(true);
393  mColsEdit->setValue(mViewData.size().col);
394  mColsEdit->blockSignals(false);
395 
396 // this->colorRegion(LayoutRegion(-1,-1,1,1), "lightgrey", "lightgrey");
397  this->colorRegion(mSelection, "dimgrey", "lightgrey");
398 
399  //this->updateGeometry();
400  //QSize msize = mLayout->minimumSize();
401 // QSize rsize = mGridWidget->size();
402  //std::cout << "minsize: " << msize.width() << "," << msize.height() << std::endl;
403 // std::cout << "real size: " << rsize.width() << "," << rsize.height() << std::endl;
404 
405 // QString color("QFrame { background-color: red }");
406 // mViewData[0][0].mFrame->setStyleSheet(color);
407 
408 // mGridWidget->resize(msize);
409 // this->resize(400,700);
410 // QTimer::singleShot(0, this, SLOT(setNiceSize()));
411 
412  // std::cout << "post update:" << streamXml2String(mViewData) << std::endl;
413 
414 }
415 
416 //void LayoutEditorWidget::setNiceSize()
417 //{
418 // //this->resize(400,700);
419 //}
420 
421 void LayoutEditorWidget::clearDisplay()
422 {
423  for (unsigned r = 0; r < mViewDataCache.size(); ++r)
424  {
425  for (unsigned c = 0; c < mViewDataCache[r].size(); ++c)
426  {
427  mViewDataCache[r][c].mFrame->hide();
428  }
429  }
430 }
431 
432 void LayoutEditorWidget::initCache()
433 {
434  int maxRows = LayoutData::MaxGridSize;
435  int maxCols = LayoutData::MaxGridSize;
436  mViewDataCache.resize(maxRows);
437 
438  for (int r = 0; r < maxRows; ++r)
439  {
440  mViewDataCache[r].resize(maxCols);
441 
442  for (int c = 0; c < maxCols; ++c)
443  {
444  QFrame* frame = new QFrame(this);
445  //frame->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
446  frame->setFrameStyle(QFrame::Panel | QFrame::Sunken);
447  frame->setLineWidth(3);
448  frame->setLayout(new QVBoxLayout);
449  QLabel* label = new QLabel("NA", frame);
450  frame->layout()->addWidget(label);
451 
452  mViewDataCache[r][c].mFrame = frame;
453  mViewDataCache[r][c].mLabel = label;
454  }
455  }
456 }
457 
458 
459 } // namespace cx
QString qstring_cast(const T &val)
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
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
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)
cxLogicManager_EXPORT ViewServicePtr viewService()
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
LayoutEditorWidget(QWidget *parent)
void setOffScreenRendering(bool val)
Definition: cxLayoutData.h:112
LayoutRegion merge(LayoutRegion a, LayoutRegion b)
bool getOffScreenRendering() const
Definition: cxLayoutData.h:111