I am working on a software that uses both QQuickWidget and QWidget.
The main window is a QWidget that contains a QQuickWidget used to render stuff with OpenGL.
The QQuickWidget is in a QFrame that is sometimes displayed and sometimes hidden.
The problem is that when the QFrame is hidden, all the UI base on QWidget stops being updated
The weird thing is that only the UI freezes. If you know where to click, the software still work, only the UI is no longer updated. I think it might be that QML steals the rendering loop that is no longer accessible when it is hidden ?
I tested this on :
Linux, Qt 5.11.2 : No problem observed
Windows 10 64bit, Qt 5.9 : Problem as described above
Windows 10 64bit, Qt 5.11.2 : Problem as described above
Below is a small code example that can reproduce the problem. it works this way :
Homescreen is a QWidget. UI is updated on mouse hover (colors change)
on click, it switches to QQuickWidget
QML UI is updated on mouse hover (color change)
on click, it switches back to QWidget
The mouse hover no longer updates the UI (no color change)
main.cpp
#include <QApplication>
#include "myMainWindow.hpp"
int main(int argc, char **argv) {
QApplication application(argc, argv);
MyMainWindow window;
window.show();
window.raise();
return application.exec();
}
myMainWindow.hpp
#include <QMainWindow>
#include <QStackedLayout>
class MyMainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MyMainWindow(QWidget *parent = nullptr);
~MyMainWindow(void){}
private:
QStackedLayout *layout;
public slots:
void switchToWidget(void);
};
myMainWindow.cpp
#include <QApplication>
#include <QDebug>
#include <QFrame>
#include <QMainWindow>
#include <QPushButton>
#include <QtQuickWidgets/QQuickWidget>
#include <QQuickItem>
#include <QQmlProperty>
#include "myMainWindow.h"
MyMainWindow::MyMainWindow(QWidget *parent) : QMainWindow(parent) {
// Central Widget
QFrame *central = new QFrame;
central->setFixedSize(300,300);
this->setCentralWidget(central);
// BUTTON - QWidget
QPushButton *buttonWidget = new QPushButton("WIDGET", central);
buttonWidget->setStyleSheet("QPushButton{"
" background-color: red;"
"}"
"QPushButton:hover{"
" background-color: yellow;"
"}");
// BUTTON - QQuickWidget
QQuickWidget *buttonQML = new QQuickWidget(central);
buttonQML->setResizeMode(QQuickWidget::SizeRootObjectToView);
buttonQML->setSource(QUrl("qrc:/qml/ButtonQML.qml"));
buttonQML->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
// Main Stack Layout
this->layout = new QStackedLayout(central);
this->layout->addWidget(buttonWidget);
this->layout->addWidget(buttonQML);
this->layout->setCurrentIndex(0);
// Stack siwtch
QObject::connect(buttonWidget, &QPushButton::clicked, [=](){
this->layout->setCurrentIndex(1);
});
QQuickItem *rootObject = buttonQML->rootObject();
QObject::connect(rootObject, SIGNAL(clicked()), this, SLOT(switchToWidget()));
}
void MyMainWindow::switchToWidget(void) {
this->layout->setCurrentIndex(0);
}
ButtonQML.qml
import QtQuick 2.0
import QtQuick.Controls 2.1
Button {
text: "ButtonQML"
id: root
background: Rectangle {
color: root.hovered ? 'green' : 'white'
}
}
Any idea what could cause this ?
I found the problem. Seems that I had not read the QQuickWidget Documentation well enough.
Note: Using QQuickWidget disables the threaded render loop on all platforms. This means that some of the benefits of threaded rendering, for example Animator classes and vsync driven animations, will not be available.
I changed my QQuickWidget to a QQuickView and everything is working.
Related
I am trying to have a window (in the form of a QWidget) consisting of both a menu on the right and a graphics area on the left.
Despite the numerous websites explaining the many ways to use QGraphicsScene and QGraphicsView, I just couldn't figure out how to do it.
Here's main.cpp modified to work on its own :
#include <QApplication>
#include <QRectF>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QPushButton>
#include <QVBoxLayout>
#include <QGraphicsRectItem>
#include <QPalette>
int main (int argc, char * argv []) {
QApplication app (argc, argv) ;
// Main window
QWidget frame ;
frame.setFixedSize(750, 550) ;
frame.show() ;
// Right side, works ok
QWidget menu (&frame) ;
menu.setFixedSize(200, 500) ;
menu.move(550, 10) ;
QPalette pal = menu.palette() ;
pal.setColor(QPalette::Background, Qt::red) ;
menu.setPalette(pal) ; // I expected this to color the whole area
// to show the extent of the zone devoted to the menu,
// but it only outlines the button
menu.show() ;
QPushButton button ("Menu", &menu) ;
button.show() ; // I didn't think this was necessary,
// but the button didn't appear without this line
// Left side, nothing displayed
QVBoxLayout layout ;
QGraphicsScene * scene = new QGraphicsScene () ;
QGraphicsView view (scene) ;
layout.addWidget(&view) ;
QWidget canvas (&frame) ;
canvas.setLayout(&layout) ;
// I found this trick to include a QGraphicsScene inside a QWidget,
// I haven't had the opportunity to see whether it really works.
scene->addItem(new QGraphicsRectItem(10, 10, 20, 20)) ;
// The above line has no visible effect
view.show() ;
return app.exec() ;
}
I would expect this to create a window, put a bunch of buttons on the right side (or in the case of the rediced code I provided, just a single button), and draw a rectangle on the left, but it leaves the whole left area blank.
Does the problem come from how I put the QGraphicsView inside the QWidget ? Or is it failing to draw because of something else ? Do I have to update the QGraphicsView to reflect the change ? Is it just out of visible range ?
Finally, is the failure to draw in any way related to the fact that the whole application crashes on line QWidget canvas (&frame) ; when closed ?
I just repaired you program a bit, so that it illustrates, what you can do with Qt and how you should likely use the framework.
I just moved the QPushButton to a QAction residing in a QMenuBar. The QMenuBar can be added to a QMainWindow, which is reasonable for a normal app.
The central widget of the QMainWindow contains the QGraphicsView. Now, you just forgot to connect the QGraphicsScene with the QGraphicsView. That was the reasons for not seeeing anything in your view.
QGraphicsView and QGraphicsScene are just a typical example for a MVC pattern. You can also add another QGraphicsView and connect it to the same QGraphicsScene.
You should also create all you objects with new, as Qt automatically disposes all its children of a QObject, if it is either deleted or leaves scope.
If you are realyl interesting into seriously learning Qt I suggest, that you are creating plenty small example programs like these. It really helped me a lot.
#include <QApplication>
#include <QMenuBar>
#include <QGraphicsView>
#include <QVBoxLayout>
#include <QGraphicsRectItem>
#include <QMainWindow>
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
auto mainWindow = new QMainWindow;
auto menuBar = new QMenuBar;
auto menu = new QMenu("Menu");
auto action = new QAction("Action");
menu->addAction(action);
menuBar->addMenu(menu);
mainWindow->setMenuBar(menuBar);
auto frame = new QFrame;
frame->setLayout(new QVBoxLayout);
mainWindow->setCentralWidget(frame);
auto scene = new QGraphicsScene();
auto view=new QGraphicsView(scene);
view->setScene(scene); // That connects the view with the scene
frame->layout()->addWidget(view);
QObject::connect(action, &QAction::triggered, [&]() {
scene->addItem(new QGraphicsRectItem(10, 10, 20, 20));
});
mainWindow->show();
return app.exec();
}
I want to make a simple application with invisible button.
I set background image for my widget by UI property styleSheet and Resources -
border-image:url(:/image.jpg).
I always get something like this
and then I try to add button on it
I was trying with
ui->pushButton->setStyleSheet("QPushButton{background: transparent;}");
ui->pushButton->setStyleSheet("background-color: rgba(255, 255, 255, 0);");
and it works with buttons on default background, but not in my case.
Every button that I add takes default parent background image. I dont want to see any hints of a button, but when I click on an area to be able to perform some functionality.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->centralWidget->setStyleSheet("background-image:url(:image.jpg)");
ui->pushButton->setStyleSheet("QPushButton{border:none;}");
}
Code an above makes button flat, but it duplicate background image from parent widget anyway.
Have you any idea how to resolve it?
Cause
A common misconception is that when a stylesheet without a selector is applied to an element, then it is used only for that element. In fact all element's children are styled as well. Thus a selector should be used to achieve the expected result.
Solution
I would suggest you to change this line in your code
ui->centralWidget->setStyleSheet("background-image:url(:image.jpg)");
to
ui->centralWidget->setStyleSheet(".QWidget { background-image:url(:image.jpg) }");
Important: Note the dot before QWidget. It means style the QWidget, but exclude the subclasses. This is necessary because QPushButton is a subclass of QWidget and otherwise would be affected as well.
Then you can set the pushButton's backgroung color to transparent as you do with
ui->pushButton->setStyleSheet("QPushButton{background: transparent;}");
Example
Here is a simple example I have prepared for you in order to demonstrate the proposed solution (requires cat.png in the resource file under pix/images):
#include <QMainWindow>
#include <QWidget>
#include <QPushButton>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr) :
QMainWindow(parent) {
auto *widget = new QWidget(this);
auto *button = new QPushButton(widget);
widget->setStyleSheet(".QWidget {"
" background-image:url(':/pix/images/cat.png');"
" background-repeat: no-repeat;"
"}");
button->setStyleSheet(".QPushButton {"
" background-color: transparent"
"}");
button->move(100, 100);
button->resize(100, 100);
connect(button, &QPushButton::clicked, [](){
qDebug("clicked");
});
setCentralWidget(widget);
resize(600, 480);
}
};
Result
The given example produces a window with a background and a 100x100px invisible clickable area positioned at (100, 100):
I think it's better to answer here than in comments.
You just have to set the following stylesheet for your QPushButton to make it invisible:
QPushButton
{
border: none;
}
I've made the test and it worked well.
For the tests, I have set the wrapping widget's background-image property. I also did another test with the background-color property instead. It worked in both cases (whether the background is a plain color or a picture/photo).
I hope it helps.
EDIT:
I have written a widget that performs what you want. And I also provided a windows in order to make the below example minimal and complete so that you can reproduce it.
I have tested it and it worked well.
test.h:
#ifndef TEST_H
#define TEST_H
#include <QMainWindow>
#include <QPushButton>
class WidgetWithHiddenButton : public QWidget
{
Q_OBJECT
protected:
QPushButton * invisible_button;
public:
WidgetWithHiddenButton(QWidget * parent = nullptr);
QPushButton * getButton();
protected:
void paintEvent(QPaintEvent *) override;
};
class TestWindow final : public QMainWindow
{
Q_OBJECT
private:
WidgetWithHiddenButton * widget;
public:
TestWindow();
};
#endif // TEST_H
test.cpp:
#include "test.h"
#include <QApplication>
#include <QStyleOption>
#include <QPainter>
#include <QVBoxLayout>
WidgetWithHiddenButton::WidgetWithHiddenButton(QWidget * parent) : QWidget(parent)
{
// build your widget as you want.
invisible_button = new QPushButton("Here is a button", this);
QVBoxLayout * lay = new QVBoxLayout;
QHBoxLayout * inner_lay = new QHBoxLayout;
inner_lay->addStretch();
inner_lay->addWidget(invisible_button);
inner_lay->addStretch();
lay->addLayout(inner_lay);
this->setLayout(lay);
this->setStyleSheet("WidgetWithHiddenButton {background-image: url(path_to_image/image.jpg);}");
invisible_button->setStyleSheet("QPushButton {border: none;}");
}
QPushButton * WidgetWithHiddenButton::getButton()
{
return invisible_button;
}
void WidgetWithHiddenButton::paintEvent(QPaintEvent *)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
TestWindow::TestWindow()
{
resize(500, 300);
widget = new WidgetWithHiddenButton;
this->setCentralWidget(widget);
connect(widget->getButton(), &QPushButton::clicked, qApp, &QApplication::quit);
}
int main(int argc, char ** argv)
{
QApplication app(argc, argv);
TestWindow tw;
tw.show();
return app.exec();
}
Feel free to adapt it (especially by changing the class name because WidgetWithHiddenButton is very ugly :) ).
Notes:
I have written a text in the button in order to make it visible (for tests purposes) but you can remove it if you want the button completely invisible.
I connected the QPushButton::clicked() signal to the QApplication::quit() slot in order to perform an action when we click on the area of the button.
I redefined the paintEvent() method because it is needed when using Q_OBJECT macro alongside stylesheets over a custom QWidget as the documentation mentioned.
Feel free to modify the way I build the widget in the constructor (layouts, sizes, ...) to make it fit your requirements.
I have this problem for months. I try to implement a webview on iOS with QtWebView by Qml and embed the webview into a widget by QWidget::createWindowContainer (following a online tutorial). But I just can't get this widget to fill the parent widget, nor change its size at all.
I have write the following minimal example for explaining the problem:
main.cpp
#include <QApplication>
#include <QQuickView>
#include <QBoxLayout>
#include <QWidget>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget *mainwindow = new QWidget();
mainwindow->setStyleSheet("background-color:red;"); // so that we can see the problem more clearly
QHBoxLayout *layout = new QHBoxLayout(mainwindow);
QQuickView *view = new QQuickView();
QWidget* container = QWidget::createWindowContainer(view, mainwindow);
// container->setMinimumSize(200, 200);
// container->setMaximumSize(200, 200); // the displaying size doesn't change with or without these lines
// container->setFocusPolicy(Qt::TabFocus);
view->setSource(QUrl("qrc:///webview.qml"));
layout->addWidget(container);
mainwindow->setLayout(layout);
mainwindow->show();
return a.exec();
}
webview.qml
import QtQuick 2.2
import QtWebView 1.0
Item {
visible: true
anchors.fill: parent
WebView {
anchors.fill: parent
url: "https://www.google.de"
}
}
When I run this mini program on iphonesimulator in QtCreator, the layout is like the following screenshots (sorry I can't embed the pictures yet):
We can see the container doesn't fill the top area. And even when the parent widget is much smaller (in my actual program), the container has always this same size. When I rotate the iphonesimulator, it looks then like this!.
Could anyone please help to solve this? It's annoying me for months. Thanks!
Hi I'm trying to make a simple layout in Qt and first of all the layout is not working properly at all, all that showed up was a cancel button. So I have been messing around and now when I run it it runs without errors but no window pops up, don't know what I could have done to cause this? Here is my code
#ifndef FILMINPUT_H
#define FILMINPUT_H
#include <QMainWindow>
#include "Film.h"
#include "FilmWriter.h"
#include <QLabel>
#include <QTextEdit>
#include <QPushButton>
namespace Ui {
class FilmInput;
}
class FilmInput : public QMainWindow
{
Q_OBJECT
public:
explicit FilmInput(QWidget *parent = 0);
~FilmInput();
private:
Ui::FilmInput *ui;
//widgets
QMainWindow* window;
QMenuBar* menubar;
QLabel* infoLabel;
QLabel* titleLabel;
QLabel* durationLabel;
QLabel* directorLabel;
QLabel* relDateLabel;
QTextEdit* titleEdit;
QTextEdit* durationEdit;
QTextEdit* directorEdit;
QTextEdit* relDateEdit;
QPushButton* saveBtn;
QPushButton* cancelBtn;
Film f;
//sets up gui and connects signals and slots
void setUpGui();
};
#endif // FILMINPUT_H
#include "filminput.h"
#include "ui_filminput.h"
#include <QtGui>
FilmInput::FilmInput(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::FilmInput)
{
ui->setupUi(this);
setUpGui();
}
FilmInput::~FilmInput()
{
delete ui;
}
void FilmInput::setUpGui(){
//initialise widgets
infoLabel = new QLabel("Please enter film data which will be saved to a file",this);
titleLabel = new QLabel("Film Title",this);
durationLabel = new QLabel("Film Duration",this);
directorLabel = new QLabel("Film Director",this);
relDateLabel = new QLabel("Film Release Date",this);
titleEdit = new QTextEdit(this);
durationEdit = new QTextEdit(this);
directorEdit = new QTextEdit(this);
relDateEdit = new QTextEdit(this);
saveBtn = new QPushButton("Save Film",this);
cancelBtn = new QPushButton("Cancel",this);
//set layout
QVBoxLayout* layout = new QVBoxLayout();
layout->setMenuBar(menubar);
layout->addWidget(infoLabel);
layout->addWidget(titleLabel);
layout->addWidget(durationLabel);
layout->addWidget(directorLabel);
layout->addWidget(relDateLabel);
layout->addWidget(titleEdit);
layout->addWidget(durationEdit);
layout->addWidget(directorEdit);
layout->addWidget(relDateEdit);
layout->addWidget(saveBtn);
layout->addWidget(cancelBtn);
this->setLayout(layout);
this->setWindowTitle("Film Archive");
}
#include <QtGui/QApplication>
#include "filminput.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
FilmInput w;
w.show();
return a.exec();
}
It looks like you have conflicting things here.
You have Qt's WYSIWYG widget editor (QtDesigner), which you tell Qt to initialize (ui->setupUi(this)):
#include "ui_filminput.h" //<---- Generated from the QtDesigner form
FilmInput::FilmInput(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::FilmInput) //<---- Creating the struct that holds of the widget pointers.
{
ui->setupUi(this); //<---- Telling Qt to setup and layout all the QtDesigner widgets from this designer form.
//setUpGui(); <--- Where your layouts and widgets are accidentally clashing with the form's widgets.
}
Then you also have the ones you manually are creating inside setUpGui(). It's fine to mix the QtDesigner forms with manually created widgets - I do it all the time. But what you accidentally are doing is you are accidentally setting a layout:
this->setLayout(layout);
On this main window.... which the QtDesigner form already did for its widgets, overwriting them, and possibly confusing the layout of the main window.
You can either remove the QtDesigner widgets entirely, or prefereably, make them interact nicely by setting your layout on a subwidget of your main window.
You can access the QtDesigner widgets through the 'ui' member-variable.
this->ui->someNameOfWidgetInQtDesigner
I believe the main window has a widget already created in QtDesigner called "centralWidget", or something similar (open up FilmInput.ui and check the actual naming).
So you should set your layout on that, assuming you didn't already create a layout in QtDesigner.
this->ui->centralWidget->setLayout(layout);
If your QtDesigner form (FilmInput.ui) already has a layout set on the centralWidget, add a new QWidget in the designer form as a child of the centralWidget in centralWidget's layout, and name it something like 'sidePanel' or whatever makes sense, and then do:
this->ui->sidePanel->setLayout(layout);
I'm writing a simple text editor by using Qt and Qt Creator. I wonder how to make right application's structure. I mean widgets. Is QMainWindow should be main widget or it can be QWidget? When I trying to specify QMainWindiw as QTextEdit's parent widget, QTextEdit is not displayed. Because of it I decided to initialize QMainWindow as QWidget's parent and QWidget became a parent widget for all another widgets. Is it a right way?
#include <QApplication>
#include <QMainWindow>
#include <QWidget>
#include <QVBoxLayout>
#include <QTextEdit>
#include <QMenuBar>
#include <QMenu>
#include <QSizePolicy>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow *mainWindow = new QMainWindow;
QMenu *fileMenu = new QMenu("File");
fileMenu->addAction("New");
fileMenu->addAction("Open");
fileMenu->addAction("Save");
fileMenu->addAction("Save as");
fileMenu->addSeparator();
fileMenu->setMaximumWidth(160);
QMenu *editMenu = new QMenu("Edit");
editMenu->addAction("Copy");
editMenu->addAction("Past");
editMenu->addAction("Cut");
editMenu->setMinimumWidth(160);
QMenuBar *mainMenu = new QMenuBar;
mainMenu->addMenu(fileMenu);
mainMenu->addMenu(editMenu);
mainMenu->addAction("Exit");
mainMenu->show();
QWidget *mainWidget = new QWidget(mainWindow);
mainWidget->move(0, 20);
mainWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
QTextEdit *textEdit = new QTextEdit;
textEdit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
QVBoxLayout *vBoxLayout = new QVBoxLayout;
vBoxLayout->addWidget(textEdit);
mainWidget->setLayout(vBoxLayout);
mainWidget->show();
mainWindow->setMenuBar(mainMenu);
mainWindow->show();
return a.exec();
}
You should use QMainWindow if you need to use one of its features: toolbars, dock widgets, main menu or status bar (see QMainWindow docs for more information). If you don't need them, you can use QWidget as your top level widget.
When working with QMainWindow, you need to set central widget using QMainWindow::setCentralWidget and add window contents to this widget, not to the QMainWindow itself.