Why aren't my QBoxLayouts working? - c++

So, I took it down a few notches and am trying simple programs to get the hang of Qt's tools.
First I tried a simple label inside main() function, then a button inside it. All good.
Next, I tried the same, but inside a main window (using the Qt's created documents). After the one-button program worked, I did a two-button program, that simple. Worked.
Then, I tried Qt's Box Layouts.
In none of these "main window" tries, I changed the main.cpp file created by Qt.
Here's the mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private:
QPushButton *button1;
QPushButton *button2;
};
#endif // MAINWINDOW_H
Next, the mainwindow.cpp file:
#include "mainwindow.h"
#include <QPushButton>
#include <QHBoxLayout>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
setWindowTitle(tr("Two Button Example"));
resize(400,300);
button1 = new QPushButton(tr("Bye!"));
button1->setGeometry(0,0,200,30);
//button1->setParent(this);
connect(button1,SIGNAL(clicked()),this,SLOT(close()));
button1->show();
button2 = new QPushButton(tr("Hide B1!"));
button2->setGeometry(0,0,200,30);
//button2->setParent(this);
connect(button2,SIGNAL(clicked()),button2,SLOT(hide()));
button2->show();
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
setLayout(layout);
}
MainWindow::~MainWindow()
{
}
I'm studying this book (for the layouts specifically, Chapter 2, the Find Dialog example (page 16 of the book, or 34 from the pdf file): C++ GUI Programming with Qt 4 1st ed.pdf
For this specific problem, I also used this: QHBoxLayout - Qt Examples and Tutorials
What I noticed:
Commenting the QHBoxLayout part, all the way to the "setLayout" function, makes no difference in the program. No test I've done is affected by this QBoxLayout thing;
The "setGeometry" function sets the position (first two parameters) and size (width and heightm, last two parameters) of a widget. This position is related to the parent widget, which may be the screen itself when no parent is assigned;
When the "setParent" functions for the buttons are commented and the "show" function is uncommented, the buttons are shown each in a separate window from the MainWindow. When I uncomment the "setParent" functions, the buttons are shown inside the MainWindow, and there's no difference if there are or no "show" functions for the buttons. The detail is, for the book I referenced, no example so far have had the need to declare the parent widgets, nor did the example in Qt's Examples and Tutorials site;
If I don't use the "setGeometry" function, the buttons are big, like 600x600, whatever. They follow the rules above regarding the other functions, but they are huge and always one on top of the other, not side by side. The function "sizeHint()" used extensively in the book, also has no effect;
Appearently, it's all following the same syntaxes and rules of the examples. So, what am I doing wrong, or not doing here? If it's not a problem to enlight me with the "sizeHint()" function, too, that would be great.
Thanks in advance!

I don't even remember the origin of the problem, but I have never been able to work with the layouts right in the window. In company I worked with Qt we used a central widget for managing layouts, so the diagram is: window -> central widget -> layouts -> subwidgets. If I modified your code so, it would look like this:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
setWindowTitle(tr("Two Button Example"));
resize(400,300);
auto *parentWidget = new QWidget;
auto button1 = new QPushButton(tr("Bye!"), parentWidget);
button1->setGeometry(0,0,200,30);
connect(button1,SIGNAL(clicked()),this,SLOT(close()));
auto button2 = new QPushButton(tr("Hide B1!"), parentWidget);
button2->setGeometry(0,0,200,30);
connect(button2,SIGNAL(clicked()),button2,SLOT(hide()));
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
parentWidget->setLayout(layout);
setCentralWidget(parentWidget);
}
Also, I don't quite understand why do you try to set the button size if the layout resets it later. To change the size inside a layout you must change the sizeHint of a widget:
setWindowTitle(tr("Two Button Example"));
resize(400,300);
auto *parentWidget = new QWidget;
auto button1 = new QPushButton(tr("Bye!"), parentWidget);
button1->setMaximumSize(20, 20);
connect(button1,SIGNAL(clicked()),this,SLOT(close()));
auto button2 = new QPushButton(tr("Hide B1!"), parentWidget);
button2->setMaximumSize(20, 20);
connect(button2,SIGNAL(clicked()),button2,SLOT(hide()));
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
parentWidget->setLayout(layout);
setCentralWidget(parentWidget);

After some observation, I believe I know why it wasn't working.
In the examples provided, the person was using QDialog or applying the layouts straight to the main() code, while I was using the QMainWindow class.
I suppose that when you use a QMainWindow class, you always have to set a Central Widget in order to make layouts work (setting this central widget layout).
So a solution would be:
QWidget centerWidget = new QWidget();
setCentralWidget(centerWidget);
//Add buttons, lists, and whatever
/*Add a QHBoxLayout or a QVBoxLayout and use
layout->addWidget() to add the widgets created,
like the examples. Then:*/
centerWidget->setLayout(layout);
It works, like Victor Polevoy suggested. Just answering as I believe I understood why weren't my QLayouts working.

Related

Qt - QLineEdit doesn't update the page and URL

I've got a small problem with my web browser project. Whenever I enter the URL address (via QLineEdit), the browser doesn't show the page, and whenever I change the page (via click on-site with starting page included) the address doesn't show up on the URL bar.
Here's my mainwindow.cpp code. The program executes and exits with code 0. I tried using qDebug inside the functions (changeUrlBar(QUrl) and setUrl()) and it turns out that the program enters these functions but they don't do anything. Every advice would be very appreciated.
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
browserView(new QWebEngineView),
urlBar(new QLineEdit)
{
ui->setupUi(this);
//
// initialization of widgets and layouts
// widgets
QWidget *browserWindow = new QWidget(this);
QLineEdit *urlBar = new QLineEdit;
QProgressBar *progressBar = new QProgressBar;
// WebEngineView - actual web browser
QWebEngineView *browserView = new QWebEngineView(parent);
// layouts
QVBoxLayout *mainLayout = new QVBoxLayout;
QHBoxLayout *topBarLayout = new QHBoxLayout;
// push buttons
QPushButton *buttonBack = new QPushButton("Back");
QPushButton *buttonForward = new QPushButton("Forward");
QPushButton *buttonReload = new QPushButton("Reload");
//
// creating the widgets and layouts
// top bar
topBarLayout->addWidget(buttonBack);
topBarLayout->addWidget(buttonForward);
topBarLayout->addWidget(buttonReload);
topBarLayout->addWidget(urlBar);
// main layout of the browser
mainLayout->addLayout(topBarLayout);
mainLayout->addWidget(progressBar);
mainLayout->addWidget(browserView);
browserWindow->setLayout(mainLayout);
setCentralWidget(browserWindow);
//
// connecting slots and signals
// internal connections
connect(buttonBack, SIGNAL(clicked()), browserView, SLOT(back()));
connect(buttonForward, SIGNAL(clicked()), browserView, SLOT(forward()));
connect(buttonReload, SIGNAL(clicked()), browserView, SLOT(reload()));
connect(browserView, SIGNAL(loadProgress(int)), progressBar, SLOT(setValue(int)));
// browser connections
connect(browserView, SIGNAL(urlChanged(QUrl)), this, SLOT(changeUrlBar(QUrl)));
connect(urlBar, SIGNAL(editingFinished()), this, SLOT(setUrl()));
// set starting page
browserView->load(QUrl("https://www.wikipedia.org"));
}
void MainWindow::setUrl()
{
browserView->load(QUrl::fromUserInput(urlBar->text()));
}
void MainWindow::changeUrlBar(QUrl)
{
urlBar->setText(browserView->url().toString());
}
MainWindow::~MainWindow()
{
delete ui;
delete browserView;
delete urlBar;
}
Your actual problem is that you've defined two local variables (urlBar and browserView) that are hiding the declaration of MainWindow::urlBar and MainWindow::browserView.
Those local objects are the ones added to the user interface, but in the slots you are using the member objects (those that were not included in the UI). Even when they are initialized in the constructor, they are not neither receiving user input nor being displayed on the user interface.
MainWindow::MainWindow(QWidget *parent) :
// ...
QLineEdit *urlBar = new QLineEdit; // <-- local variable hiding member declaration
QProgressBar *progressBar = new QProgressBar;
// WebEngineView - actual web browser
QWebEngineView *browserView = new QWebEngineView(parent); // <-- local variable hiding member declaration
// ...
void MainWindow::changeUrlBar(QUrl)
{
urlBar->setText(browserView->url().toString()); // <-- urlBar and browserView are members
}
Moral: avoid hiding or be conscious about it ;). Some tricks used to reduce this are to always access member through this (this->urlBar), or using a different notation for members (like m_urlBar or urlBar_). Also, many compilers should warn about this.
I feel like an idiot now because I managed to solve this issue and the only thing to do was to delete following lines:
QLineEdit *urlBar = new QLineEdit;
QWebEngineView *browserView = new QWebEngineView(parent);
As these objects were already initialised.

QDockWidgets merging incorrectly

I have a QDockWidget class and a QMainWindow:
// docker.hpp
class Docker : public QDockWidget
{
Q_OBJECT
public:
Docker(QString title, QWidget* parent = 0);
}
// docker.cpp
Docker::Docker(QString title, QWidget* parent): QDockWidget(title, parent)
{
QWidget* widget = new QWidget(this);
widget.setMinimumSize(200, 200);
setWidget(widget);
widget->setStyleSheet("border:5px solid gray;");
setAllowedAreas(Qt::AllDockWidgetAreas);
}
// mainwindow.hpp
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget* parent);
private slots:
void createDockers();
};
// mainwindow.cpp
MainWindow::MainWindow(QWidget* parent): QMainWindow(parent)
{
setDockOptions(dockOptions() |
QMainWindow::AllowTabbedDocks |
QMainWindow::GroupedDragging);
// The following line of code does not change the situation.
// setTabPosition(Qt::RightDockWidgetArea, QTabWidget::East);
// There are some other codes which connect a button to the void createDockers() method
}
void createDockers()
{
Docker* dock = new Docker("Docker", this);
dock->setFloating(true);
dock->show();
}
I am able to create two Dockers with clicks of the button mentioned above.
However, when I drag one QDockWidget onto the other, the border disappears and no tabs show up:
I am expecting the following to happen: (Achieved by spawning several QDockWidgets)
I am also noticing that one of the QDockWidgets did not vanish. Instead, it merged back to the MainWindow. This only happens if they are the "first two" QDockWidgets.
What caused this problem and how to solve it? I am trying to mimic this project.
I guess it's linked to the QMainWindow::GroupedDragging option. I'm pretty sure it should work well without it (I mean for the not showing tab issue). Do you have restrictions on dock position somewhere else? The documentation implies it could create issues: http://doc.qt.io/qt-5/qmainwindow.html#DockOption-enum
For the style issue, you may need to redefine it on tab event, because once tabbed, the widget may inherit the tab style instead of the dock widget style you defined (not certified at all ^^)
Last guess/thing you can try, is to start with the dock tabbed and not floating to see if you have any new bahaviour, it was what I was doing in a previous project and it was working pretty well.
Sorry but no other ideas for the moment.

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.

Qt : how to implement QDialog StatusBar

I have QDialog that is heavily designed with QDesigner , I saw on the web that I could add QStatusBar with code like this :
#include <QDialog>
#include <QStatusBar>
#include <QLayout>
#include <QApplication>
#include <QTextEdit>
#include <QStatusTipEvent>
class Dialog : public QDialog {
public:
Dialog() : QDialog(){
QLayout *l = new QVBoxLayout(this);
QTextEdit *te = new QTextEdit;
te->setStatusTip("XXX");
l->addWidget(te);
bar = new QStatusBar;
l->addWidget(bar);
l->setMargin(0);
l->setSpacing(0);
}
private:
QStatusBar *bar;
protected:
bool event(QEvent *e){
if(e->type()==QEvent::StatusTip){
QStatusTipEvent *ev = (QStatusTipEvent*)e;
bar->showMessage(ev->tip());
return true;
}
return QDialog::event(e);
}
};
int main(int argc, char **argv){
QApplication app(argc, argv);
Dialog dlg;
return dlg.exec();
}
Its not even working in my case .. maybe the QDialog is already have few layets that holds widget.
My question is can I some how use palceholder in the QDesigner or somehow promote widget that place hold the QStatusbar class? I don’t know …
What can I do in such case? can I implement new QStatusbar?
Thanks
I presume when you say it doesn't work, that you are not seeing the status bar when you run.
I don't see any way to do this wholly in the designer. The designer certainly resists the idea of promoting something to a QStatusBar. I suppose you could fool the designer by subclassing QStatusBar, and then promoting a QWidget to your subclass.
But I don't think we need to go that route just yet. I think with a few tweaks to the code you have above should help.
In the designer, add a layout, it doesn't matter what kind, at the bottom of your dialog. I called mine 'StatusBarLayout'. You can see the layout (the red box that's squished at the bottom). I removed the bottom margin in the dialog so that the status bar is flush at the bottom.
Now remove everything in the above code about layout l, and just do this:
bar = new QStatusBar(this);
pUI->StatusBarLayout->addWidget(bar);
pUI->textEdit->setStatusTip("XXX");
The textEdit was something added in the designer. Now when you run it you should see this:
I hope that helps
Edit:
You can also set the Status Tips for various widgets in the designer too, so there is no need to do that in the code unless you want to.
Try adding a QStatusBar like this:
QDialog dialog;
QLayout* layoutWidget = new QVBoxLayout(&dialog);
layoutWidget ->addWidget(new QTextEdit);
QStatusBar* statusBar = new QStatusBar;
layoutWidget ->addWidget(statusBar );