Closable QTabWidget tabs, but not all of them - c++

In a Qt/C++ piece of code, I have a QTabWidget class with different tabs.
I would like to add a last "+" tab, so when the user is clicking on it, I create a new tab.
However I would like to have all my tabs closable ('x' at the right of the tab), except the last one where I don't want the 'x' to be displayed. How can I have this granularity in the closable flag ?

Surprised to see that this is not yet answered. Had some time and I have implemented a working example. Note that instead of using one of the tabs as your "+" button, I have used QToolButton thereby making it simpler to make tabs closable with QTabWidget::setTabsClosable(bool)
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTabWidget>
#include <QToolButton>
#include <QLabel>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
QTabWidget* _pTabWidget;
private slots:
void slotAddTab();
void slotCloseTab(int);
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
_pTabWidget = new QTabWidget(this);
this->setCentralWidget(_pTabWidget);
// Create button what must be placed in tabs row
QToolButton* tb = new QToolButton(this);
tb->setText("+");
// Add empty, not enabled tab to tabWidget
_pTabWidget->addTab(new QLabel("Add tabs by pressing \"+\""), QString());
_pTabWidget->setTabEnabled(0, false);
// Add tab button to current tab. Button will be enabled, but tab -- not
_pTabWidget->tabBar()->setTabButton(0, QTabBar::RightSide, tb);
// Setting tabs closable and movable
_pTabWidget->setTabsClosable(true);
_pTabWidget->setMovable(true);
connect(tb,SIGNAL(clicked()),this,SLOT(slotAddTab()));
connect(_pTabWidget,SIGNAL(tabCloseRequested(int)),this,SLOT(slotCloseTab(int)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::slotAddTab()
{
QWidget* newTab = new QWidget(_pTabWidget);
_pTabWidget->addTab(newTab, tr("Tab %1").arg(QString::number(_pTabWidget->count())));
_pTabWidget->setCurrentWidget(newTab);
}
void MainWindow::slotCloseTab(int index)
{
delete _pTabWidget->widget(index);
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

Related

How to identify the pressed button in Qt C++?

I have 4 buttons on my main window. Each button opens its own window with its own data. How to identify the pressed button to open right window? For example: I press sales button and it opens a window that shows information about ticket sales.
Mainwindow ui
Here is my code from mainwindow h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <sales.h>
#include <theatres.h>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void button_pressed();
private:
Ui::MainWindow *ui;
sales *s;
theatres *t;
};
#endif // MAINWINDOW_H
And here is my code from mainwindow cpp:
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include "build/sqlite/sqlite3.h"
#include <QtSql/QSqlDatabase>
#include <QTableView>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect((*ui).pushButton,SIGNAL(released()), this, SLOT(button_pressed()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::button_pressed()
{
s = new sales(this);
s -> show();
}
As Andy Newman already answered
the shortest solution is a lambda function
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPushButton>
#include <QHBoxLayout>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QHBoxLayout *h_layout = new QHBoxLayout;
centralWidget()->setLayout(h_layout);
for(int c =1; c <= 10; c++)
{
QPushButton *button = new QPushButton(this); // create button
button->setText(QString::number(c)); // set button id
h_layout->addWidget(button); // add a button to the form
// lambda magic
/* connecting a button signal to a lambda function that captures a pointer to a
button and invokes an arbitrary type function. */
connect(button, &QPushButton::clicked, [this, button]() {
pressedButton(button->text());
});
}
}
void MainWindow::pressedButton(const QString &id_button)
{
qDebug("Pressed button: %ls", id_button.utf16());
}
MainWindow::~MainWindow()
{
delete ui;
}
#include "widget.h"
#include "./ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(ui->btn_0,&QPushButton::clicked,this,&Widget::SlotButtonClicked);
connect(ui->btn_1,&QPushButton::clicked,this,&Widget::SlotButtonClicked);
connect(ui->btn_2,&QPushButton::clicked,this,&Widget::SlotButtonClicked);
connect(ui->btn_3,&QPushButton::clicked,this,&Widget::SlotButtonClicked);
}
Widget::~Widget()
{
delete ui;
}
void Widget::SlotButtonClicked()
{
auto sender = this->sender();
if ( sender == ui->btn_0 ) {
// Click btn_0 to open widget0
} else if ( sender == ui->btn_1 ) {
// Click btn_1 to open widget1
} else if ( sender == ui->btn_2 ) {
// Click btn_2 to open widget2
} else if ( sender == ui->btn_3 ) {
// Click btn_3 to open widget3
}
}
If you can use Qt Designer, the best way to do this is to click with button right on the QPushButton (On .ui file in Qt Designer) and click to "Go to Slot", this will create a private slot to this button! In the header file will create the definition, like this:
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
void on_pushButton_4_clicked();
And in the source file (.cpp) will create the "function" clicked pushButton:
void MainWindow::on_pushButton_clicked()
{
}
void MainWindow::on_pushButton_2_clicked()
{
}
void MainWindow::on_pushButton_3_clicked()
{
}
void MainWindow::on_pushButton_4_clicked()
{
}
Inside of the "function" in .cpp, you put the task that you want this button to do, in this case, to open a new window!
When you click "go to slot" in another button, will create another private slot with the respective number (If is the second QPushButton that you create, the private slot will be called by pushButton_2).
The usual way to do this would be to connect the 4 different buttons to 4 different slots. It looks like you are using QtDesigner so that shouldn't be an issue.
If you were generating an array of buttons at run time you'd run into problems and would need a different solution. You could try something like this to pass an array index to the function, for example:
connect(button[x], &QPushButton::clicked, this, [this, x]() { button_pressed(x); });
Or you could do it the Qt way, which would be to call ::setProperty to store data in the button, and then retrieve it from the event, but it's so esoteric that I can't actually remember how to do that...

QMdiArea is returning the wrong height and width

I am trying to arrange my subWindows in the QMdiArea vertically. I saw lot of examples online and they all were doing the same thing as I am doing here.
I have two textEdits which needs to be tiled vertically both covering half of the screen. So in the constructor of the MainWindow I add the two textEdits as subWindow to the qMdiArea and then find the height and width of the qMdiArea divide the height by 2 and resize the subWindow. Please see the code below.
My mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->showMaximized();
qMdiArea = new QMdiArea();
qTextEdit1 = new QTextEdit();
qTextEdit2 = new QTextEdit();
setCentralWidget(qMdiArea);
qMdiArea->adjustSize();
qMdiArea->addSubWindow(qTextEdit1);
qMdiArea->addSubWindow(qTextEdit2);
QPoint position(0, 0);
foreach (QMdiSubWindow *window, qMdiArea->subWindowList())
{
QRect rect(0, 0, qMdiArea->width(), qMdiArea->height() / qMdiArea->subWindowList().count());
window->setGeometry(rect);
window->move(position);
position.setY(position.y() + window->height());
}
}
MainWindow::~MainWindow()
{
delete ui;
}
My window.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMdiArea>
#include <QTextEdit>
#include <QPoint>
#include <QMdiSubWindow>
#include <QRect>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
QMdiArea *qMdiArea;
QTextEdit *qTextEdit1;
QTextEdit *qTextEdit2;
};
#endif // MAINWINDOW_H
and my Main File :
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
But its not happening as expected. The window just occupy a part of the screen though they are tiled vertically. My screen resolution is 1920x1200
The height() and width() of mdiArea are invalid at that stage, because the widget hasn't been exposed/shown yet. Calling show() only schedules a widget for display, the act of sizing it and showing it on screen happens later when the control has returned to the event loop.
As a solution, you can override the resizeEvent handler. Once you do, your project will work again:
Definition in mainwindow.h:
virtual void resizeEvent(QResizeEvent *ev) override;
Implementation in mainwindow.cpp:
void MainWindow::resizeEvent(QResizeEvent *ev)
{
Q_UNUSED(ev)
QPoint position(0, 0);
foreach (QMdiSubWindow *window, qMdiArea->subWindowList())
{
QRect rect(0, 0, qMdiArea->contentsRect().width(), qMdiArea->contentsRect().height() / qMdiArea->subWindowList().count());
window->setGeometry(rect);
window->move(position);
position.setY(position.y() + window->height());
}
}
Also it seems that you don't really need to have this->showMaximized(); call inside MainWindow's constructor. You can call it from main.cpp, for example.

QComboBox currentIndex() to hide other widgets

What it should do: When the QComboBox is on the first item (index 0) it should hide the QStackedWidget. Which causes the QComboBox to extend as much as possible. As soon as you change the item in the QComboBox to anything, the QComboxBox should shrink and the QStackedWidget should display the correct page.
What I have and what it does instead: I test what the current index item is of the QComboBox and depending on that I change the size policies and visibility of widgets in order to obtain what I want. But it doesn't work. I tried to do workarounds but I cant seem to figure this out.
I also used qDebug() to see what currentIndexItem returns, and it seems to be stuck at 0, no matter to what index I change the QComboBox to. Do I have to update the currentItemIndex?
Note: I have a signal connected in the designer from QComboBox to QStackedWidget: currentIndexChanged(int) -> setCurrentIndex(int)
Here is my code.
My code:
interfacewindow.h
#ifndef INTERFACEWINDOW_H
#define INTERFACEWINDOW_H
#include <QMainWindow>
#include <QPainter>
#include <QComboBox>
namespace Ui
{
class InterfaceWindow;
}
class InterfaceWindow : public QMainWindow
{
Q_OBJECT
public:
explicit InterfaceWindow(QWidget *parent = 0);
~InterfaceWindow();
private:
Ui::InterfaceWindow *ui;
private slots:
void on_actionClose_triggered();
};
#endif // INTERFACEWINDOW_H
interfacewindow.cpp
#include "interfacewindow.h"
#include "ui_interfacewindow.h"
#include <QDebug>
InterfaceWindow::InterfaceWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::InterfaceWindow)
{
ui->setupUi(this);
if(ui->comboBox->currentIndex() == 0)
{
ui->comboBox->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
ui->stackedWidget->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
ui->stackedWidget->setVisible(false);
}
else
{
ui->comboBox->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
ui->stackedWidget->setVisible(true);
}
}
InterfaceWindow::~InterfaceWindow()
{
delete ui;
}
void InterfaceWindow::on_actionClose_triggered()
{
this->close();
}
main.cpp
#include <QApplication>
#include "interfacewindow.h"
int main(int argc, char **argv)
{
QApplication a(argc, argv);
InterfaceWindow w;
w.show();
return a.exec();
}

Segmentation Fault while dynamicaly adding widget in Qt

I was trying to add a QWidget while runtime in Qt but It is showing SIGSEV signal received from OS because of segmentation fault.
Here is my code:
//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QLabel>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QtGui>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_submit_clicked();
private:
Ui::MainWindow *ui;
QLabel *label;
QLineEdit *line_edit;
};
#endif // MAINWINDOW_H
//mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_submit_clicked()
{
QString str = ui->lineEdit1->text();
QString str1 =ui->lineEdit2->text();
if(str=="rana"&&str1=="vivek")
{
label = new QLabel();
label->setText("Success");
MainWindow.layout->addWidget(label);
label->show();
}
else
{
line_edit = new QLineEdit();
line_edit->setText("Sorry");
MainWindow.layout->addWidget(line_edit);
line_edit->show();
}
}
//main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
I know that segmentation fault occurs due to dereferencing of a null pointer but i couldn't find where I have done that mistake.Any Suggestions?
MainWindow.layout->addWidget(label);
doesn't make a lot of sense - this should not even compile, as Sebastian noted.
First, make sure you have layout in the Ui file (I added one vertical layout named verticalLayout), so you have a layout where you will add widgets. You will have a pointer to it inside your ui object.
Now, just use addWidget on that layout and everything should work:
void MainWindow::on_pushButton_submit_clicked()
{
QString str = ui->lineEdit1->text();
QString str1 =ui->lineEdit2->text();
if(str=="rana"&&str1=="vivek")
{
QLabel *label = new QLabel();
label->setText("Success");
ui->verticalLayout->addWidget(label);
// label->show(); widgets will became the part of the MainWindow, as the addWidget
// will add them into the hierarchy.
}
else
{
QLineEdit *line_edit = new QLineEdit();
line_edit->setText("Sorry");
ui->verticalLayout->addWidget(line_edit);
// line_edit->show()
}
}
Note - addWidget will set the owner of the widget to be the layout, so the widget will be deleted on the destruction of the layout.
Maybe implementing in this way will make sense?
void MainWindow::on_pushButton_submit_clicked()
{
QString str = ui->lineEdit1->text();
QString str1 =ui->lineEdit2->text();
QWidget *w = new QWidget(this);
QVBoxLayout *layout = new QVBoxLayout; // creates a vertical layout
if(str=="rana"&&str1=="vivek")
{
label = new QLabel(w);
label->setText("Success");
layout->addWidget(label);
}
else
{
line_edit = new QLineEdit(w);
line_edit->setText("Sorry");
layout->addWidget(line_edit);
}
w->setLayout(layout);
setCentralWidget(w);
}
UPDATE:
QMainWindow already has a predefined layout, so it was needless to introduce a new one. The code above creates an intermediate widget and construct it using its own layout. Than the widget set as a central widget in the MainWindow.

Qt: Custom widget in QScrollArea

I am attempting to create a custom widget. My Widget renders itself unless it is inside a scroll area. The code below works. If I change the if(0) to an if(1) inside the MainWindow constructor, it will not render the "Hello World" string. I assume that I must (re)implement some additional methods, but so far I have not been able to find the correct ones with trial and error.
// hellowidget.h
#ifndef HELLOWIDGET_H
#define HELLOWIDGET_H
#include <QtGui>
class HelloWidget : public QWidget
{
Q_OBJECT
public:
HelloWidget(QWidget *parent = 0);
void paintEvent(QPaintEvent *event);
};
#endif // HELLOWIDGET_H
// hellowidget.cpp
#include "hellowidget.h"
HelloWidget::HelloWidget(QWidget *parent)
: QWidget(parent)
{
}
void HelloWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.drawText(rect(), Qt::AlignCenter, "Hello World");
}
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtGui>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private:
};
#endif // MAINWINDOW_H
// mainwindow.cpp
#include "mainwindow.h"
#include "hellowidget.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
HelloWidget *hello = new HelloWidget;
QWidget *central = hello;
if( 0 )
{
QScrollArea *scroll = new QScrollArea ;
scroll->setWidget(hello);
central = scroll;
}
setCentralWidget( central );
}
MainWindow::~MainWindow()
{
}
// main.cpp
#include <QtGui/QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
You just have to give your HelloWidget a size and place.
Add this line to your code.
hello->setGeometry(QRect(110, 80, 120, 80));
Or if you want to fill the scroll area with your widget:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QScrollArea *const scroll(new QScrollArea);
QHBoxLayout *const layout(new QHBoxLayout(scroll));
HelloWidget *const hello(new HelloWidget);
hello->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
layout->addWidget(hello);
setCentralWidget( scroll );
}
Per Qt docs, "When using a scroll area to display the contents of a custom widget, it is important to ensure that the size hint of the child widget is set to a suitable value. If a standard QWidget is used for the child widget, it may be necessary to call QWidget::setMinimumSize() to ensure that the contents of the widget are shown correctly within the scroll area."
Does it work right if you follow these instructions?
I was pulling my hair out over this also, but eventually found QScrollArea's setWidgetResizable, which made the QScrollArea allow my widget to expand to take up the available space.