QGridLayout addWidget(CustomWidget) is not working - c++

I'm using the QGridLayout in my code, and want to add my custom widget to gridlayout, it's not working with addWidget(CustomWidget*).
This is runing on Windows10 with Visual Studio 2013 and Qt5.6.3.
// *.h
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = Q_NULLPTR);
private:
Ui::MainWindow ui;
};
class CustomWidget : public QWidget {
Q_OBJECT
public:
CustomWidget(QWidget *parent = Q_NULLPTR) : QWidget(parent) {}
~CustomWidget() {}
};
// *.cpp
// when i use CustomWidget
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
ui.setupUi(this);
QWidget *grid = new QWidget(this);
grid->setStyleSheet("background:pink;");
QGridLayout *layout = new QGridLayout(grid);
layout->setMargin(0);
layout->setSpacing(0);
grid->setLayout(layout);
grid->setGeometry(500, 150, 240, 180);
// following code is not working, when run this program,
// i can only see the 'grid' widget with pink background
CustomWidget *w = new CustomWidget(grid);
w->setStyleSheet("background:red;");
layout->addWidget(w, 0, 0);
}
// but if i use QWidget
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
ui.setupUi(this);
QWidget *grid = new QWidget(this);
grid->setStyleSheet("background:pink;");
QGridLayout *layout = new QGridLayout(grid);
layout->setMargin(0);
layout->setSpacing(0);
grid->setLayout(layout);
grid->setGeometry(500, 150, 240, 180);
// following code is working, when run this program,
// i can see the 'w' widget with red background
QWidget *w = new QWidget(grid);
w->setStyleSheet("background:red;");
layout->addWidget(w, 0, 0);
}

As it is said in the Qt's stylesheets reference, applying CSS styles to custom widgets inherited from QWidget requires reimplementing paintEvent() in that way:
void CustomWidget::paintEvent(QPaintEvent *)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
Although for what it says in the documentation, without doing it your custom widgets will support only the background, background-clip and background-origin properties, for what might be a bug.
You can read about it here: Qt Stylesheets reference in the section "List of Stylable Widgets" -> QWidget.

Related

Why Qt::AlignTop doesn't work in QVBoxLayout that I use like main layout?

I have simple class that inherits QDialog, I add dynamically elements
and my elements are located in the center, but I want to add them at the top.
class CustomDialog : public QDialog {
Q_OBJECT
private:
QVBoxLayout *mainLayout;
CustomDialog()
{
mainLayout = new QVBoxLayout();
setLayout(mainLayout);
}
public:
void update()
{
QLabel* label = new QLabel("some text");
QVBoxLayout *verLayout = new QVBoxLayout;
verLayout->addStretch();
verLayout->setAlignment(Qt::AlignTop);
verLayout->addWidget(label, Qt::AlignTop);
mainLayout->setAlignment(Qt::AlignTop);
mainLayout->addLayout(verLayout, Qt::AlignTop);
}
};
What am I doing wrong? and why my dynamically added elements always in center?
I understand that you want to place it and that the top is shown, so you could use QSpacerItem to push it.
class CustomDialog : public QDialog {
Q_OBJECT
QVBoxLayout *mainLayout;
public:
CustomDialog(QWidget *parent=0): QDialog(parent)
{
mainLayout = new QVBoxLayout(this);
QSpacerItem *verticalSpacer = new QSpacerItem(20, 217, QSizePolicy::Minimum, QSizePolicy::Expanding);
mainLayout->addItem(verticalSpacer);
addWidgets("1");
addWidgets("2");
}
private:
void addWidgets(const QString &text)
{
QLabel* label = new QLabel(text);
QVBoxLayout *verLayout = new QVBoxLayout;
verLayout->addStretch();
verLayout->setAlignment(Qt::AlignTop);
verLayout->addWidget(label, Qt::AlignTop);
mainLayout->setAlignment(Qt::AlignTop);
mainLayout->insertLayout(mainLayout->count()-1, verLayout);
}
};
Or if you want it to have a reverse order you must insert it in the first position with:
mainLayout->insertLayout(0, verLayout);
Note: The use of addLayout is incorrect since the second parameter is stretch.

How to display image on specific coordinates in Qt?

I have this dialog window class in Qt:
class Board : public QDialog
{
Q_OBJECT
public:
explicit Board(QWidget *parent = 0);
~Board();
private:
Ui::Board *ui;
void mousePressEvent(QMouseEvent *mouseEvent);
};
I want to dislay a png image on user given coordinates in function mousePressEvent, which is called every time user clicks somewhere on the dialog window. So I need something like displayImage("path/to/image.png", coordX, coordY);. How can I do it?
The new code:
class Board : public QDialog
{
public:
Board(QWidget *parent = 0) :
QDialog(parent),
ui(new Ui::Board),
view(&scene)
{
// Set background image
/**************/
ui->setupUi(this);
QPixmap pix("path/background.png");
ui->label_board->setPixmap(pix);
/**************/
/ Set layout for displaying other images on the background
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(&view);
//or set the layout and the view in the designer if using Qt Creator
}
protected:
virtual void mousePressEvent(QMouseEvent *mouseEvent) override
{
QGraphicsPixmapItem *item = new QGraphicsPixmapItem(QPixmap("path/to/image.png"));
scene.addItem(item);
item->setPos(coordX, coordY);
}
private:
Ui::Board *ui;
QGraphicsScene scene;
QGraphicsView view;
};
The label_board is a label 500x500 set to some position with Qt Designer.
You will need to use QGraphicsView (docs) and QGraphicsScene (docs):
class Board : public QDialog
{
public:
Board(QWidget *parent = 0) :
QDialog(parent),
ui(new Ui::Board),
view(&scene)
{
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(&view);
//or set the layout and the view in the designer if using Qt Creator
//EDIT: add background like this first
QGraphicsPixmapItem *background = QGraphicsPixmapItem(QPixmap("path/to/background.png"));
scene.addItem(background);
background.setPos(0, 0); //position it to cover all the scene so at 0,0 which is the origin point
background.setScale(2.0); //scale the image to the scene rectangle to fill it
background.setZValue(-0.1); //to ensure it is always at the back
}
protected:
virtual void mousePressEvent(QMouseEvent *mouseEvent) override
{
QGraphicsPixmapItem *item = new QGraphicsPixmapItem(QPixmap("path/to/image.png"));
scene.addItem(item);
item->setPos(coordX, coordY);
}
private:
Ui::Board *ui;
QGraphicsScene scene;
QGraphicsView view;
};
And that is pretty much it. Also note that the position is that of upper left corner of the item (image). If you would want to center it you would need to adjust the position based on the proportions of the item (image).

QT: Dynamic child button not visible

I have a project that I want to add button dynamically wherever I click in my form.
This is my header:
namespace Ui {
class frmBedBook;
}
class frmBedBook : public QWidget
{
Q_OBJECT
public:
explicit frmBedBook(QWidget *parent = 0);
void mousePressEvent(QMouseEvent *event);
~frmBedBook();
private:
Ui::frmBedBook *ui;
QSignalMapper *signalMapper;
QList<QPushButton*> buttonList;
QGridLayout *lyWidget;
QWidget *m_widget;
public slots:
void clicked(int buttonId);
};
And this is my implementation:
frmBedBook::frmBedBook(QWidget *parent) :
QWidget(parent),
ui(new Ui::frmBedBook)
{
ui->setupUi(this);
signalMapper = new QSignalMapper();
QPushButton *p;
lyWidget = new QGridLayout();
m_widget = new QWidget();
m_widget->setGeometry(0,0,930,472);
lyWidget->setContentsMargins(0,0,0,0);
lyWidget->addWidget(m_widget);
setLayout(lyWidget);
p = new QPushButton(m_widget);
p->setText("00");
p->setGeometry(0, 0, 50, 50);
buttonList.append(p);
connect(p, SIGNAL(clicked()), signalMapper, SLOT(map()));
signalMapper->setMapping(p,0);
p = new QPushButton(m_widget);
p->setText("01");
p->setGeometry(50, 0, 50, 50);
p->setObjectName("01");
buttonList.append(p);
connect(p, SIGNAL(clicked()), signalMapper, SLOT(map()));
signalMapper->setMapping(p,1);
connect(signalMapper, SIGNAL(mapped(int)),this, SLOT(clicked(int)));
}
void frmBedBook::mousePressEvent(QMouseEvent *event)
{
QPushButton *p;
p = new QPushButton(m_widget);
p->setText("02");
p->setGeometry(QCursor::pos().x(), QCursor::pos().y(), 50, 50);
buttonList.append(p);
connect(p, SIGNAL(clicked()), signalMapper, SLOT(map()));
signalMapper->setMapping(p,2);
}
The problem is the button is created, but not visible. I know it because I have traced through m_widget's children and it's found. I also already resetting layout in MousePressEvent function, but nothing happened. Could anyone please help me about this problem?
You need to call show() on your buttons if they are added after the form has been constructed. Also, QCursor::pos() will probably not deliver the position you want to have.
You can use the x()/y() functions of the QMouseEvent instead:
void frmBedBook::mousePressEvent(QMouseEvent *event)
{
QPushButton *p;
p = new QPushButton(m_widget);
p->setText("02");
p->setGeometry(event->x(), event->y(), 50, 50);
p->show();
buttonList.append(p);
connect(p, SIGNAL(clicked()), signalMapper, SLOT(map()));
signalMapper->setMapping(p,2);
}
Note that you need to #include <QtGui/QMouseEvent> unless you already have that.
You never show the buttons, so they stay hidden. Use QWidget::show(). This applies to widgets created in mousePressEvent, after parent is already shown. The button created in constructor should get automatically shown when its parent is shown.

QResizeEvent causes SIGSEGV

My purpose is to use a class called overlay.h to add a rectangular box and text on top of a Widget (MarbleWidget). Here below is my code for the GUI. I tried to remove all unnecessary parts:
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
setupUi(this);
}
MainWindow::~MainWindow()
{
}
void MainWindow::resizeEvent(QResizeEvent *event)
{
overlay->resize(event->size()); //////////////// CAUSES SIGSEGV!!!!!!!!!!!!!
event->accept();
}
void MainWindow::setupUi(QMainWindow *MainWindow)
{
MainWindow->showMaximized();
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
sizePolicy.setHeightForWidth(MainWindow->sizePolicy().hasHeightForWidth());
MainWindow->setSizePolicy(sizePolicy);
MainWindow->setTabShape(QTabWidget::Rounded);
QWidget *centralwidget = new QWidget(MainWindow);
centralwidget->setObjectName(QString::fromUtf8("centralwidget"));
sizePolicy.setHeightForWidth(centralwidget->sizePolicy().hasHeightForWidth());
centralwidget->setSizePolicy(sizePolicy);
centralwidget->setLayoutDirection(Qt::LeftToRight);
QGridLayout *gridLayout_4 = new QGridLayout(centralwidget);
gridLayout_4->setObjectName(QString::fromUtf8("gridLayout_4"));
QSplitter *splitter_4 = new QSplitter(centralwidget);
splitter_4->setObjectName(QString::fromUtf8("splitter_4"));
splitter_4->setOrientation(Qt::Horizontal);
QTabWidget *tabWidget_2 = new QTabWidget(splitter_4);
tabWidget_2->setObjectName(QString::fromUtf8("tabWidget_2"));
sizePolicy.setHeightForWidth(tabWidget_2->sizePolicy().hasHeightForWidth());
tabWidget_2->setSizePolicy(sizePolicy);
tabWidget_2->setMaximumSize(QSize(443, 16777));
tabWidget_2->setAutoFillBackground(true);
tabWidget_2->setTabPosition(QTabWidget::West);
tabWidget_2->setTabShape(QTabWidget::Rounded);
tabWidget_2->setIconSize(QSize(30, 16));
QWidget *tab_10 = new QWidget();
tab_10->setObjectName(QString::fromUtf8("tab_10"));
QGridLayout *gridLayout = new QGridLayout(tab_10);
gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
QVBoxLayout *verticalLayout_4 = new QVBoxLayout();
verticalLayout_4->setObjectName(QString::fromUtf8("verticalLayout_4"));
QHBoxLayout *horizontalLayout_7 = new QHBoxLayout();
horizontalLayout_7->setObjectName(QString::fromUtf8("horizontalLayout_7"));
QLabel *label_3 = new QLabel(tab_10);
label_3->setObjectName(QString::fromUtf8("label_3"));
horizontalLayout_7->addWidget(label_3);
verticalLayout_4->addLayout(horizontalLayout_7);
QTreeView *treeView_4 = new QTreeView(tab_10);
treeView_4->setObjectName(QString::fromUtf8("treeView_4"));
QStandardItemModel *standardModel = new QStandardItemModel ;
QStandardItem *rootNode = standardModel->invisibleRootItem();
treeView_4->setModel(standardModel);
verticalLayout_4->addWidget(treeView_4);
gridLayout->addLayout(verticalLayout_4, 0, 0, 1, 1);
tabWidget_2->addTab(tab_10, QString());
treeView_4->raise();
Marble::MarbleWidget* MarbleWidget = new Marble::MarbleWidget(splitter_4);
splitter_4->addWidget(MarbleWidget);
splitter_4->setStretchFactor(0,0);
splitter_4->setStretchFactor(1,6);
gridLayout_4->addWidget(splitter_4, 0, 0, 1, 1);
MainWindow->setCentralWidget(centralwidget);
MarbleWidget->raise();
tabWidget_2->raise();
overlay = new Overlay(MarbleWidget);
overlay->raise();
QMetaObject::connectSlotsByName(MainWindow);
} // setupUi
overlay.h
#include <QWidget>
#include <QPainter>
class Overlay : public QWidget
{
public:
Overlay(QWidget *parent);
protected:
void paintEvent(QPaintEvent *event);
};
overlay.cpp
#include "overlay.h"
Overlay::Overlay(QWidget *parent)
: QWidget(parent)
{
setPalette(Qt::transparent);
setAttribute(Qt::WA_TransparentForMouseEvents);
}
void Overlay::paintEvent(QPaintEvent *event)
{
QFont font;
font.setStyleHint(QFont::Helvetica, QFont::PreferAntialias);
font.setPointSize(10);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(QColor(10, 10, 10, 255));
painter.fillRect(QRect(50, 50, 100, 100), QColor(100, 100, 100, 120));
painter.setFont(font);
painter.drawText(20, 20, "hi..............................");
}
The problem is that overlay->resize(event->size()); causes SIGSEGV when the core runs that line.
What is wrong with the code, how can I fix it?
The problem is solved by Thiago from freenode.
He pointed that MainWindow->showMaximized(); causes resize() event to be occurred before overlay is initialized. Removing that line or moving it to after the initialization solves the problem.
Take a look at official documentation: QWidget, Qt5 at resize method. Here you can read
Warning: Calling resize() or setGeometry() inside resizeEvent() can lead to infinite recursion.
You are doing exactly the same. To avoid infinite recursion and respectively your SIGSEGV you can (descending order of difficulty, descending order of true-way):
overwrite your resizeEvent() in Overlay and comment that line in MainWindow's resizeEvent;
in MainWindow's resizeEvent emit signal like mainWindowSizeChanged(QSize) and connect it to your Overlay's slot;
do QTimer::singleShot(...) in MainWindow's resizeEvent, so it will call a given slot after a given time interval.

Qt: Can't set layout in QMainWindow

I am trying to set my layout (using setLayout()) in my mainwindow. It does not show anything on launch:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0)
{
QVBoxLayout *vBoxLayout = new QVBoxLayout;
{
QPushButton *pushButton = new QPushButton(tr("A button"));
vBoxLayout->addWidget(pushButton);
}
setLayout(vBoxLayout);
}
};
You need to change the last two lines of code to be the following:
QWidget *widget = new QWidget();
widget->setLayout(VBoxLayout);
setCentralWidget(widget);
//VBoxLayout->addWidget(new QLayout);
//setLayout(VBoxLayout);
The QMainWindow is a special case. You set the contents of this widget by putting the layout in a new QWidget and then setting that as the central widget.See this answer also.