Add QMdiSubWindow to current QStackedLayout - c++

So I have this code:
QStackedLayout *layout = new QStackedLayout;
QMdiArea *mdi1 = new QMdiArea;
mdi1->addSubWindow(new QMdiSubWindow);
layout->addWidget(mdi1);
QMdiArea *a = (QMdiArea *) layout->currentWidget();
a->addSubWindow(new QMdiSubWindow);
Which for some reason doesn't work. What I want to do is get the widget that is being displayed in layout - as a QMdiArea, then add a sub window to it.
P.S. this is a simplified version on the full app. adding a sub window directly to mdi1 will work but it is NOT what I'm looking for (as there are many QMdiArea's in the QStackedLayout).

So the answer was that I needed to use QStackedWidget instead of QStackedLayout.

Related

Why does scene not being displayed when widget is set as central

I have a problem displaying two labels, the label "label" is displayed with no problem ( it has some buttons adjusted Horizontally ) and the second label "label1" is not being displayed ( it contains a scene which has a view , in the scene i have a title and a picture ). This is the code i am trying to use.
widget1 = new QWidget;
setCentralWidget(widget1);
label1 = new QLabel(widget1);
scene = new QGraphicsScene(label1);
vue = new QGraphicsView(scene);
label1->move(100,100);
label = new QLabel(widget1);
layout = new QHBoxLayout(label);
label->resize(500,100);
It's my first time using QGraphicsScene/View and it is confusing me a little bit.
Thanks for the answers
You're definitly not using all those widgets the right way.
you don't need a label to display a QGraphcisView, neither the QGraphicsScene to be the child of the label.
If you want to display, let's say, a label and a view in the center widget you could write this :
//==== Central widget part
QWidget* widget = new QWidget(this);
setCentralWidget(widget);
//==== Graphics part
QGraphicsScene* scene = new QGraphcisScene(this);
QGraphicsView* view = new QGraphicsView(widget);
view->setScene(scene);
//==== Label part
QLabel* label = new QLabel(widget);
//==== Layout part
QHBoxLayout* layout = new QHBoxLayout(widget);
layout.addWidget(label);
layout.addWidget(view);
widget.setLatout(layout);
This should show a label and a graphics view in row.
If you allow me a piece of advice : you should understand the parenting system before trying to use the QGraphics module. It's one of the cornerstones of Qt and that what you did wrong here. Starts here for the parenting system : https://doc.qt.io/qt-5/objecttrees.html
and here for the graphics : https://doc.qt.io/qt-5/graphicsview.html
Hope that helps you.

Qt widget stacking child layouts on top of each other

Qt has a layout system kind of separate from the widgets, which is causing me problems. I know that when I have a QSplitter, I can add stuff to it just by doing
new SomeCustomQWidget(splitter);
However, if I have a QWidget, shouldn't I be able to do something similar? The Qt web pages only explain how to add layoutItems to layouts, only connecting them to widgets by inheriting both in a subclass. So, right no I am doing something like this
item = new QWidget(parentsplitter); //this works
subitem = new customWidget(item); //this too
subitem2 = new QSlider(item); //this isn't laid out with subitem
However this results in the slider being on top of the subitem instead of being next to it (either right or below I'd expect). Calling
//either
new QVBoxLayout(item);
//or
item->setLayout(new QVBoxLayout());
//or
item->setLayout(new QVBoxLayout(item));
after initializing the item doesn't help. (My logic is that the item takes the layout as it's child and will add subsequent children to it).
I went around this problem earlier but now I'm facing the exact same problem when making a widget inside a QGraphicsView. I feel like it shouldn't be necessary to make a custom subclass of every item I could ever want to dynamically add to those widgets, like the Qt website seems to expect. (If I simply want to add some text and a couple buttons, do I really need to make 2 custom subclasses?) I am using a .ui file for basic layout but these items need to be created dynamically.
Solution
Forgot to call item->show(); on the widget used for layout.
Turns out, I had tested otherwise correct code, but since some of my subclasses called show() but others didn't, it just seemed broken to me.
This however isn't the issue with the graphicswidgets, as graphicslayout can only be used to add graphicslayoutitems, which don't include for example graphicstextitems.
Solution 2
Using the QGraphicsProxyWidget instead of QGraphicsWidget fixed the issue!
QWidget *parent = new QWidget();
QVBoxLayout *rootLayout = new QVBoxLayout(parent);
QHBoxLayout *subLayout1 = new QHBoxLayout();
QHBoxLayout *subLayout2 = new QHBoxLayout();
rootLayout->addLayout(subLayout1);
rootLayout->addLayout(subLayout2);
subLayout1->addWidget(new QLabel("Foo"));
subLayout1->addWidget(new QLabel("Bar"));
subLayout2->addWidget(new QPushButton("Foo button"));
subLayout2->addWidget(new QPushButton("Bar button"));
parent->show();
Note once layout is assigned to a widget by setLayout or when widger is passed to constructor fo layout, the are in 1 to 1 relationship which last until widget or layout is destroyed. See documentation:
If there already is a layout manager installed on this widget, QWidget won't let you install another. You must first delete the existing layout manager (returned by layout()) before you can call setLayout() with the new layout.
That is why subitem2 = new QSlider(item) didn't work for you.
I think you're confusing parent/child relationship with managing layout.
Lets start with layouts. When set on a widget the layout becomes a child of that widget. When constructed with a widget parent the widget becomes a parent and the layout is set on the widget. When adding widgets to layout they become children of the widget that layout is set on, not the layout itself. Setting a parent on a widget or constructing widget with a parent does not automatically add the widget to its parent layout. Deleting a layout does not delete widgets governed by it, because they are not its children.
To summarize - layouting and parenting are two distinct mechanisms. first is for governing position and size of a widget, the other is for object hierarchy and resource management (parents delete their children). Layouts have a convenience constructor though, that does both - sets a parent of the layout and sets that layout on the parent widget.
In code:
item = new QWidget()
layout = new QVBoxLayout(item);
is equivalent to:
item = new QWidget();
layout = new QVBoxLayout();
layout->setParent(item);
item->setLayout(layout);
This code adds a child to a layout and item becomes its parent:
item = new QWidget();
layout = new QVBoxLayout(item);
child = new QWidget();
layout->addWidget(child);
This adds a child to a parent item, but does not add child to a layout:
item = new QWidget();
layout = new QVBoxLayout(item);
child = new QWidget(item);
A QSplitter is special in that it is a widget and it does its own layout of children i.e. when you use a splitter as a parent of a widget that widget's geometry is governed by the splitter. Other widgets don't behave like that.
So to comment your code:
item = new QWidget(parentsplitter); //this works because splitter does layout
subitem = new customWidget(item); //item is not splitter so subitem geometry is not managed
subitem2 = new QSlider(item); //item is not splitter so subitem2 geometry is not managed
If you want to lay out subitems in the item then item either has to be a splitter too or have a layout that will manage children, so:
childsplitter = new QWidget(parentsplitter); //note that parentsplitter has only 1 item
subitem = new customWidget(childsplitter);
subitem2 = new customWidget(childsplitter);
or
widget = new QWidget(parentsplitter); //note that parentsplitter has only 1 item
lay = new QHBoxLayout(widget);
subItem = new customWidget();
subItem2 = new customWidget();
lay->addWidget(subItem);
lay->addWidget(subItem2);
If you want parent splitter to have two items then you do:
item = new customWidget(parentsplitter);
item2 = new customWidget(parentsplitter);

Qt: How to add two widgets (say QPushButton) to the status bar, one to the left and other to the right side?

I would like to add two widgets (say QPushButton) to the status bar, one to the left and other to the right side.
I am thinking of adding horizontal spacer in between the two widgets, but don't know how to add.
PS: I tried using addWidget() to add to the left and addPermanentWidget() to add to the right but it doesn't look neat and also it doesn't feel right.
You can add two buttons to a layout in a widget and add the widget to the status bar using QStatusBar::addWidget :
QWidget * widget = new QWidget();
QPushButton * leftBut = new QPushButton("Left");
QPushButton * rightBut = new QPushButton("Right");
QGridLayout * layout = new QGridLayout(widget);
layout->addWidget(leftBut,0,0,1,1,Qt::AlignVCenter | Qt::AlignLeft);
layout->addWidget(rightBut,0,1,1,1,Qt::AlignVCenter | Qt::AlignRight);
ui->statusBar->addWidget(widget,1);
I am thinking of adding horizontal spacer in between the two widgets, but don't know how to add.
Here is a way to use a "fake" spacer.
QPushButton *leftButton = new QPushButton("Left");
QPushButton *rightButton = new QPushButton("Right");
QLabel *spacer = new QLabel(); // fake spacer
ui->statusBar->addPermanentWidget(leftButton);
ui->statusBar->addPermanentWidget(spacer, 1);
ui->statusBar->addPermanentWidget(rightButton);
The second parameter in addPermanentWidget is "used to compute a suitable size for the given widget as the status bar grows and shrinks".
Demo:
I think the simplest way is using a QGridLayout (honestly I never tried to modify a status bar anyway) supposing that the status bar is or descends from widget you can do this:
QGridLayout *myGridLayout = new QGridLayout();
statusbar->setLayout(myGridLayout)
QPushButton *button1 = new QPushButton(this);
myGridLayout->addWidget(button1,0,0,1,1);
QPushButton *button2 = new QPushButton(this);
myGridLayout->addWidget(button2,X,0,1,1);
The biggest is X the more space you want to leave in between, I would suggest to start with 3 and then make few tests to see how it looks.

How to change contents of QMainWindow dynamically

I have a QMainWindow that starts out with nothing but a menubar with a menu that has two options. When the first is clicked the window should be populated with QLabels and various input widgets to recieve data. When the second option is clicked the window should be populated with a QTextEdit(obviously removing whatever was on the window at the time)
The following is code I have tried :
void OrderWindow::displayAddOrder(){
QVBoxLayout* tlayout = new QVBoxLayout();
QHBoxLayout* row = new QHBoxLayout();
row->addWidget(nameLbl);
tlayout->addLayout(row);
qDeleteAll(children());
delete layout();
setLayout(tlayout);
}
It's a bit messy since I've been trying various things. When I click on a menu option with this code it simply says the application has stopped working.
Any help would be appreciated.
You have at least the following options:
Always show the actual widget, and hide the rest. This is simple in case of two widgets like in your example. You could use this technique with the observer design pattern for any number of widgets.
Use the QStackedWidget class which basically behaves the way as your custom observer pattern implementation would be, although you will need to use an extra class for this.
Therefore, I would suggest to write the following code:
orderwindow.h
...
class QStackedWidget;
class OrderWindow
{
...
public:
explicit OrderedWindow(QWidget *parent);
...
private:
QStackedWidget m_stackedWidget;
...
}
...
orderwindow.cpp
#include "orderwindow.h"
#include <QStackedWidget>
...
OrderWindow::OrderWindow(QWidget *parent)
: QWidget(parent)
, m_stackedWidget(new QStackedWidget(this))
{
QWidget *firstPageWidget = new QWidget;
QWidget *secondPageWidget = new QWidget;
m_stackedWidget->addWidget(firstPageWidget);
m_stackedWidget->addWidget(secondPageWidget);
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(stackedWidget);
setLayout(layout);
}
...
void OrderWindow::displayAddOrder() {
m_stackedWidget->setCurrentWidget(nameLbl);
}
...
you can use a QStackedWidget
start with showing an empty page and then show the correct page as needed:
there is then no need to mess with adding or removing widgets
Yes, you can use a QStakedWidget if your input options are fixed. If it's not, I suggest you to use an abstract factory pattern to create the stacked widget content. This woluld make your code more readable.

Setting the form lines like iTunes info dialog on Mac with Qt

I'm just a beginner and I wonder if it would be possible to create a form widget on Mac like the info dialog in iTunes.
I tried using:
QGroupBox: I cannot find a way to get rid of the frames.
Creating my own widget: I cannot find a way to fix the spacing between the label and the QLineEdit widget using the QVBoxLayout (actually I'm not sure I understand well the differences between margin/spacing).
QFormLayout: I cannot find a way to reduce the size of the QLabel after using setrowWrapPolicy::WrapAllRows
Also I am not (yet) very comfortable with QtDesigner, so i'd like to avoir using it (for now)
Thanks in advance
Edit: Some precisions on the programs. I use QtCreator 2.6.1 with Qt 4.8.1 and 5.0 on Mac OS X Mountain Lion.
Edit 2: Here is the code.
Subclass of QWidget:
MCLineEdit::MCLineEdit(const QString &header)
{
m_lineEdit = new QLineEdit;
m_lineTitle = new QLabel(header);
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(m_lineTitle);
layout->addWidget(m_lineEdit);
layout->setSpacing(0);
setLayout(layout);
}
To display the widget
myView::myView(QWidget *parent) :
QWidget(parent)
{
setFixedSize(600, 500);
MCLineEdit *lineEdit1 = new MCLineEdit("Test 1");
MCLineEdit *lineEdit2 = new MCLineEdit("Test 2");
MCLineEdit *lineEdit3 = new MCLineEdit("Test 3");
MCLineEdit *lineEdit4 = new MCLineEdit("Test 4");
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(lineEdit1);
mainLayout->addWidget(lineEdit2);
mainLayout->addWidget(lineEdit3);
mainLayout->addWidget(lineEdit4);
mainLayout->setSpacing(0);
setLayout(mainLayout);
}
This can be accomplished a ton of ways I am sure. Qt gives you all the possible layouts you could need to achieve this. You could do it with a QGridLayout, and add widgets with different amounts of "cell" spanning, and control the row and column sizes to suit. Or you can just do it with a bunch of nested vertical/horizontal layouts.
For example, you can group a label and a field together in a QVBoxLayout, by adding the widgets with a left alignment, and then setting the spacing to 0 between the items:
layout->setSpacing(0);
layout->addWidget(aLabel, Qt::AlignLeft);
layout->addWidget(aLineEdit, Qt::AlignLeft);
mainVerticalLayout->addLayout(layout);
For something like the track numbers, it is just more nested layouts:
vLayout->addWidget(aLabel);
hLayout->addWidget(aCheckbox);
hLayout->addWidget(aLabel);
hLayout->addWidget(aCheckbox);
vLayout.addLayout(hLayout);
And regarding your bullet points:
QGroupBox lets you remove the frame with setFlat(bool)
With layouts, the margin is the padding around the outside of the contained widgets. What you want is setSpacing(int) to control the amount of space between the items in the layout.
QFormLayout is probably not your best choice here. That is usually for having labels on one side and widgets on the other. Basically it is a 2 column layout. A QGridLayout would be more appropriate. And to reduce the size of the QLabel, you can give it a max or a fix size. Such as using setFixedWidth() or setMaximumWidth() on the label.
So I finally managed to get the expected result after playing a bit with QtCreator.
Here is the code for who might be interested:
myLineEdit:myLineEdit(const QString &header)
{
m_lineEdit = new QLineEdit;
m_groupBox = new QGroupBox;
QFont groupFont;
groupFont.setPixelSize(10);
groupFont.setBold(true);
m_groupBox->setTitle(header);
m_groupBox->setFlat(true);
m_groupBox->setFont(groupFont);
m_groupBox->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
QFont lineFont;
lineFont.setPixelSize(13);
lineFont.setBold(false);
m_lineEdit->setFont(lineFont);
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(m_lineEdit);
layout->setContentsMargins(0, 10, 0, 0);
layout->setSpacing(10);
layout->setSizeConstraint(QLayout::SetMinAndMaxSize);
m_groupBox->setLayout(layout);
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(m_groupBox);
setLayout(mainLayout);
}
One comment though: will only work on Qt 5 since on 4.8 the setFlat() method will display a separating line between the header and the QLineEdit.
Thanks to jdi for his help!