I have two different views to display inside a QWidget window. Each view has a separate QGraphicsScene.
I want to toggle between the two views on button click.
This is my current implementation:
void toggleUi(bool type){
QGraphicsView* currentView;
if(bool){
currentView = getFirstView(); // returns QGraphicsView of first type
}
else{
currentView = getSecondView(); // returns QGraphicsView of second type
}
QLayout* layout = widget->layout ();
if (layout != 0)
{
QLayoutItem *item;
while ((item = layout->takeAt(0)) != 0)
layout->removeItem (item);
delete layout;
}
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(currentView);
}
Issue: Both views are displayed over one another on toggle, even after deleting the layout and adding a new one.
Both views are rendered fine without toggle.
Is there a better/another way to do it?
Use QStackedWidget! Use the addWidget function to add widgets:
QStackedWidget *stackedWidget = new QStackedWidget(this);
stackedWidget->addWidget(getFirstView());
stackedWidget->addWidget(getSecoundView());
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(stackedWidget);
setLayout(layout);
Here's how to switch between the widgets:
void toggleUi(bool type){
if(condition)
stackedWidget->setCurrentindex(0)
else
stackedWidget->setCurrentindex(1)
I hope it helps!
Related
I have a QHBoxLayout in which I added some widgets. I need to be able to refresh the layout dynamically so I use this to clear the layout :
void ClearLayout(QLayout* layout)
{
if (!layout)
return;
QLayoutItem* item;
while ((item = layout->takeAt(0)) != nullptr)
{
delete item->widget();
ClearLayout(item->layout());
}
}
This indeed removes all widgets and layouts. After this layout->isEmpty() returns true and layout->count() returns 0.
However, when I try to add new widgets (same type of other previously added but new instance) It does not work !
AddWidget()
{
// DeviceWidget inherits QWidget
DeviceWidget* deviceWidget = new DeviceWidget;
deviceWidget->setFixedSize(150, 200);
connect(deviceWidget->GetSignalObject(), &DeviceObject::Selected, this,
&DeviceLayout::SelectedDevice);
layout->addWidget(deviceWidget, 0, Qt::AlignCenter);
}
This is the same function used previously to add the widgets to the layout and worked the first time at Construction:
MainLayout(QWidget* parent) : QHBoxLayout(parent)
{
layout = new QHBoxLayout;
addLayout(layout);
uint32 nb = GetDeviceNumber(); // returns 2
for (uint32 i = 0; i < deviceNb; ++i)
AddDeviceWidget();
}
After trying to add 2 widgets I have layout->isEmpty() returns true and layout->count() returns 2 so I'm confused …
thanks for any help provided :)
EDIT:
The problem seems to be comming from my DeviceWidget class since trying to add a simple QLabel to the cleared layout worked. Here's the DeviceWidget Constructor:
DeviceWidget::DeviceWidget(QWidget* parent) : QWidget(parent)
{
QVBoxLayout* vLayout = new QVBoxLayout;
QLabel* deviceIcon = new QLabel("DeviceIcon", this);
deviceIcon->setFixedSize(128, 128);
deviceIcon->setPixmap(QPixmap::fromImage(QImage("Resources/Icons/device.png")));
deviceIcon->setObjectName("DeviceIcon");
// StatusWidget inherits QWidget
// Just override paintEvent to display a colored filled disk
m_status = new StatusWidget(20, Status::Close, this);
m_status->setObjectName("DeviceStatus");
vLayout->addWidget(deviceIcon, 0, Qt::AlignCenter);
vLayout->addWidget(m_status, 0, Qt::AlignCenter);
// DeviceObjct inherits from QObject an add a signal
m_object = new DeviceObject(size());
// Function clearing the stylesheet background-color
Clear();
setLayout(vLayout);
installEventFilter(this);
setObjectName(QString("DeviceWidget"));
}
Commenting installEventFilter(this) make it work so I think I need to add an event filter to make it work but I don't know which one
As said in the Edit, The problem is coming from DeviceWidget added in the layout that override eventFilter. There is probably a way to add a case in eventFilter to make it work but in my case it was best either (1) or (2):
1. Remove eventFilter from class DeviceWidget and put it in class DeviceObject: m_object is present to emit a signal according to event:
DeviceObject.h:
DeviceObject(QObject* parent);
bool eventFilter(QObject* obj, QEvent* event) override;
signals:
void Select(uint32 i);
Then in class DeviceWidget still call installEventFilter but with m_object as parameter: installEventFilter(m_object);
For the other event (Enter/Leave) I overrode void enterEvent(QEvent* event) and void leaveEvent(QEvent* event) for class DeviceWidget. That's what lead me to the second option that seems better.
2. Completely remove eventFilter and installEventFilter as it is only used to emit a signal when widget is clicked and do things when cursor hovers the widget. Instead override enterEvent and leaveEvent for class DeviceWidget like said before for hover event.
Then in class DeviceObjecy override void mousePressEvent(QMouseEvent*) for clicked event.
I know this has been asked several times in different posts, however I am still not convinced that my implementation is the best solution.
I have a QVBoxLayout in which I add a QHBoxLayout which hold widgets. The QVBoxLayout by default is empty.
I delete all layouts widgets using this code, however the widgets can fire signals. Is this save? Also I would rather want to change it to a recursive function as shown in https://stackoverflow.com/questions/4272196/qt-remove-all-widgets-from-layout/7077340#=
QLayoutItem *child;
while ((child = ui->verticalLayoutAmplitude->takeAt(0)) != 0) {
QLayoutItem * subchild;
while ((subchild = child->layout()->takeAt(0)) != 0) {
delete subchild->widget();
delete subchild;
}
delete child->widget();
delete child;
}
What I do not understand is why it is so complicated.
Also the creation of the widgets could be different
The parent layout can not be used in the creation of the widgets.
Thus only the MainWindow as a parent class was inserted.
I wonder what else should be inserted as a parent class?
for (int i = 0; i < parameterList.size(); ++i) {
QString valueName = parameterList.at(i);
double value = parameter(valueName);
QHBoxLayout * hLayout = new QHBoxLayout(this);
QDoubleSpinBox * spinbox = new QDoubleSpinBox();
QLabel * label = new QLabel();
label->setText(valueName);
spinbox->setValue(value);
hLayout->addWidget(label, 1);
hLayout->addWidget(spinbox, 3);
ui->verticalLayoutAmplitude->addLayout(hLayout);
}
I have so when a person clicks a button it should duplicate a tab but I have run into the problem that tab->layout() only returns QLayout and I can't convert it QHBoxLayout.
void MainWindow::on_dublicateSection_clicked()
{
QWidget* tab = tabWidget->currentWidget();
QWidget* newTab = new QWidget(tab);
QHBoxLayout* layout = new QHBoxLayout(tab->layout());
newTab->setLayout(layout);
content->IncreaseArraySize(Section(tabWidget->count()));
QString tabText = tabWidget->tabText(tabWidget->currentIndex());
content->sections[tabWidget->count()].name = tabText;
tabWidget->addTab(newTab,tabText);
}
Actually you can.
QHBoxLayout* hbLayout = qobject_cast<QHBoxLayout*>(tab->layout());
Q_ASSERT(hbLayout);
But keep in mind that QObject derived classes are NOT copyable.
I am new to Qt. As it stands, I have a table with a button btn. When the button is clicked the setCentralWidget(view) takes over the window so I can no longer see the table obviously. But if I remove the setCentralWidget(view), nothing displays when I click the button.
Is there a way I can display both in the same window? Split or dock maybe?
(I have removed code that is irrelevant to my question)
MainWindow::MainWindow()
{
//etc
packet = new QTabWidget;
setCentralWidget(packet)
}
//other code
void MainWindow::create(const QString &a)
{
QTableWidget* table = new QTableWidget;
int tabIndex = packet->addTab(table, a);
packet->setCurrentIndex(tabIndex);
table->setRowCount(1);
table->setColumnCount(2);
table->setHorizontalHeaderLabels(QString("a;Simulator").split(";"));"));
table->setItem(0,0,new QTableWidgetItem(a));
QPushButton *btn = new QPushButton("load", this);
connect(btn, SIGNAL(clicked()), this, SLOT(sim()));
table->setCellWidget(0,1, btn);
}
void MainWindow::sim()
{
QGraphicsScene* scene = new QGraphicsScene(QRect(-10, -10, 100, 50));
QGraphicsView* view = new QGraphicsView();
scene->addText("Network");
view->setScene(scene);
view->setGeometry(QRect(10, 10, 100, 50));
setCentralWidget(view);
}
You should look at the QMdiArea class - it's designed to be used as the central widget of a Main Window that can have many inner widgets. It has a variety of layout styles as well - scroll down on that page to see the examples.
Hope that helps!
You should subclass QWidget. For example make your own MyWidget class. And this class will include a QGraphicsView and a QTabWidget in a splitter for example. And in your MainWindow set the central widget as an instance of MyWidget.
I am adding three tables dynamically in widget containing table widget and labels, but nothing shows on screen, I have tried to do it with vertical layout but it does not expand if i add a new row, so not scrolling.
Is there any other way to get all three tables on a same page with scrolling.
QScrollArea *m_scrollArea =ui->scrollArea_Stats;
m_scrollArea->setWidgetResizable(true);
QWidget *area = new QWidget;
QVBoxLayout *vlay = new QVBoxLayout(m_scrollArea);
area->setLayout(vlay);
StatsWidget *objStatsWidget;
for(int i=0;i<2;i++)
{
objStatsWidget=new StatsWidget(ui->scrollArea_Stats);
vlay->addWidget(objStatsWidget);
}
m_scrollArea->setWidget(area);
here StatsWidget is my custom widget containing 2 lables at top and a table widget
I am adding three tables dynamically but page is not scrolling, vlay is not showing all tables it is just showing what it can show in a page without scrolling.
try rewrite the code as this:
m_scrollArea->setWidgetResizable(true);
QVBoxLayout *vlay = new QVBoxLayout;
StatsWidget *objStatsWidget;
for(int i=0;i<2;i++)
{
objStatsWidget=new StatsWidget(ui->scrollArea_Stats);
vlay->addWidget(objStatsWidget);
}
QWidget *area = new QWidget(m_scrollArea);
area->setLayout(vlay);
m_scrollArea->setWidget(area);
EDIT: i made something like what you are trying to do some time ago..
so: create a custom QWidget with a QVBoxLayout as member.let's call this object "widgetList". then reimplement all method that you need, as addWidget, takeAt etc.. using your layout as a list
finally set widgetList as widget for your scroll area..
let me know..
I made all this because QWidgetList was not enough easy to use and i needed something else that i have omitted here..
I found my piece of code:
class WidgetList : public QWidget
{
Q_OBJECT
public:
WidgetList(QWidget *parent = 0);
~WidgetList();
void addWidget(QWidget*);
void removeWidget(QWidget*);
QList<QWidget*> getListWidget() const;
QWidget* takeAt(int) const;
int count() const;
private:
QVBoxLayout* layout_;
};
.cpp
WidgetList::WidgetList(QWidget *parent)
: /**/QWidget(parent)
/**/,layout_(new QVBoxLayout(this))
{
this->setLayout(layout_);
}
void WidgetList::removeWidget(QWidget* widget)
{
layout_->removeWidget(widget);
}
void WidgetList::addWidget(QWidget* widget)
{
layout_->addWidget(widget);
}
QWidget* WidgetList::takeAt(int index) const
{
return layout_->takeAt(index)->widget();
}
int WidgetList::count() const
{
return layout_->count();
}
this will be your new Widget with layout where to insert your custom widget..
then i put widgetList as widget of QScrollArea:
QScrollArea* scrollArea = new QScrollArea;
widgetList* list = new widgetList(scrollArea);
scrollArea->setWidget(list);
everything works for me..
EDIT 2: i post my main that works good with my previous code:
QScrollArea* scroll = new QScrollArea;
WidgetList* w = new WidgetList(scroll);
QLabel * label = new QLabel("Label1");
QLabel* label2 = new QLabel("label2");
QTableWidget* table = new QTableWidget(10,10);
w->addWidget(label);
w->addWidget(label2);
w->addWidget(table);
scroll->setWidget(w);
scroll->setWidgetResizable(true);
scroll->show();