How to add a QToolBar inside a QTabWidget - c++

I have been trying to add a QToolBar inside a QTabWidget in order to achieve something like the image below, so that everytime I add a new QTabWidget I have also a related QToolBar inside it.
Everything seems to work fine, I create a QAction to link it to the QTabWidget and according to this post it seems to be possible to do that but the problem is that when I compile nothing shows up as shows below:
Below is what I have done so far:
mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void onChangeTab(int index);
void newTab();
void closeTab(const int &index);
private slots:
void on_addTabBtn_clicked();
void on_tabWidget_tabCloseRequested(int index);
private:
Ui::MainWindow *ui;
QAction *addTab1;
QToolBar *mToolBar1;
QAction *addIconToolBar1;
};
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->tabWidget->clear();
ui->tabWidget->setTabsClosable(true);
ui->tabWidget->addTab(new QLabel("Add"), QString("Add"));
ui->toolBar->setContextMenuPolicy(Qt::ActionsContextMenu);
mToolBar1 = new QToolBar;
addIconToolBar1 = new QAction;
addIconToolBar1->setIcon(QIcon("qrc:/cardio.png"));
ui->toolBar->addWidget(mToolBar1);
ui->toolBar->addAction(addIconToolBar1);
connect(ui->addTabBtn, &QPushButton::clicked, this, [&] { ui->tabWidget->addTab(new QLabel("Add"), QString("Add")); });
connect(ui->tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_addTabBtn_clicked()
{
int index = 0;
if(index == this->ui->tabWidget->count() - 1) {
newTab();
}
}
void MainWindow::on_tabWidget_tabCloseRequested(int index)
{
ui->tabWidget->removeTab(index);
}
I tried to solve the problem in many ways and researched what the cause might be. I came across several references such as this one, which is the most important I found as the user seems to be doing it but there is no reference to documentation or no example to code to understand/study through.
Thanks for pointing to the right direction to solve this issue.

You can simply do something like this, and it really works.
QToolBar *toolbar=new QToolBar("toolbar",ui->tab);
toolbar->addAction("action1");
toolbar->addAction("action2");
enter image description here

I don't see where you are trying to add Toolbar to your TabWidget...
You must define Layout, add your toolbar to that layout and finally set layout to your tabWidget.
Try to do something like this, in your mainwindow constructor.
QHBoxLayout* tabWidgetLayout = new QHBoxLayout;
tabWidgetLayout->addWidget( your toolbar);
tabwidget->setLayout(tabWidgetLayout);
Also don't forget to include QHBoxLayout's header.
Even if other answers may seem to work, this is actually the right way to do what you asked for.

Related

Passing arguments to a slot in qt5 c++ [duplicate]

This question already has answers here:
Passing an argument to a slot
(6 answers)
Closed 6 years ago.
I'm creating a ToDo list app in c++ qt. When a plus button is pressed, it adds a QHBoxLayout containing a QLabel and a QToolButton to a vertical layout inside my gui, so I get new boxes with the 'ToDos' inside them, and buttons to remove them next to them. I have set up the various widgets inside my slot which is called when the add button is clicked. However, I need to pass them as arguments to the slot which is called when a remove button is pressed. I have researched already, and all I have found is QSignalMapper. However, I cannot find any cases close enough to mine to replicate, and I have read it only works with certain arguments, and not the three I need (QHBoxLayout, QLineEdit and QToolButton).
Some of the code for the slot which is called when the 'add' button is pressed is:
//Creates a read only LineEdit which the user will add
QLineEdit *toDoBox = new QLineEdit(this);
toDoBox->setText(ui->lineEdit->text());
toDoBox->setReadOnly(true);
//Creates a new X button for removal of ToDo's
QToolButton *removeButton = new QToolButton;
removeButton->setText("X");
//Adds a horizontal layout with the ToDo and the remove button in it, to keep them together
QHBoxLayout *toDoLayout = new QHBoxLayout;
toDoLayout->addWidget(toDoBox);
toDoLayout->addWidget(removeButton);
//Removes a ToDo when the remove button is clicked
connect(removeButton, SIGNAL(clicked()), this, SLOT(on_removeButton_clicked()));
My code is hosted on GitHub if you want to see the whole project:
https://github.com/DanWilkes02/ToDoList
Thanks for bearing with me- I struggle explaining things that are so clear in my head!
If I understand well your problem, you want to get the allocated objects which represent a todo in order to free them and to update your View.
You could achieve this by simply wrapping your QLineEdit, QToolButton and QHBoxLayout objects into a class, and use a container (a vector for instance) in your ToDoList class. That way, you push_back your "todo object" each time you press the on_toolButton_clicked method.
Then, you simply have to use a signal with an index triggering an on_delete_todo slot which deletes a "todo object" from your vector and update the view.
Also, take a look at this Qt Model-View Programming
Here is a sample (tested and working under QT5):
Your Todo Widget
#ifndef TODOVIEW_H
#define TODOVIEW_H
#include <QString>
class QLineEdit;
class QToolButton;
class QHBoxLayout;
#include <QWidget>
class TodoView : public QWidget
{
Q_OBJECT
private:
QLineEdit* frame;
QToolButton* removeButton;
QHBoxLayout* toDoLayout;
int index;
public:
TodoView(const QString& what, int index, QWidget* parent);
~TodoView();
inline void setIndex(int i) { index = i; }
inline int getIndex(){ return index; }
private slots:
void emitIndex();
signals:
void selectedIndex(int);
};
#endif // TODOVIEW_H
#include "todoview.h"
#include <QLineEdit>
#include <QToolButton>
#include <QHBoxLayout>
TodoView::TodoView(const QString& what, int index, QWidget* parent) : QWidget(parent), index(index)
{
frame = new QLineEdit(this);
frame->setText(what);
frame->setReadOnly(true);
removeButton = new QToolButton(this);
removeButton->setText("X");
toDoLayout = new QHBoxLayout(this);
toDoLayout->addWidget(frame);
toDoLayout->addWidget(removeButton);
connect(removeButton, SIGNAL(clicked()), this, SLOT(emitIndex()));
}
TodoView::~TodoView() {}
void TodoView::emitIndex()
{
emit selectedIndex(getIndex());
}
Your MainWindow
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <vector>
class TodoView;
class QVBoxLayout;
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void addTodo();
void delTodo(int);
private:
Ui::MainWindow* ui;
QVBoxLayout* vBoxLayout;
std::vector<TodoView*> todoView;
int max = -1;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "todoview.h"
#include <QVBoxLayout>
#include <QAction>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
vBoxLayout = new QVBoxLayout(centralWidget());
QAction* add = new QAction(ui->mainToolBar);
ui->mainToolBar->addAction(add);
connect(add, SIGNAL(triggered()), this, SLOT(addTodo()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::addTodo()
{
if(max > 9)
{
// Error msg.
}
else
{
TodoView* tdV = new TodoView("Yolo", max, centralWidget());
connect(tdV, SIGNAL(selectedIndex(int)), this, SLOT(delTodo(int)));
vBoxLayout->addWidget(tdV);
todoView.push_back(tdV);
++max;
}
}
void MainWindow::delTodo(int i)
{
// check if i < todoView.size().
delete todoView.at(i);
// update vector indexes !!!
--max;
}
I have edited this piece of code rapidly, I may have made several mistakes, but you have an idea of at least one solution.
It is also possible to use a fixed size for the vector (better solution). Setting the TodoView deleted objects to nullptr in the vector and search for nullptr when you want to add new Todo view components:
In the MainWindow constructor
todoView.reserve(10);
for(std::size_t i = 0; i < 10; ++i)
{
todoView[i] = nullptr;
}
In the addTodo Slot
// Do not use push back.
// retrieve the max index.
// if < 10
for(std::size_t i = 0; i < todoView.size(); ++i)
{
if(todoView[i] == nullptr)
{
// allocate TodoView and affect it to the i° element
}
}
In the delTodo slot
delete todoView[i];
todoView[i] = nullptr;
Using a vector of pair is also possible (a pair of int TodoView).
I can think of two approaches you can go with.
Using QObject::sender().
Using QSignalMapper.
QObject::sender()
This static method returns QObject * of the sender object who emitted the signal. In this case QToolButton, so you can use this reference to find its parent (i.e. QHBoxLayout) and its sibling (i.e. QLineEdit).
QSingalMapper
First define a list for saving references to all rows, then assign each row an unique identifier (e.g. row number), then according to the official documentation's example use this identifier as QSignalMapper key for each button. Now if user clicks a button, in your slot, you will be given the same identifier, use it and lookup the whole row in the list of rows.

Keep QDialog open when parent is minimized?

I have a QMainWindow opening a QDialog (not modal). When I minimize the main window, the dialog is closed as well. Can I somehow keep it open? (the other way round as in Keep QMainWindow minimized when QDialogs show() ).
One thing I have tried is to ignore the event, but to my surprise I never see such a state. Actually I only see ActivationChange (0n99) there.
void CMyDialog::changeEvent(QEvent *evt)
{
QEvent::Type t = evt->type();
if (t == QEvent::WindowStateChange)
{
evt->ignore();
hide();
}
else
{
QDialog::changeEvent(evt);
}
}
Question in Qt center dealing with a similar topic:
http://www.qtcentre.org/threads/24765-Intercept-minimize-window-event
Here I create it as member:
QScopedPointer<MyDialog> m_navigator{new MyDialog(this)}; // this here is the main application window
It is displayed by a public slot:
void MyDialog::toogleNavigator()
{
this->setVisible(!this->isVisible());
}
and is a QDialog derived class:
class MyDialog : public QDialog { ...
---- Edit 2 ------
First Wouter has mentioned it , then Alexander. It is like you guys say, if I pass no parent (as in Alexander`s minimal example), the dialog stays open - with parent it is minimized along with its parent. My apologizes Wouter.
However, in my case it does not work like that. So I did not turn Wouter`s comment without checking or in bad intention. Now it is my duty to find out why. I suspect some utility classes to alter the dialog. I will report back here when I have found the root cause.
Ok, it is the windows flags. If the dialog is a tool window, it is always minimized, as normal window it depends on the parent.
Try to create MyDialog without this(MainApplication) like parent
and may be play with a second parameter of the constructor.
new MyDialog(0/*, ?*/);
Addition It is working code
MainWindow.cpp
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
QScopedPointer<Dialog> dialog;
};
MainWindow.hpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
dialog(new Dialog(0))
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
dialog->setVisible(!dialog->isVisible());
}

Qt build menu for touch screen

i want to build a QT application for windows/android.
I have one mainwindow in which i have different buttons to call submenus, as you can see on the pictures. What do you think is the best implementation for that?
I thought about replacing the central widget
Using QStackwidget
Or open a new widget on the same position and size and close after.
What do you think? Do you have some favourite implementation or do i miss some important?
Edit
My Mainwindow constructor
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_stacked = new QStackedWidget;
HomeScreen *homescreen=new HomeScreen(m_stacked);
Control *manual=new Control(m_stacked);
m_stacked->addWidget(homescreen);
m_stacked->addWidget(manual);
this->setCentralWidget(m_stacked);
}
Implementation of every Screen
class HomeScreen : public QWidget
{
Q_OBJECT
public:
explicit HomeScreen(QStackedWidget* stack, QWidget *parent = 0);
~HomeScreen();
private slots:
void on_pushButton_clicked();
private:
Ui::HomeScreen *ui;
QStackedWidget *m_stacks;
};
HomeScreen::HomeScreen(QStackedWidget* stack,QWidget *parent) :
m_stacks(stack),QWidget(parent),
ui(new Ui::HomeScreen)
{
ui->setupUi(this);
}
HomeScreen::~HomeScreen()
{
delete ui;
}
void HomeScreen::on_pushButton_clicked()
{
m_stacks->setCurrentIndex(1);
}
What is your solution to change the current Widget, inside a widget of the QStackwidget? I used the solution above what do you think about it?
QStackedWidget was made right for that, I don't see any reason why you should incline to other options. You can always removeWidget(widget) and delete widget; if you want to free the memory of some rarely used menu.
Edit:
Your code is fine, there can be only a few enhancements made. You can create enum for your indexes in some separate header file.
Option 1:
If you use only QStackedWidget as a parent menu, you can adjust the constructor:
HomeScreen(QStackedWidget* parent) :
QWidget(parent),
ui(new Ui::HomeScreen)
{
ui->setupUi(this);
}
If you'd want to access QStackedWidget and change an index, you spare m_stacks pointer and use the parent: dynamic_cast<QStackedWidget*>(parent())->setCurrentIndex(1 /* or enum */);.
Option 2:
Leave index changing to the parent MainWindow. Each menu will have a request signal:
signals:
void goToMenuRequest(int index /* or enum */);
which you connect to m_stacked's setCurrentIndex in the constructor of MainWindow.
Not a big things, but It'll make coding easier.

Qt: Change application QMenuBar contents on Mac OS X

My application uses a QTabWidget for multiple 'pages', where the top-level menu changes depending on what page the user is on.
My issue is that attempting to re-create the contents of the menu bar results in major display issues. It works as expected with the first and third style (haven't tested second, but I'd rather not use that style) on all platforms except for Mac OS X.
The first menu is created in the way I create most in the application, and they receive the correct title, but disappear as soon as the menu is re-created.
The second menu appears both on the initial population and the re-population of the menu bar, but in both cases has the label "Untitled". The style for the second menu was only created when trying to solve this, so it's the only way I've been able to have a menu stick around.
The third dynamic menu never appears, period. I use this style for dynamically populating menus that are about to show.
I have tried deleting the QMenuBar and re-creating one with
m_menuBar = new QMenuBar(0);
and using that as opposed to m_menuBar->clear() but it has the same behavior.
I don't have enough reputation to post images inline, so I'll include the imgur links:
Launch behavior: http://i.imgur.com/ZEvvGKl.png
Post button-click behavior: http://i.imgur.com/NzRmcYg.png
I have created a minimal example to reproduce this behavior on Mac OS X 10.9.4 with Qt 5.3.
mainwindow.cpp
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
m_menuBar = new QMenuBar(0);
m_dynamicMenu = new QMenu("Dynamic");
connect(m_dynamicMenu, SIGNAL(aboutToShow()), this, SLOT(updateDynamicMenu()));
changeMenuBar();
QPushButton *menuBtn = new QPushButton("Test");
connect(menuBtn, SIGNAL(clicked()), this, SLOT(changeMenuBar()));
setCentralWidget(menuBtn);
}
void MainWindow::changeMenuBar() {
m_menuBar->clear();
// Disappears as soon as this is called a second time
QMenu *oneMenu = m_menuBar->addMenu("One");
oneMenu->addAction("foo1");
oneMenu->addAction("bar1");
oneMenu->addAction("baz1");
// Stays around but has 'Untitled' for title in menu bar
QMenu *twoMenu = new QMenu("Two");
twoMenu->addAction("foo2");
twoMenu->addAction("bar2");
twoMenu->addAction("baz2");
QAction *twoMenuAction = m_menuBar->addAction("Two");
twoMenuAction->setMenu(twoMenu);
// Never shows up
m_menuBar->addMenu(m_dynamicMenu);
}
void MainWindow::updateDynamicMenu() {
m_dynamicMenu->clear();
m_dynamicMenu->addAction("foo3");
m_dynamicMenu->addAction("bar3");
m_dynamicMenu->addAction("baz3");
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtWidgets>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
private slots:
void changeMenuBar();
void updateDynamicMenu();
private:
QMenuBar *m_menuBar;
QMenu *m_dynamicMenu;
};
#endif // MAINWINDOW_H
All this looks like Qt bug on OS X. And it's very old bug, actually.
You can do workaround and don't work with QMenu via QMenuBar::addMenu function calls, as you do here:
m_menuBar->addMenu("One");
Instead of this work with QAction retrieved from QMenu by creation of the QMenu instance dynamically and then calling of QMenuBar::addAction for the QAction instance retrieved by QMenu::menuAction, as following:
m_menuBar->addAction(oneMenu->menuAction());
Beside QMenuBar::addAction you can use QMenuBar::removeAction and QMenuBar::insertAction if you want to make creation only of some specific menu items dynamically.
Based on your source code here it's modified version of it which deals with all menus dynamic creation on every button click (you do this in your source code) and the menu 'Dynamic' is populated with different count of items every time you click the button.
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtWidgets>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
private slots:
void changeMenuBar();
private:
QMenuBar *m_menuBar;
QMenu *m_dynamicMenu;
int m_clickCounter;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),
m_clickCounter(1)
{
m_menuBar = new QMenuBar(this);
connect(m_dynamicMenu, SIGNAL(aboutToShow()), this, SLOT(updateDynamicMenu()));
changeMenuBar();
QPushButton *menuBtn = new QPushButton("Test");
connect(menuBtn, SIGNAL(clicked()), this, SLOT(changeMenuBar()));
setCentralWidget(menuBtn);
}
void MainWindow::changeMenuBar() {
++m_clickCounter;
m_menuBar->clear();
QMenu *oneMenu = new QMenu("One");
oneMenu->addAction("bar1");
oneMenu->addAction("baz1");
m_menuBar->addAction(oneMenu->menuAction());
QMenu *twoMenu = new QMenu("Two");
twoMenu->addAction("foo2");
twoMenu->addAction("bar2");
twoMenu->addAction("baz2");
m_menuBar->addAction(twoMenu->menuAction());
m_dynamicMenu = new QMenu("Dynamic");
for (int i = 0; i < m_clickCounter; ++i) {
m_dynamicMenu->addAction(QString("foo%1").arg(i));
}
m_menuBar->addAction(m_dynamicMenu->menuAction());
}
Additionally while developing menus logic for OS X it's good to remember:
It's possible to disable QMenuBar native behavior by using QMenuBar::setNativeMenuBar
Because of turned on by default QMenuBar native behavior, QActions with standard OS X titles("About","Quit") will be placed automatically by Qt in the predefined way on the screen; Empty QMenu instances will be not showed at all.
I think your problem is this line:
QMenu *oneMenu = m_menuBar->addMenu("One");
To add menus to a menubar you'd want code as follows:
QMenuBar *m = new QMenuBar;
m->addMenu( new QMenu("Hmmm") );
m->show();
To create menus, and then add actions, and then add the menu to the menu bar:
QMenu *item = new QMenu( "Test1" );
item->addAction( "action1" );
QMenuBar *t = new QMenuBar;
t->addMenu( item );
t->show();

Is there any standard way to create drop-down menu from QLineEdit without QCompleter?

Is there any standard way to create drop-down menu from QLineEdit without QCompleter? For example, using QMenu or creating own class. Or there are any other existing widgets?
Or maybe I should use QAbstractItemModel for QCompleter? I've thought about it, but I don't really understand this QAbstractItemModel. If you have experience about creating menu in this way, please also help me.
So I need a common type of drop-down menu: menu with lines, everyone of which includes icon (QPixmap) and text (QLabel) in itself. It's like in Opera or Chrome browser in address input line, like the right part of Apple Spotlight etc.
It's not possible with QMenu because it catch focus when showed and hides when loses focus. However, it's possible to use QListWidget (or any other regular widget) for this. I developed some working example for the proof of concept. It's default Qt Widget project with QMainWindow as main window. You need to add QLineEdit with name "lineEdit" into it and create slot for textChanged signa. Here's the code:
MainWindow.h:
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_lineEdit_textChanged(const QString &arg1);
private:
Ui::MainWindow *ui;
QListWidget* list;
};
MainWindow.cpp:
#include "MainWindow.h"
#include "ui_MainWindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
list(new QListWidget)
{
ui->setupUi(this);
list->setWindowFlags(Qt::WindowFlags(
Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint));
list->setAttribute(Qt::WA_ShowWithoutActivating);
}
MainWindow::~MainWindow() {
delete list;
delete ui;
}
void MainWindow::on_lineEdit_textChanged(const QString &arg1) {
if (ui->lineEdit->text().isEmpty()) {
list->hide();
return;
}
list->clear();
list->addItem(ui->lineEdit->text());
list->addItem(tr("Google: ") + ui->lineEdit->text());
list->move(ui->lineEdit->mapToGlobal(QPoint(0, ui->lineEdit->height())));
if (!list->isVisible()) list->show();
}
There are several problems: you should hide menu when line edit loses focus or user move window, you can't set focus on the menu using down arrow button from line edit, etc. But I believe all these issues can be solved easily.
From what you describe, you could try an editable QComboBox: It has its own model and view, its own completer, and can display icons.
QComboBox *comboBox = new QComboBox;
...
comboBox->setEditable(true);
// The completer popup isn't enabled by default
comboBox->completer()->setCompletionMode(QCompleter::PopupCompletion);
And since that QCompleter can display icons, I guess you can use a regular QLineEdit with a QCompleter and a model with icons. For the model, you can use a QStandardItemModel.