How to make WA_TranslucentBackground work on Windows without using FramelessWindowHint? - c++

Im using setAttribute(Qt::WA_TranslucentBackground); to make my QMainWindow background transparent, however, this attribute only work if:
I also set: setWindowFlag(Qt::FramelessWindowHint);
or, have at least one QOpenGLWidget in my GUI.
I have no experience with QOpenGlWidget, i wonder if its possible to use QSurfaceFormat or something like, to achieve the same 'thing' QOpenGL does when added to a QMainWindow, that makes the attribute work.
Or if there's any other 'option' than setting the FramelessWindowHint flag or creating an QOpenGL widget.
Example code:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
/*
QSurfaceFormat format;
format.setAlphaBufferSize(8);
QSurfaceFormat::setDefaultFormat(format);
QSurfaceFormat format;
format.setAlphaBufferSize(8);
format.setSamples(16);
format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
format.setRenderableType(QSurfaceFormat::OpenGL);
format.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(format);
*/
MainWindow w;
w.show();
return a.exec();
}
//mainwindow.h
#include <QtWidgets/QMainWindow>
#include "ui_MainWindow.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindowClass; };
QT_END_NAMESPACE
class MainWindow : public QMainWindow{
Q_OBJECT
public:
MainWindow(QWidget* parent = nullptr);
}
//mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindowClass())
{
ui->setupUi(this);
setAttribute(Qt::WA_TranslucentBackground);
QGridLayout* layout = new QGridLayout();
ui->centralWidget->setStyleSheet("background-color: transparent;");
ui->centralWidget->setLayout(layout);
QWidget* widget = new QWidget(this);
widget->setStyleSheet("background-color: green; border-radius: 32px;");
layout->addWidget(widget);
//QOpenGLWidget* opengl = new QOpenGLWidget(this);
//opengl->deleteLater();
}
Creating the QOpenGlWidget and calling opengl->deleteLater() the WA_TranslucentBackground works, however, it make the application use 50mb more of ram compared to not creating the QOpenGLWidget.
This is not much if i were creating just one GUI, but I'm creating multiples.

Why not try setting the opacity level of the window by changing the windowOpacity property using setProperty?
Example:
YourWindow->setProperty("windowOpacity", 0.0);
Setting the opacity level to 0 results in a transparent background

Related

How to launch the QMainWindow with a fading animation?

I tried launching my window this way:
#include "stdafx.h"
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
setWindowOpacity(0);
QGraphicsOpacityEffect* eff = new QGraphicsOpacityEffect(this);
QPropertyAnimation* ani = new QPropertyAnimation(eff, "windowOpacity");
ani->setDuration(3000);
ani->setStartValue(0);
ani->setEndValue(1);
ani->setEasingCurve(QEasingCurve::OutBack);
ani->start(QPropertyAnimation::DeleteWhenStopped);
}
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
But the window never became visible, I would like to it be initialized with a fade in effect.
What's the proper way to achieve it?
Two issues I see. First, as originally written, your main function is going to exit immediately after opening the window. Add return a.exec(); at the end of main.
Next, you are animating QGraphicsOpacityEffect. As written, your example code has no connection between QGraphicsOpacityEffect and the window. Your animation is animating the property on the effect class, but that doesn't propagate back to the widget. There is no need to use QGraphicsOpacityEffect. Instead, just animate the widget directly:
ui.setupUi(this);
setWindowOpacity(0);
// Notice that the first argument passed in is 'this' - the widget.
// the widget is what you want to animate.
QPropertyAnimation* ani = new QPropertyAnimation(this, "windowOpacity");
ani->setDuration(3000);
ani->setStartValue(0);
ani->setEndValue(1);
ani->setEasingCurve(QEasingCurve::OutBack);
ani->start(QPropertyAnimation::DeleteWhenStopped);

How to make invisible button on widget with background image?

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.

QWidget UI freezes when using a QQuickWidget

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.

Qt program doesn't display

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);

QGraphicsView not displaying in mainWindow

If I just start from main with a QGraphicsView, it displays.
But if I place the QGraphicView in mainwindow.cpp, it flashes up and disappears??
int main(int argc, char **argv)
{
QApplication a(argc, argv);
QGraphicsView view;
view.resize(1000, 800);
view.show();
return a.exec();
}
int main(int argc, char **argv)
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QPushButton *m_button1 = new QPushButton("1", this);
m_button1->setGeometry(QRect(QPoint(100, 100), QSize(100, 100)));
connect(m_button1, SIGNAL(released()), this, SLOT(handleButton1()));
}
void MainWindow::handleButton1()
{
QGraphicsView view;
view.resize(1000, 800);
view.show();
}
You've created a local QGraphicsView variable there in the handleButton1() function that will be destroyed as soon as the function finishes, in your first example, the view would exist until the end of main() which is the end of the application, i.e it exists until you close the application. Your best bet would be to either use Qt Designer to place the QGraphicsView in the MainWindow or to give MainWindow a private QGraphicsView* member variable
If you use a private variable, use Qts built in memory management to set the parent of it to the MainWindow so it's cleaned up when the window is destroyed.
class MainWindow : QMainWindow {
// etc...
private:
QGraphicsView *view;
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow) {
ui->setupUi(this);
view = new QGraphicsView(this);
view->setGeometry(QRect(50, 50, 400, 200));
view->show();
// etc...
}
Instead of using 'this' as above, if you have a central widget, or any widget where you want the QGraphicsView, you would do
view = new QGraphicsView(ui->centralWidget);