2

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

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?

scopchanov
  • 7,966
  • 10
  • 40
  • 68
EmmaStone
  • 23
  • 8
  • Why do you use `border-image` ? If you want to set the background image, use `background-image` instead. Moreover, in the code you provided, the second `setStyleSheet()` overwrites the first one (that become useless). Then, the second stylesheet is incorrect. – Fareanor Aug 08 '19 at 14:30
  • @Fareanor, I use it because I need to fit background image into the widget. Sample code Is not that I actually use - just for example. – EmmaStone Aug 08 '19 at 14:31
  • This is not the meaning of `border-image`. – Fareanor Aug 08 '19 at 14:31
  • @Fareanor, but there's a trick with – EmmaStone Aug 08 '19 at 14:32
  • I don't really understand what you want to do. But if you want to make the button invisible, just remove the borders (`"QPushButton {border: none;}"`) and call `pushButton->setIcon(QIcon());` to clear the displayed image. It should work. – Fareanor Aug 08 '19 at 14:36
  • @Fareanor, It doen't work – EmmaStone Aug 08 '19 at 14:48
  • Try to not set the image with stylesheets but with `setIcon()` too. This way it will work (I did it and it worked well). – Fareanor Aug 08 '19 at 14:56
  • @Fareanor, I dont get it. What did u exactly do? – EmmaStone Aug 08 '19 at 15:24
  • First of all, tell me what you exactly want to do. Because setting an image to remove it afterward is a nonsense, you better have to not set the image at all. Your question is not clear, explain what you want to do and I will be able to give you the real solution. – Fareanor Aug 08 '19 at 17:19
  • @Fareanor, I want to have invisible button on an image, that of course must not be the button entirely. – EmmaStone Aug 08 '19 at 17:55
  • Ok so the image is not the button's background. If I understand. Try to only set `"QPushButton {border: none;}"` for the button. In my case, it disappears. – Fareanor Aug 08 '19 at 18:51
  • I updated my answer with an example. But the stylesheet you used to set an image is invalid because you did not specify the related widget it refers to. – Fareanor Aug 12 '19 at 11:27

2 Answers2

4

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

Window with a background and an invisible button

scopchanov
  • 7,966
  • 10
  • 40
  • 68
  • 1
    I didn't think of the inheritance between `QPushButton` and `QWidget`. Your solution is better than mine so I upvoted you. Thanks for the explanation. – Fareanor Aug 12 '19 at 12:18
  • @Fareanor, you are welcome! Actually, I don't find it very intuitive either, but it is what it is. :( – scopchanov Aug 12 '19 at 12:20
  • In fact I'm really surprised. What you explain makes sense but... I never encountered this error even though I often have set a QWidget stylesheet over custom widgets. But in this case, the error occurs (that's why I had to add _"background: transparent;"_). – Fareanor Aug 12 '19 at 12:22
  • @Fareanor, I assume that you haven't noticed it because you apply the stylesheet correctly by putting a dot in front of the class name in the selector, so the sub-classes are not affected. – scopchanov Aug 12 '19 at 12:25
  • 1
    No I hadn't. You just learned me that we can do this with a dot. I think I just got lucky. From now on, I'll add this dot on my future applications that will request it. Thanks for the tip anyway :) – Fareanor Aug 12 '19 at 12:27
  • @Fareanor, :) To be honest, I don't like stylesheets, because they are limited and behave strangely. Normally I prefer to paint the widget myself, as you do in your answer. For example, check out [my repository of custom widgets](https://github.com/scopchanov/flat-gui). – scopchanov Aug 12 '19 at 12:31
  • Yes I understand. I just made a test and writing the dot before QWidget for a custom widget (that inherits `QWidget`) does not work. Even worse, the stylesheet is not applied at all. Very strange indeed. – Fareanor Aug 12 '19 at 12:37
  • 1
    @Fareanor, for a custom widget you have to put the name of the custom class instead of QWidget and to enable the stylesheets with `setAttribute(Qt::WA_StyledBackground);`. – scopchanov Aug 12 '19 at 12:40
  • 1
    It worked. You learned me two things today ahah :). I will correct my answer then. It seems to work even without setting this attribute though. – Fareanor Aug 12 '19 at 12:43
2

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.
Fareanor
  • 5,900
  • 2
  • 11
  • 37
  • I dont know, that am i doing wrong or I poorly explained my problem. I dont get your point. This is the [result](https://pasteboard.co/Is3tR5B.jpg) of your answer. I have the visible button yet. 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. – EmmaStone Aug 10 '19 at 14:19
  • @KkVova If you use QtDesigner as it seems, don't set the stylesheet from here but from the C++ code. I have never used any designer, that's why I can't know its behaviour. I have written everything in C++ in my example. – Fareanor Aug 11 '19 at 09:38
  • I delete all styleSheet mentions from Qt designer and take [the same result](https://ibb.co/C5Wqzm3) from the code. – EmmaStone Aug 11 '19 at 15:23
  • Ok, I don't understand then, it worked for me. I tried with `QPushButton` as well as `QToolButton` and it worked the same. Perhaps if we can see your code... – Fareanor Aug 12 '19 at 06:21
  • @KkVova I can't see it because my network access is restricted. I would suggest you to edit your question and put your code here. – Fareanor Aug 12 '19 at 10:19
  • @KkVova Your code seems fine. I don't understand why your `QPushButton` copies the background of another widget. I never had this behaviour. The error shall come from somewhere else in your code but I can't guess where. – Fareanor Aug 12 '19 at 10:41
  • It appears with this simpliest code on 3 machines, including 2 win10 and one VM MacOS. Could you, please, share some working example to me? – EmmaStone Aug 12 '19 at 10:47
  • I'll do better, I'll create a `QWidget`'s subclass that does what you want. – Fareanor Aug 12 '19 at 10:56
  • _I don't understand why your QPushButton copies the background of another widget_ - see my answer for an explanation and a very simple solution. – scopchanov Aug 12 '19 at 12:10
  • 1
    @KkVova My solution works but I think the one provided by **scopchanov** is way simpler. – Fareanor Aug 12 '19 at 12:19