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.
Related
I was implementing a custom camera controller for Qt3DRender::QCamera and I faced a rather strange behavior of the Qt3DInput::QMouseHandler. Depending on the environment it was created it either responds to mouse events or not. There are two cases: create both the device and the handler in the MainWindow object or create them inside my camera controller class (it only works in the first case).
First case:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
auto* window = new Qt3DExtras::Qt3DWindow();
auto* window_container = QWidget::createWindowContainer(window, this, Qt::Widget);
setCentralWidget(window_container);
auto* scene = new Qt3DCore::QEntity();
window->setRootEntity(scene);
auto* mouse_device = new Qt3DInput::QMouseDevice(scene);
auto* mouse_handler = new Qt3DInput::QMouseHandler(scene);
mouse_handler->setSourceDevice(mouse_device);
connect(mouse_handler, &Qt3DInput::QMouseHandler::positionChanged,
[](Qt3DInput::QMouseEvent* event)
{
qDebug() << "I am actually printing to console!!!";
});
}
Second case:
class CustomCameraController final : public Qt3DCore::QNode
{
Q_OBJECT
public:
explicit CustomCameraController(Qt3DCore::QNode* parent = nullptr)
: Qt3DCore::QNode(parent),
mouse_device(new Qt3DInput::QMouseDevice(this)),
mouse_handler(new Qt3DInput::QMouseHandler(this))
{
mouse_handler->setSourceDevice(mouse_device);
connect(mouse_handler, &Qt3DInput::QMouseHandler::pressAndHold, this,
&CustomCameraController::MousePositionChanged);
}
public slots:
void MousePositionChanged(Qt3DInput::QMouseEvent* event)
{
qDebug() << "I am not printing anything...";
}
protected:
Qt3DInput::QMouseDevice* mouse_device;
Qt3DInput::QMouseHandler* mouse_handler;
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
auto* window = new Qt3DExtras::Qt3DWindow();
auto* window_container = QWidget::createWindowContainer(window, this, Qt::Widget);
setCentralWidget(window_container);
auto* scene = new Qt3DCore::QEntity();
window->setRootEntity(scene);
auto* controller = new CustomCameraController(scene);
}
All the unnecessary code was removed. The main.cpp file was automatically generated by Qt Framework
I searched all through the Qt documentation and found nothing that would help me in this scenario. I also noticed that if the device and the handler are initialized in the constructor with parent as the parameter it does not help. Furthermore, I tried to create the device and the handler in the MainWindow scope and pass them to the controller via some setter function but it does not help neither.
So the questions I want to ask: what is the proper way of using Qt3DInput::QMouseDevice and Qt3DInput::QMouseHandler? Is there a better workaround for implementing input handling for my custom camera controller?
Update 1:
You should add the controller class declaration to the header instead of main window source file. Here are all the includes and the qmake options you are going to need:
#include <Qt3DCore/QNode>
#include <Qt3DInput/QMouseDevice>
#include <Qt3DInput/QMouseHandler>
#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DCore/QEntity>
3dinput 3dcore 3dextras
Looks like you just connected to different signals.
In first case you are using &Qt3DInput::QMouseHandler::positionChanged which will be sended quite often. In second one - &Qt3DInput::QMouseHandler::pressAndHold which will be sended when a mouse button is pressed and held down.
After fix both logging functions are called.
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.
I have a MainWindow with a menu that opens a dialog for registration. How can I update the tableView in the MainWindow after the registration?
Here is my MainWindow implementation:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow){
ui->setupUi(this);
}
void MainWindow::list()
{
qDebug() << "test";
QSqlQueryModel *model = new QSqlQueryModel();
//model->clear();
model->setQuery("SELECT test_qt FROM db_qt WHERE strftime('%Y-%m-%d', date)='"+dateTime.toString("yyyy-MM-dd")+"'");
model->setHeaderData(0, Qt::Horizontal, tr("qt_test"));
ui->tableView->setModel(model);
}
void MainWindow::on_actionMenu_triggered()
{
dialog_test->show();
}
Here is my Dialog implementation
Dialog_test::Dialog_test(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog_test)
{
ui->setupUi(this);
}
void Dialog_test::insert_date(){
QSqlQuery qry;
qry.prepare("INSERT INTO db_qt(test_qt) VALUES (?)");
qry.addBindValue(id);
if (qry.lastInsertId()>0){
QMessageBox::information(this,"test", "Success");
MainWindow *mw = new MainWindow(this);
mw->list(); // I call back list, but not update the tableView the MainWindow.
}
}
The following line in your code
MainWindow *mw = new MainWindow(this);
creates a new main window and updates the list of it. I assume this actually happens, but the window is never shown so you do not see any of it. What you actually want to do is update the list of your existing main window.
There are basically two ways of doing that. You can either obtain a pointer to the existing main window (which can be provided to the constructor of the dialog or a method of its own) or use the Signals and Slots concept of Qt which is the way to go in my opinion.
First of all, you define the signal in the header of the dialog:
...
signals:
void user_registered();
...
Then you emit the signal in your function
//MainWindow *mw = new MainWindow(this);
//mw->list();
emit this->user_registered();
Make sure the list() method is declared as a SLOT in the MainWindow header
Connect the signal in the MainWindow constructor to call the list() slot:
...
QObject::connect(this->dialog_test, SIGNAL(user_registered()), this, SLOT(list()));
...
With this approach, the dialog does not need to know the main window at all. It basically just tells anyone who is interested that a user registered and the main window acts on it completly by itself.
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();
I've created an Application which derives from QWidget. When I create an QLabel and yield the show command, it opens in a separate window. I was deriving my BaseClass from QMainWindow before which worked fine.
#include "widget.h"
#include "ui_widget.h"
#include <iostream>
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
testlabel = new my_qlabel(parent);
QImage myImage = QImage(QString::fromStdString("D:/Lighthouse.jpg"));
testlabel->setParent(parent);
testlabel->name="testName";
testlabel->setPixmap(QPixmap::fromImage(myImage, Qt::AutoColor));
testlabel->setGeometry(QRect(500, 500, 100, 100));
testlabel->show();
std::cout<<"i am in the output "<<std::endl;
qDebug() << QString("init");
}
Widget::~Widget()
{
delete ui;
}
testlabel = new my_qlabel(parent);
The above should probably instead be
testlabel = new my_qlabel(this);
Also make sure that your my_qlabel constructor is passing its QWidget pointer argument up to the superclass's (QLabel?) constructor. If you forgot to do that, then the my_qlabel object will not have a parent widget, which will cause it to show up as top-level window, which would match the behavior you are seeing.
testlabel->show();
Once you have testlabel's parenting problems fixed, this show() command should no longer be necessary (or appropriate), since any child widgets you add to your Widget object will be automatically show()'n when the Widget itself is first show()'n. (The only time you would need to manually call show() is if you had previously called hide() or setVisible(false) on that same widget, and now you wanted to make it re-appear; or if you had added the child widget to its parent widget after the parent widget was already visible on-screen; neither is the case here)