Qt, change widget overlay? - c++

I know I could overlay widgets by just specifying their parent child relation-ship.
But if I have two widgets, say A and B.
At first B is over A and on the bottom left corner of A.
But when a button is clicked, I want A appear over B and on the bottom left corner of B.
How could I implement this without QML?

You can use a combination of QWidget::raise(), QWidget::lower(), QWidget::move(), QWidget::resize().
Pseudo Code:
void slotButtonClicked()
{
// Raize z-index to be "above"
widgetB->raise();
// Update size to small
widgetB->resize( smallWidth, smallHeight );
// Lower z-index to be "below"
widgetA->lower();
// Update size to large
widgetA->resize( largeWidth, largeHeight );
// Move smaller widget to corner
widgetB->move( 0, widgetA->height()-widgetB->height() );
}

Related

Notifying wxSizer of dynamic layout changes

I have the following hierarchy for layout:
wxMDIChildFrame -> wxNotebook ->
wxScrolledWindow -> wxBoxSizer -> wxStyledTextCtrl
GOAL: The wxStyledTextCtrl (CScriptWnd) resizes itself when user adds or deletes a line or presses Shift+Enter to add another CScriptWnd to the wxScrolledWindow.
The following code is when a line is added:
void CScriptWnd::OnNewLineAdded(wxStyledTextEvent& event)
{
long col, line;
PositionToXY(GetInsertionPoint(), &col, &line);
auto ClientSize = GetClientSize();
int Height = ClientSize.GetHeight();
Height += TextHeight(line);
SetClientSize(ClientSize.GetWidth(), Height);
SetMinSize(wxSize(ClientSize.GetWidth(), Height));
Refresh();
//m_ParentWindow->FitInside(); //CScriptWnd gets smaller rather than larger
//m_ParentWindow->Layout(); //CScriptWnd gets smaller rather than larger
//m_ParentWindow->GetGrandParent()->Layout(); //No effect
event.Skip();
}
Most work well such as the CScriptWnd resizes itself, the new script window added to wxScrolledWindow etc...
The problem is that the update to user interface only properly happens when the wxMDIChildFrame is resized using the mouse.
For example, if there are two CScriptWnd and the top one resizes itself, it overlaps with the bottom one
until the wxMDIChildFrame is resized using the mouse. Similar happens for the visibility of scrollbars such that when CScriptWnd client size gets larger the scrollbars only become visible when top-level window resized using the mouse.
Not sure what I am missing.
You probably need
SetMinSize(GetSize());
GetParent()->Layout();

How to position a QLabel on Qt?

I'm writing a program where I need to create a QLabel by code, instead of drag and drop, but I'm having problems positioning the QLabel as I want, this is the code I have:
if(ui->WorksList->currentItem()->text() == "Work1")
{
ui->InformationLabel->show();
QLabel *label = new QLabel(this);
label->show();
label->setText("Extraction");
label->setMinimumWidth(100);
int x = 2000;
x = label->geometry().x();
int y = 2000;
y = label->geometry().y();
}
With this piece of code my QLabel do not move from the top left corner.
Thank you
Your posted code doesn't move anything; as one of the comments said, you have to use the move method to move the label. If it doesn't show up, then it's either off screen or covered up by something else, or both. The coordinates for the move method are in the parent widget's coordinates, not the screen, and not the window. You've parented this QLabel to this, so the coordinates are relative to the top left corner of this, whatever that is.
Your code doesn't show what this is, nor any mention of how large the area of this is, so it's impossible for us to tell what coordinates might be reasonable.

How to appropriately get position of QGraphicsRectItem after drag-release?

I wanted to have an online monitoring system that could tell where the shape is currently, but am getting very weird coordinates of the item, also the dimensions of it get higher by 1 each time I create new one and drag it.
Initial position (map size is 751 by 751, checked by outputting to qDebug(), scene bound to yellow space) :
Dragging it to the left top corner.
As you can see in the beginning it was on (200;200), but after dragging it is on (-201;-196). After deleting it and creating new shape on the same position with the same properties, new shape can't be seen because it is outside of the map, which suggests that edits don't show correct data.
Here is the code of updating the edits:
void CallableGraphicsRectItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{
QGraphicsRectItem::mouseReleaseEvent(event);
ptr->updateEdits(this);
}
Here is what I managed to cut down into updateEdits():
void MainWindow::updateEdits(QAbstractGraphicsShapeItem* item)
{
//stuff not related to scene
auto posReal = item->scenePos();
auto pos = posReal.toPoint();
//create QString from coordinates
QString coordinate;
coordinate.setNum(pos.x());
ui->leftXEdit->setText(coordinate);
coordinate.setNum(pos.y());
ui->upperYEdit->setText(coordinate);
//get width and height for rect, radius for circle
auto boundingRectReal = item->sceneBoundingRect();
auto boundingRect = boundingRectReal.toRect();
ui->widthEdit->setText(QString::number(boundingRect.width()));
//disables height edit for circles, not really relevant
if (!items[currentShapeIndex].isRect)
{
ui->heightEdit->setDisabled(true);
}
else
{
ui->heightEdit->setDisabled(false);
ui->heightEdit->setText(QString::number(boundingRect.height()));
}
}
Here is how I anchor the QGraphicsScene to the left top corner of the yellow area:
scene->setSceneRect(0, 0, mapSize.width() - 20, mapSize.height() - 20);
ui->graphicsView->setScene(scene);
How can I report the right data to the edits?
You're better off overriding the itemChange method and using the ItemPositionHasChanged notification. You have to set the ItemSendsGeometryChanges flag on the item so that it receives these notifications.
I'm not sure that your item's final position has been set when you're still in the mouseReleaseEvent method. Tracking it in itemChange will ensure that the data is valid, and this kind of thing is what it's for.
Also, note that "pos" is in the item's parent coordinates, and "boundingRect" is in the item's coordinate space. You should use "scenePos" and "sceneBoundingRect" if you want to be sure you're using scene coordinates. If the item doesn't have a parent, then "pos" and "scenePos" will return the same values, but "boundingRect" and "sceneBoundingRect" will generally differ.

Constraining child QGraphicsItem to scene?

Does anyone have a better way to constrain a child of a QGraphicsItem to a scene?
I have successfully properly constrained a parent QGraphicsItem to its scene by overriding itemChange, but now I need to do the same for the child QGraphicsItem.
Example Use-case:
This code works... for the most part. The only problem is the QGraphicsItem's velocity when hitting either side will affect its endstop position:
QVariant SizeGripItem::HandleItem::itemChange(GraphicsItemChange change,
const QVariant &value)
{
QPointF newPos = value.toPointF();
if (change == ItemPositionChange)
{
if(scene())
{
newPos.setY(pos().y()); // Y-coordinate is constant.
if(scenePos().x() < 0 ) //If child item is off the left side of the scene,
{
if (newPos.x() < pos().x()) // and is trying to move left,
{
newPos.setX(pos().x()); // then hold its position
}
}
else if( scenePos().x() > scene()->sceneRect().right()) //If child item is off the right side of the scene,
{
if (newPos.x() > pos().x()) //and is trying to move right,
{
newPos.setX(pos().x()); // then hold its position
}
}
}
}
return newPos;
}
For the parent item, I used:
newPos.setX(qMin(scRect.right(), qMax(newPos.x(), scRect.left())));
which worked perfectly, but I'm stumped as to how or if I could use that here.
First, to be specific, scenes effectively have no boundaries. What you're trying to do is constrain the item to the scene rectangle that you've set elsewhere.
The problem I see is in your use of scenePos. This is an ItemPositionChange; the item's scenePos hasn't been updated with the new position yet, so when you check for scenePos being out of the scene rect, you're really checking the result of the last position change, not the current one. Because of that, your item ends up just off the edge of the scene rectangle and then sticks there. How far off the edge depends on how fast you were moving the mouse, which dictates how much distance there is between ItemPositionChange notifications.
Instead, you need to compare the new position to the scene rectangle and then restrict the value that gets returned to be within the scene rectangle. You need the new position in scene coordinates to do the comparison, so you need something like:
QPoint new_scene_pos = mapToScene (new_pos);
if (new_scene_pos.x() < scene()->sceneRect().left())
{
new_scene_pos.setX (scene()->sceneRect().left());
new_pos = mapFromScene (new_scene_pos);
}
This isn't complete code, obviously, but these are the conversions and checks you need to do to keep it in on the left side. The right side is very similar, so just use the new_scene_pos for the comparison there.
Note that I didn't assume that the left edge of sceneRecT is at 0. I'm sure that's what you coded where you set the sceneRect, but using the actual left value rather than assuming it's 0 eliminates any problems if you end up later changing the range of scene coordinates you're planning to work with.
I used "left" instead of "x" on the sceneRect call just because it parallels using "right" for the other side. They're exactly the same, but I think it reads slightly better in this case.

Make a QDialog appear in a different screen

The title says it pretty much all:
I have two screens, and each time I create a QDialog it appears in the same screen as its parent.
How can I make it appear in a different screen? Or should I use a different type of top-level widget?
The code I use to create the dialog is:
QDialog my_dialog = new QDialog(this,
Qt::WindowMaximizeButtonHint |
Qt::WindowCloseButtonHint);
...
EDIT:
I have also tried using the QDesktopWidget which gives me a QScreen object that refers to the second screen. But then I don't find how to instruct the QDialog to use that QScreen (setting it as the parent doesn't work).
It is bad, that you edit your question without reading comments :(
// Your screen geometry:
QRect buildScreenGeometry()
{
auto desktop = QApplication::desktop();
QRect virtualRect;
const auto n = desktop->screenCount();
for ( auto i = 0; i < n; i++ )
virtualRect |= desktop->screenGeometry(i);
return virtualRect;
}
// Moving
auto dlg = new QDialog( someParent );
auto newPoint = QPoint( 2000, 0 ); // point on another screen
auto realPos = someParent->mapFromGlobal( newPoint );
dlg->move( realPos );
That's all.
UPDATE:
You should understand, that there are only ONE screen area with COMMON coordinate system, that contains ALL screens.
For example, you have 2 monitors with 800x600 resolution. First (main) monitor is standing left, and second standing right. In this case, coordinate system, that is available for your application is 1600x600. So, if your widget has 100x100 top-left position, on a first monitor, and you want to move it to another, you should call move(900x100); // 900 == screen1.width() + dialog.pos().x(). Then your widget will have 100x100 position on second monitor.
You should read Qt documentation.
You can use move on your QDialog, but be aware that move will set the QDialog position relative to it's parent.
You can get the Main Window's screen position and use that to setup the QDialog position. Just know that you're not guaranteed to have 2 screens on the end user machine.
For more information on move see: http://doc.qt.io/qt-5/application-windows.html#window-geometry