How to make QMenu Item checkable in QT - c++

How to give Qmenu item checkable using QT
QMenu *preferenceMenu = new QMenu();
preferenceMenu = editMenu->addMenu(tr("&Preferences"));
QMenu *Mode1 = new QMenu();
Mode1 = preferenceMenu->addMenu(tr("&Mode 1"));
Mode1->addAction(new QAction(tr("&Menu1"), this));
QMenu *Mode2 = new QMenu();
Mode2 = preferenceMenu->addMenu(tr("&Mode 2"));
Mode2->addAction(new QAction(tr("&Menu2"), this));
Mode2->addAction(new QAction(tr("&Menu3"), this));
On QAction I called slot "slotActionTriggered(QAction* actionSelected)"
void csTitleBar::slotActionTriggered(QAction* actionSelected)
{
actionSelected->setChecked(true);
}
How to show small TICK in the selected Menu# so that user can know which is selected
Currently I am able to change to all Menu#, but I need to show a small tick on menu so that the selected one can be easly Identified

Small example:
cmainwindow.h
#ifndef CMAINWINDOW_H
#define CMAINWINDOW_H
#include <QMainWindow>
#include <QPointer>
class CMainWindow : public QMainWindow
{
Q_OBJECT
public:
CMainWindow(QWidget *parent = 0);
~CMainWindow();
private slots:
void slot_SomethingChecked();
private:
QPointer<QAction> m_p_Act_Button1 = nullptr;
QPointer<QAction> m_p_Act_Button2 = nullptr;
};
#endif // CMAINWINDOW_H
cmainwindow.cpp
#include "cmainwindow.h"
#include <QtWidgets>
#include <QDebug>
CMainWindow::CMainWindow(QWidget *parent)
: QMainWindow(parent)
{
m_p_Act_Button1 = new QAction("Super Button 1", this);
m_p_Act_Button1->setCheckable(true);
m_p_Act_Button1->setChecked(true);
connect(m_p_Act_Button1, SIGNAL(triggered()), this, SLOT(slot_SomethingChecked()));
m_p_Act_Button2 = new QAction("Super Button 2", this);
m_p_Act_Button2->setCheckable(true);
m_p_Act_Button2->setChecked(true);
connect(m_p_Act_Button2, SIGNAL(triggered()), this, SLOT(slot_SomethingChecked()));
QMenu *p_menu = menuBar()->addMenu("My Menu");
p_menu->addAction(m_p_Act_Button1);
p_menu->addAction(m_p_Act_Button2);
}
CMainWindow::~CMainWindow() { }
void CMainWindow::slot_SomethingChecked()
{
if(!m_p_Act_Button1 || !m_p_Act_Button2) {return;}
qDebug() << "Hi";
if(m_p_Act_Button1->isChecked())
{
qDebug() << "The action 1 is now checked";
}
else
{
qDebug() << "The action 1 is now unchecked";
}
if(m_p_Act_Button2->isChecked())
{
qDebug() << "The action 2 is now checked";
}
else
{
qDebug() << "The action 2 is now unchecked";
}
}

Modern connection syntax is preferable.
Checkable items can also be used on context menu as follow :
void MyWidget::initContextMenu()
{
if( _contextMenu)
{
delete _contextMenu;
}
_contextMenu = new QMenu(this);
auto action1 = new QAction("Action Item 1", &_contextMenu);
action1->setCheckable(true);
action1->setChecked(getMyProperty1());
connect(action1 , &QAction::triggered, this, &MyWidget::setMyProperty1);
_contextMenu->AddAction( action1 );
}
void MyWidget::contextMenuEvent(QContextMenuEvent *event)
{
initContextMenu();
_contextMenu.popup(
this->viewport()->mapToGlobal(
event->pos()));
}
bool MyWidget::setMyProperty1(bool state)
{
_myProperty1 = state;
emit MyPropertyChanged(_myProperty);
}

Related

QWidget shown problems after QStackedLayout setCurrentIndex

I have a task which was to show a dialog beside the windows desk's edge. When the mouse move to the specific regions(some where in the windows desk), the dialog shows, while out it'll be hide.
I put some QWidget in the dialog with QStackedLayout used as layout,
and use QStackedLayout::setCurrentIndex to change the current widget before the dialog shown.
The problem is that the previous widget will stay a littel while when the dialog is shown, and then the new widget is shown. And what i want is to show the new widget immediately.
So why is that happen, and how can i fix it?
ConfigDialog::ConfigDialog(int role, QWidget *parent) :
QDialog(parent),
ui(new Ui::ConfigDialog)
{
ui->setupUi(this);
setWindowFlags(windowFlags() | Qt::FramelessWindowHint | Qt::SubWindow | Qt::WindowStaysOnTopHint);
// here is the code i commented out and the problem seems to be gone
// setAttribute(Qt::WA_TranslucentBackground);
buttonArea = new QRect();
canReShow = true;
init(role);
}
ConfigDialog::~ConfigDialog()
{
delete ui;
}
void ConfigDialog::setModel(int model)
{
int tmp = model;
QStackedLayout* lay = qobject_cast<QStackedLayout*>(layout());
if (lay->count() < tmp || tmp < 0)
{
tmp = 0;
}
lay->setCurrentIndex(tmp);
}
void ConfigDialog::init(int role)
{
QStackedLayout* stackLayout = new QStackedLayout(this);
stackLayout->setMargin(0);
stackLayout->setSpacing(0);
// 根据系统缩放比例,缩放窗口
qreal scale = ::GetDpiForSystem() / 96;
int iconWidth = 64 * scale;
int iconHeight = 64 * scale;
// system menu
QWidget* syscfg = new QWidget(this);
QHBoxLayout* lay = new QHBoxLayout(syscfg);
lay->setSpacing(0);
lay->setMargin(0);
lay->setAlignment(Qt::AlignCenter);
lay->addStretch();
QPushButton* btnSysConfig = new QPushButton(syscfg);
QIcon syscfgIcon;
syscfgIcon.addFile(":/icon/icon/console.png");
btnSysConfig->setIcon(syscfgIcon);
btnSysConfig->setIconSize(QSize(iconWidth, iconHeight));
btnSysConfig->setFlat(true);
connect(btnSysConfig, &QPushButton::clicked, [this]()
{
emit sysConfig();
accept();
});
lay->addWidget(btnSysConfig);
if (2 != role)
{
QPushButton* btnUserConfig = new QPushButton(syscfg);
QIcon usrcfgIcon;
usrcfgIcon.addFile(":/icon/icon/setting.png");
btnUserConfig->setIcon(usrcfgIcon);
btnUserConfig->setIconSize(QSize(iconWidth, iconHeight));
btnUserConfig->setFlat(true);
connect(btnUserConfig, &QPushButton::clicked, [this]()
{
emit userConfig();
accept();
});
lay->addWidget(btnUserConfig);
}
QPushButton* btnExitApp = new QPushButton(syscfg);
QIcon exitIcon;
exitIcon.addFile(":/icon/icon/exit.png");
btnExitApp->setIcon(exitIcon);
btnExitApp->setIconSize(QSize(iconWidth, iconHeight));
btnExitApp->setFlat(true);
connect(btnExitApp, &QPushButton::clicked, [this]()
{
emit exitSys();
// accept();
});
lay->addWidget(btnExitApp);
lay->addStretch();
stackLayout->addWidget(syscfg);
// end system menu
// app menu
QWidget* appcfg = new QWidget(this);
lay = new QHBoxLayout(appcfg);
lay->setSpacing(0);
lay->setMargin(0);
lay->addStretch();
QPushButton* btnResetWindow = new QPushButton(appcfg);
QIcon resetIcon;
resetIcon.addFile(":/icon/icon/reset.png");
btnResetWindow->setIcon(resetIcon);
btnResetWindow->setIconSize(QSize(iconWidth, iconHeight));
btnResetWindow->setFlat(true);
connect(btnResetWindow, &QPushButton::clicked, [this]()
{
emit resetWindow();
accept();
setModel();
});
lay->addWidget(btnResetWindow);
lay->addStretch();
stackLayout->addWidget(appcfg);
// end app menu
// web menu
QWidget* webcfg = new QWidget(this);
lay = new QHBoxLayout(webcfg);
lay->setSpacing(0);
lay->setMargin(0);
lay->addStretch();
// reset
btnResetWindow = new QPushButton(webcfg);
btnResetWindow->setIcon(resetIcon);
btnResetWindow->setIconSize(QSize(iconWidth, iconHeight));
btnResetWindow->setFlat(true);
connect(btnResetWindow, &QPushButton::clicked, [this]()
{
emit resetWindow();
accept();
setModel();
});
lay->addWidget(btnResetWindow);
// back
QPushButton* btnBackward = new QPushButton(webcfg);
QIcon backIcon;
backIcon.addFile(":/icon/icon/back.png");
btnBackward->setIcon(backIcon);
btnBackward->setIconSize(QSize(iconWidth, iconHeight));
btnBackward->setFlat(true);
connect(btnBackward, &QPushButton::clicked, [this]()
{
emit backWard();
accept();
});
lay->addWidget(btnBackward);
// forward
QPushButton* btnForward = new QPushButton(webcfg);
QIcon forwardIcon;
forwardIcon.addFile(":/icon/icon/front.png");
btnForward->setIcon(forwardIcon);
btnForward->setIconSize(QSize(iconWidth, iconHeight));
btnForward->setFlat(true);
connect(btnForward, &QPushButton::clicked, [this]()
{
emit forWard();
accept();
});
lay->addWidget(btnForward);
lay->addStretch();
stackLayout->addWidget(webcfg);
// end web menu
// show & hide test
showHideTest = new QTimer(this);
connect(showHideTest, &QTimer::timeout, [this]()
{
QScreen* screen = qApp->primaryScreen();
auto testArea = new QRect(0, 0, screen->size().width(), rect().height());
if (testArea->contains(cursor().pos()))
{
if (canReShow)
{
show();
}
}
else
{
canReShow = true;
hideTest->stop();
hoverTest->stop();
accept();
}
});
showHideTest->start(30);
// hover test
// 当处于非有效区域(对话框之外)5秒后,对话框隐藏
hoverTest = new QTimer(this);
connect(hoverTest, &QTimer::timeout, [this]()
{
qDebug() << buttonArea->left() << buttonArea->right() << cursor().pos();
if (buttonArea->contains(cursor().pos()))
{
canReShow = true;
hideTest->stop();
}
else
{
canReShow = false;
if (!hideTest->isActive())
{
hideTest->start(5000);
}
}
});
// hide when hover time out
hideTest = new QTimer(this);
connect(hideTest, &QTimer::timeout, [this]()
{
hideTest->stop();
hoverTest->stop();
accept();
});
}
bool ConfigDialog::event(QEvent *e)
{
if (e->type() == QEvent::Show)
{
QScreen* screen = qApp->primaryScreen();
QSize curSize = size();
QPoint point(screen->size().width() / 2 - curSize.width() / 2, 0);
move(point);
buttonArea->setTopLeft(point);
buttonArea->setSize(size());
// 启动悬停检测
hoverTest->start(30);
}
// if (e->type() == QEvent::Leave)
// {
// QScreen* screen = qApp->primaryScreen();
// auto testArea = new QRect(0, 0, screen->size().width(), rect().height());
// if (!testArea->contains(cursor().pos()))
// {
// hideTest->stop();
// hoverTest->stop();
// accept();
// }
// }
// qDebug() << "----------------------- config dialog event: " << e->type();
return QWidget::event(e);
}
void ConfigDialog::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.fillRect(rect(), QColor(49, 52, 58, 255));
}

QPalette propagation to QDialog not working

I have set a dark theme on my QWidget. In that widget I call a QDialog. The dialog appears in default palette which is light.
This is my custom dialog (Which in this case is for choosing colors for palette! Twisted right?) .cpp file:
#include "custompalettedialog.h"
CustomPaletteDialog::CustomPaletteDialog(QPalette palette, QWidget *parent) : QDialog(parent),
m_palette(palette)
{
QVBoxLayout* mainLayout = new QVBoxLayout(this);
mainLayout->setSpacing(0);
mainLayout->setMargin(0);
QFormLayout* mainFormLayout = new QFormLayout;
mainFormLayout->setHorizontalSpacing(0);
mainFormLayout->setVerticalSpacing(0);
mainLayout->addLayout(mainFormLayout);
for (int var = 0; var < QPalette::ColorRole::NColorRoles; ++var) {
QPushButton* bandButton = new QPushButton;
bandButton->setFlat(true);
bandButton->setAutoFillBackground(true);
QPalette pal = bandButton->palette();
pal.setColor(QPalette::Button, m_palette.color(static_cast<QPalette::ColorRole>(var)));
bandButton->setPalette(pal);
connect(bandButton, &QPushButton::clicked, this, [this, bandButton, var]() {
QColor color = QColorDialog::getColor();
if (color.isValid()) {
m_palette.setColor(static_cast<QPalette::ColorRole>(var), color);
QPalette pal = bandButton->palette();
pal.setColor(QPalette::Button, color);
bandButton->setPalette(pal);
}
});
mainFormLayout->addRow(QVariant::fromValue(static_cast<QPalette::ColorRole>(var)).toString(), bandButton);
}
QPushButton* doneButton = new QPushButton("Done");
connect(doneButton, &QPushButton::clicked, this, &QDialog::accept);
mainLayout->addWidget(doneButton);
}
QPalette CustomPaletteDialog::getPalette()
{
return m_palette;
}
QPalette CustomPaletteDialog::getPalette(bool* ok, const QPalette palette, QWidget *parent, const QString title)
{
CustomPaletteDialog* dialog = new CustomPaletteDialog(palette, parent);
dialog->setFont(parent->font());
dialog->setModal(true);
dialog->setWindowTitle(title);
dialog->exec();
QPalette pal = dialog->getPalette();
*ok = dialog->result();
dialog->deleteLater();
return pal;
}
and the .h file:
#ifndef CUSTOMPALETTEDIALOG_H
#define CUSTOMPALETTEDIALOG_H
#include <QDialog>
#include <QFormLayout>
#include <QPushButton>
#include <QLineEdit>
#include <QColorDialog>
#include <QComboBox>
class CustomPaletteDialog : public QDialog
{
Q_OBJECT
public:
explicit CustomPaletteDialog(QPalette palette, QWidget *parent = nullptr);
static QPalette getPalette(bool* ok, const QPalette palette, QWidget *parent = nullptr, const QString title = QString());
QPalette getPalette();
private:
QPalette m_palette;
};
#endif // CUSTOMPALETTEDIALOG_H
I call the static method getPalette from a parent widget.
I have tried adding this:
dialog->setPalette(parent->palette());
before dialog->setFont(parent->font()); in that static method and not working. My question is how to propagate QPalette from parent QWidget to child QDialog?
This is how I used it:
QPushButton* paletteAddButton = new QPushButton("Add...");
mainGroupBoxLayout->addWidget(paletteAddButton);
connect(paletteAddButton, &QPushButton::clicked, this, [this, customThemeItemModel]() {
bool ok = false;
QPalette palette = CustomPaletteDialog::getPalette(&ok, this->palette(), this, "Chosse UI Palette");
if (ok) {
QStandardItem* item = new QStandardItem("newPalette");
item->setToolTip("Double Click to Change Name, Right Click for More");
item->setData(palette);
customThemeItemModel->appendRow(item);
saveThemes(customThemeItemModel);
}
});
I have a list of themes, user can add custom themes to that list.

Qt: propagate mouse clicks through a transparent widget

I'm trying to create a widget which, when shown, it will intercept any mouse clicks, process them, but then forward the click to the widget that was under the mouse when it was clicked. I've created a class that represents this widget; all it does for now is capture mouse release events, hide itself and post the event to the widget the click was intended for using QApplication::postEvent:
class Overlay : public QWidget
{
Q_OBJECT
public:
Overlay(QWidget* parent)
: QWidget(parent){}
protected:
void mouseReleaseEvent(QMouseEvent* event)
{
hide();
auto child = parentWidget()->childAt(event->pos()); // get the child widget the event was intended for
auto e = new QMouseEvent(*event);
if(child) QCoreApplication::postEvent(child, e); // why doesn't this have any effect?
event->accept();
}
void paintEvent(QPaintEvent* event)
{
QPainter p(this);
p.fillRect(rect(), QColor(0,0,0,150));
}
};
Unfortunately, the call to postEvent doesn't seem to have any effect at all. Here is an example where I'm setting up some child widgets; clicking on any widget will print something out:
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr)
{
m_overlay = new Overlay(this);
m_overlay->hide();
auto button = new QPushButton("Add overlay");
auto button2 = new QPushButton("PushButton");
auto list = new QListWidget();
list->addItems(QStringList() << "item1" << "item2" << "item3");
connect(list, &QListWidget::itemClicked, [](const QListWidgetItem* item)
{
qDebug() << item->text();
});
connect(button2, &QPushButton::clicked, []()
{
qDebug() << "Button clicked";
});
connect(button, &QPushButton::clicked, [this]()
{
m_overlay->setGeometry(rect());
m_overlay->raise();
m_overlay->show();
});
auto layout = new QVBoxLayout(this);
layout->addWidget(list);
layout->addWidget(button);
layout->addWidget(button2);
}
public:
~Widget(){}
private:
Overlay* m_overlay;
};
I've confirmed that clicks on the overlay are captured and the overlay is hidden as intended but the event is not propagated to the underlying widget. Using QApplication::sendEvent doesn't work either. What am I missing?
Using Qt 5.15.1 on MacOS 11.2.3. Thanks

Qt dialog not showing its widgets

I would like to create custom QT dialog (non-modal). Problem with my implementation is that it shows only dialog window with title, and no widgets that I've added to it.
Code below (I've ommited most of it, added just dialog and main window parts).
MainWindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
/// some other stuff
private:
std::unique_ptr<ui::DialogAddUpdateItem> addItemDialog;
/// some other stuff
}
MainWindow.cpp
/// some stuff
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
/// some stuff
addItemButton = new QPushButton(tr("Add item"));
QObject::connect(addItemButton, &QPushButton::pressed, this, &MainWindow::openAddItemDialog);
navLay->addWidget(addItemButton);
addItemDialog = make_unique<ui::DialogAddUpdateItem>(this);
/// some stuff
}
void MainWindow::openAddItemDialog() {
addItemDialog->show();
//addItemDialog->raise(); does not work with or without those functions
//addItemDialog->activateWindow();
//QApplication::processEvents();
}
DialogAddUpdateItem.h
namespace ui {
class DialogAddUpdateItem : public QDialog
{
Q_OBJECT
public:
DialogAddUpdateItem(QWidget *parent = nullptr);
private:
QPushButton *buttonAcc, *buttonRevert, *buttonCancel;
QGroupBox *centralWidget, *buttonsWidget;
QLabel *labelName, *labelDescription;
QLineEdit *textName;
QPlainTextEdit *textDescription;
}
}
DialogAddUpdateItem.cpp
namespace ui {
DialogAddUpdateItem::DialogAddUpdateItem(QWidget *parent) : QDialog(parent)
{
if (!item) {
setWindowTitle(tr("New object"));
}
centralWidget = new QGroupBox;
QHBoxLayout *itemLay = new QHBoxLayout;
centralWidget->setLayout(itemLay);
labelName = new QLabel(tr("Name"));
itemLay->addWidget(labelName);
textName = new QLineEdit;
if (item) {
textName->setText(QString::fromStdString(item->getName()));
}
itemLay->addWidget(textName);
labelDescription = new QLabel(tr("Description"));
if (item) {
textDescription = new QPlainTextEdit(QString::fromStdString(item->getDescription()));
} else {
textDescription = new QPlainTextEdit;
}
itemLay->addWidget(textDescription);
buttonAcc = new QPushButton(tr("Save"));
buttonAcc->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
QObject::connect(buttonAcc, &QPushButton::clicked, this, &DialogAddUpdateItem::acceptItem);
buttonRevert = new QPushButton(tr("Revert"));
buttonRevert->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
QObject::connect(buttonRevert, &QPushButton::clicked, this, &DialogAddUpdateItem::revertItem);
buttonCancel = new QPushButton(tr("Cancel"));
buttonCancel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
QObject::connect(buttonCancel, &QPushButton::clicked, this, &DialogAddUpdateItem::cancelItem);
buttonsWidget = new QGroupBox;
itemLay->addWidget(buttonsWidget);
QVBoxLayout *buttonsLay = new QVBoxLayout;
buttonsLay->addWidget(buttonAcc);
buttonsLay->addWidget(buttonRevert);
buttonsLay->addWidget(buttonCancel);
}
}
In DialogAddUpdateItem.cpp the centralWidget has no parent, therefore it is not bound to anything and therefore not displayed. You should modify this:
centralWidget = new QGroupBox;
into this:
centralWidget = new QGroupBox(this);
Now the DialogAddUpdateItem will be the parent of the centralWidget and it should display it.
Also it seems like You are leaving some other widgets without a parent - it might cause trouble. For example the QLineEdit textName has this issue.

Qt: the signal handler is not called when the signal is emitted

I am doing something like this:
MyClass::MyClass(QWidget *parent) : QLabel(parent)
{
this->MyMenu = new QMenu();
QAction* act_del = new QAction(MyMenu);
act_delete->setText("MyAction");
MyMenu->addAction(act_del);
QObject::connect(act_del,SIGNAL(triggered()),this,SLOT(MySlot()));
}
void MyClass::MySlot()
{
//Not called
}
Any suggestion on when the SIGNAL is triggered the SLOT is not called. Here is where the menu is displayed:
void MyClass::contextMenuEvent(QContextMenuEvent *ev)
{
QPoint globalPos = this->mapToGlobal(ev->pos());
QAction* selectedItem = MyMenu->exec(globalPos);
if (selectedItem)
{
}
else
{
// nothing was chosen
}
}
Any suggestions on why the SLOT is not called?
MyClass needs to contain the Q_OBJECT macro for the signal-slot connections to work.