I'm currently working on a nice way to use a GUI to modify object contents at run time, project uses Qt.
So I thought of passing a QLayout to the objects, to let them create their own GUI. As POC, I created this ("display" is the name of the QVBoxLayout* parameter):
QPushButton* button = new QPushButton();
button->setText("foo");
button->setObjectName("bar");
display->addWidget(button);
which works just as fine as expected. But I will need to read what the user typed into the GuI, so this is what I did next:
QPushButton *button2 = display->findChild<QPushButton *>();
if(button2)
std::cout << button2->objectName().toStdString() << std::endl;
here nothing is put out - if() statement is false!
QList<QWidget *> widgets = display->findChildren<QWidget *>();
foreach (QWidget* b, widgets) {
std::cout << b->objectName().toStdString() << std::endl;
}
similarly, the widgets list is empty.
I tried looking through the full member list at: http://doc.qt.digia.com/qt/qvboxlayout-members.html, but findChild/findChildren seems like the best fit to my needs...
SOLUTION BELOW
This is how I handle it now:
instead of passing a QVBoxLayout to the GUI creation, a QWidget should be passed, in following named "display" as above.
QVBoxLayout* layout = new QVBoxLayout();
display->setLayout(layout);
QPushButton* button = new QPushButton();
button->setText("foo");
button->setObjectName("bar");
layout->addWidget(button);
now to findChild / findChildren:
QPushButton *button2 = display->findChild<QPushButton *>("bar");
if(button2)
std::cout << button2->objectName().toStdString() << std::endl;
QList<QWidget *> widgets = display->findChildren<QWidget *>();
foreach (QWidget* b, widgets) {
std::cout << b->objectName().toStdString() << std::endl;
}
both methods work for me as expected! (Plus, now the layout can be chosen freely by the GUI creation!)
The parent of a QWidget must be a QWidget. QLayout is not a QWidget.
Layouts exist to move and resize the children of a QWidget. Though you may add child widgets by making calls on the layout, ultimately, their parent will be the QWidget that the layout resides on.
To illustrate:
QWidget* widget = new QWidget;
qDebug("Widget: %p", widget);
QHBoxLayout* layout = new QHBoxLayout;
qDebug("Layout: %p", layout);
QWidget* child = new QWidget;
layout->addWidget(child);
qDebug("Child's parent before setLayout: %p", child->parent());
widget->setLayout(layout);
qDebug("Child's parent after setLayout: %p", child->parent());
Output:
Widget: 0x8e1c1e0
Layout: 0x8e1c3f0
Child's parent before setLayout: 0x0
Child's parent after setLayout: 0x8e1c1e0
Related
I am using Qt Creator 4.13.1 with Qt 5.15.1 on Windows 10 Pro.
I am new to using Stylesheets in Qt and a bit confused of the possible selectors considering QSplitter and its child. I want to have 2 childs separated by a splitter-handle and draw their backgrounds in different colors.
This is an example szenario
QSplitter* splitter = new QSplitter();
QWidget* widgetA = new QWidget();
// add a layout with some further child widgets
QWidget* widgetB = new QWidget();
// add a layout with some further child widgets
splitter->addWidget(widgetA);
splitter->addWidget(widgetB);
layout()->addWidget(splitter);
where I tried:
widgetA->setStyleSheet("background-color: #ff0000;"); which applies to all child widgets of widgetA (e.g. QLabels, QPushButtons, ...) , but not to their surrounding widgetA itself
splitter->setStyleSheet("background-color: #ff0000;"); applies to the widgetA, widgetB and the handle and all widgets below widgetA and widgetB
naming widgetA->setObjectName("Tim"); and splitter->setStyleSheet("QWidget#Tim {background-color: #ff0000;}"); which has no effect at all.
[EDIT] splitter->setStyleSheet("QSplitter QWidget #Andy {background-color: #ff0000;}"); applies the background only to a certain widget inside either child widget. But using splitter->setStyleSheet("QSplitter #Tim {background-color: #ff0000;}"); has (again) no effect at all.
How would I set the background-color of widgetA separately, so without affecting the childs of widgetA or the other splitter child widgetB?
I don't see any problems with using widget->setStyleSheet() in your case. Here, I have tested your implementation of setObjectName():
Application::Application(QWidget *parent) :
QWidget(parent)
{
//set a base layout for the parent of splitter
QHBoxLayout *boxLayout = new QHBoxLayout(this);
this->setLayout(boxLayout);
//allocate splitter with parent
QSplitter* splitter = new QSplitter(this);
QWidget* widgetA = new QWidget();
widgetA->setObjectName("widgetA");
widgetA->setLayout(new QVBoxLayout());
//example buttons as child for widgetA
QPushButton *p = new QPushButton("0",widgetA);
widgetA->layout()->addWidget(p);
p = new QPushButton("1",widgetA);
widgetA->layout()->addWidget(p);
p = new QPushButton("2",widgetA);
widgetA->layout()->addWidget(p);
p = new QPushButton("3",widgetA);
widgetA->layout()->addWidget(p);
//widgetB, all similar to widgetA
QWidget* widgetB = new QWidget();
widgetB->setObjectName("widgetB");
widgetB->setLayout(new QVBoxLayout());
widgetA->setStyleSheet("QWidget#widgetA{background-color: #ff0000;}");
widgetB->setStyleSheet("QWidget#widgetB{background-color: #00ff00;}");
p = new QPushButton("1",widgetB);
widgetB->layout()->addWidget(p);
p = new QPushButton("2",widgetB);
widgetB->layout()->addWidget(p);
p = new QPushButton("3",widgetB);
widgetB->layout()->addWidget(p);
//add both widgets to splitter
splitter->addWidget(widgetA);
splitter->addWidget(widgetB);
//add splitter to base layout
this->layout()->addWidget(splitter);
}
Output
I have some simple code to create a new widget using Qt:
Dock::Dock() : QDockWidget() {
label = new QLabel(QLatin1String("TEST"));
QVBoxLayout* layout = new QVBoxLayout();
layout->addWidget(label, 0, Qt::AlignTop);
layout->addStretch(-1);
QWidget* multiWidget = new QWidget();
multiWidget->setLayout(layout);
setWidget(multiWidget);
}
Where label is a private member QLabel* label. My question is: am I responsible for deleting label in Dock's destructor?
I tried changing the code so that label is a std::shared_ptr<QLabel>:
Dock::Dock() : QDockWidget() {
label = std::make_shared<QLabel>(QLatin1String("TEST"));
QVBoxLayout* layout = new QVBoxLayout();
layout->addWidget(label.get(), 0, Qt::AlignTop);
layout->addStretch(-1);
QWidget* multiWidget = new QWidget();
multiWidget->setLayout(layout);
setWidget(multiWidget);
}
So that it is deleted automatically when `Dock' is destroyed but I get an error when I close the program.
In order to properly set up the objects tree you need to do the following:
Create the container widget first,
Create child widget and set it's parent,
Create layout of the container widget,
Add widget to the layout.
Here is the code that demonstrates the mentioned approach:
Dock::Dock()
:
QDockWidget()
{
QWidget* multiWidget = new QWidget;
label = new QLabel(QLatin1String("TEST"), multiWidget); // Set parent
QVBoxLayout* layout = new QVBoxLayout(multiWidget); // Sets layout
layout->addWidget(label, 0, Qt::AlignTop);
layout->addStretch(-1);
setWidget(multiWidget);
}
I created a layout contain a parent widget. In that parent widget i created another widget.
My code is similarly to this:
QGridLayout *layout = new QGridLayout();
QWidget *parentWidget = new QWidget();
layout->addWidget(parentWidget );
QWidget *childWidget = new QWidget(parentWidget);
How can i center the child widget in parent widget ?
The problem is we cannot get the true size of parent widget because it's in a layout.
Move the child inside the parent's showeEvent. You can use a bool flag to do it only when the parent is shown for the first time.
void Parent::showEvent(QShowEvent *)
{
if(_first_show)
{
_first_show = false;
_child->move(this->rect().center() - _child->rect().center());
}
}
Proof that it works (red is the parent, and blue is the child):
You can do this by setting fixed size of child widget and placing it inside grid layout of parent widget.
QGridLayout *layout = new QGridLayout();
QWidget *parentWidget = new QWidget();
layout->addWidget(parentWidget );
QWidget *childWidget = new QWidget(parentWidget);
QGridLayout *parentLayout = new QGridLayout();
parentWidget->setLayout(parentLayout);
parentLayout->addWidget(childWidget);
childWidget->setFixedSize(/*some fixed size for child widget*/);
I'm working with qt, and I'm creating dynamically the objects of the following pictures (each timeline is a widget).
The structure is that I'm adding this widget to a verticalLayout, that is the widget contained by the scrollArea.
But unfortunately the scrollbar does not appear when instead it should be present:
If I increase the size of the window, the content is shown correctly:
But since the number of timelines created inside the window can be greater than the screen size, I need a scrollbar. What could be the problem? that
EDIT: Some source code, the constructor of the main window: some code is not present because it is created with QTCreator
Schedule::Schedule(QString pathname, QWidget *parent) :
QWidget(parent),
ui(new Ui::Schedule)
{
ui->setupUi(this);
ui->scrollArea->setLayout(ui->pageLayout);
traceParser parser(pathname);
parser.readJson();
ArchitectureParameter arch = parser.getArchParam();
QString taskName;
for(std::list<QString>::iterator taskNameIter = parser.getTaskNames().begin();
taskNameIter != parser.getTaskNames().end(); taskNameIter++)
{
taskName = *taskNameIter;
TaskSchedule *t = new TaskSchedule(this , taskName, 80, arch.nCPU(), arch.maxTime(),
parser.getExecList(taskName), parser.getTaskSimpleEventsMap(taskName));
t->resize(600, t->height());
t->resize(600, t->width());
ui->pageLayout->addWidget(t);
}
}
Probably this happens because you set the layout on the scrollArea. Here is the fast snippet which works for me:
QWidget* testWidget = new QWidget;
QVBoxLayout* layout = new QVBoxLayout;
QStringList strings;
strings << "asdfasd" << "asdffdfd" << "asdvvsdf" << "asdfccasdf";
Q_FOREACH(QString string, strings){
TagButton* btn = new TagButton();
btn->setText(string);
layout->addWidget(btn);
}
testWidget->setLayout(layout);
QScrollArea* scrollArea = new QScrollArea;
scrollArea->setWidget(testWidget);
scrollArea->show();
Notice that I am setting the layout on the testWidget and then setWidget on scrollArea
Here they tell us how to create tabs:
Create a QTabWidget.
Create a QWidget for each of the pages in the
tab dialog, but do not specify parent widgets for them.
Insert child
widgets into the page widget, using layouts to position them as
normal.
Call addTab() or insertTab() to put the page widgets into the
tab widget, giving each tab a suitable label with an optional
keyboard shortcut.
So, I created a tab widget:
class mainWindow : public QDialog
{
Q_OBJECT
QWidget* m_mainWindow;
QTabWidget* tab;
...
Then I have a Widget Class that defines a "page":
class tradeView : public QWidget
{
Q_OBJECT
QWidget* tradeWidget;
...
THis is how the c'tor of the widget (that is supposed to go into the tab as a page) looks like:
tradeView::tradeView()
{
tradeWidget = new QWidget;
tradeWidget->setWindowTitle("Trade View");
tradeWidget->setGeometry(150,18,1800,800);
m_pTableWidget = new QTableWidget(this);
m_pTableWidget->setRowCount(100);
m_pTableWidget->setColumnCount(6);
m_TableHeader<<"Client Id"<<"Symbol"<<"Quantity"<<"Strategy Id"<<"Expiry" << "Side";
m_pTableWidget->setHorizontalHeaderLabels(m_TableHeader);
m_pTableWidget->verticalHeader()->setVisible(false);
m_pTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
m_pTableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
m_pTableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
m_pTableWidget->setShowGrid(false);
m_pTableWidget->setStyleSheet("QTableView {selection-background-color: red;}");
m_pTableWidget->setGeometry(QApplication::desktop()->screenGeometry());
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(m_pTableWidget);
tradeWidget->setLayout(layout);
}
Then I did the following inside my main dialog c'tor:
mainWindow::mainWindow(QWidget* parent):QDialog(parent)
{
m_mainWindow = new QWidget;
m_mainWindow->setWindowTitle("Main Window");
QVBoxLayout *layout = new QVBoxLayout;
tradeView* tradeViewWindow = new tradeView();
orderView* orderViewWindow = new orderView();
tab = new QTabWidget(this);
tab->addTab(tradeViewWindow, "Trade");
tab->addTab(orderViewWindow, "Order");
layout->addWidget(tab);
m_mainWindow->setLayout(layout);
m_mainWindow->setGeometry(150,18,1850,900);
m_mainWindow->show();
}
I was expecting the widget to show up in the tab.
But when I run the code, the tab is created, but is completely empty.
What am I missing here ?
Your widget construction is strange. The rough object ownership of your widgets is like the following:
MainWindow (top-level QDialog)
m_mainWindow (top-level QWidget)
tab (QTabWidget)
tradeViewWindow (QWidget)
orderViewWindow (QWidget)
tradeView->tradeWidget (hidden top-level QWidget)
QTableWidget
orderView->orderWidget (hidden top-level QWidget)
QTableWidget
Do you see the problem now? You actually have 4 top-level QWidgets and two of them are hidden. You are seeing empty tabs since you create QTableWidget in another QWidget which is hidden. In other words, QTableWidget's parent is not the tab, its parent is a hidden top-evel QWidget.
The solution: tradeView is already a QWidget itself, there is no need to create another tradeWidget inside it again. You should set the layout's parent to tradeView itself:
tradeView::tradeView()
{
//tradeWidget = new QWidget; // this is a hidden top-level QWidget
//tradeWidget->setWindowTitle("Trade View");
//tradeWidget->setGeometry(150,18,1800,800);
m_pTableWidget = new QTableWidget(this);
m_pTableWidget->setRowCount(100);
m_pTableWidget->setColumnCount(6);
m_TableHeader<<"Client Id"<<"Symbol"<<"Quantity"<<"Strategy Id"<<"Expiry" << "Side";
m_pTableWidget->setHorizontalHeaderLabels(m_TableHeader);
m_pTableWidget->verticalHeader()->setVisible(false);
m_pTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
m_pTableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
m_pTableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
m_pTableWidget->setShowGrid(false);
m_pTableWidget->setStyleSheet("QTableView {selection-background-color: red;}");
m_pTableWidget->setGeometry(QApplication::desktop()->screenGeometry());
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(m_pTableWidget);
//tradeWidget->setLayout(layout);
this->setLayout(layout);
}
As a sidenote, your MainWindow which is a QDialog creates another m_mainWindow as well, are you sure this is your intention?