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