Qt: the signal handler is not called when the signal is emitted - c++

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.

Related

How to detect when a QWebEngineView widget is opened/closed?

I'm adding the discord widget from widgetbot to a grid layout in a way it can expand above other widgets, the problem is, even when the widget is 'closed' the QWebEngineView widget occupies the entire area blocking things below it to be clicked:
I thought of setting a maximum size to the widget when it is opened and another when it's closed so it doesn't overlay other widgets when it's not 'opened'.
I tried installing an event filter but it didn't throw any event when the widget is opened/closed, would like to ask what other way I could detect it?
#include "discordwidget.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
auto wdgt = new DiscordWidget(this);
}
//discordwidget.h
#include <QtWebEngineWidgets>
class DiscordWidget : public QWebEngineView
{
Q_OBJECT
public:
DiscordWidget(QWidget* parent = 0) : QWebEngineView(parent)
{
this->page()->setBackgroundColor(Qt::transparent);
// Tutorial: https://docs.widgetbot.io/embed/crate/tutorial/#getting-started
this->page()->setHtml(R"(
<script src='https://cdn.jsdelivr.net/npm/#widgetbot/crate#3' async defer>
new Crate({
server: '', // Replace with your discord server
channel: '' // ... channel
})
</script>
)");
this->installEventFilter(this);
this->page()->installEventFilter(this);
}
bool eventFilter(QObject *obj, QEvent *e)
{
qDebug() << e->type();
if( e->type() == QEvent::ChildAdded )
{
QChildEvent *ce = static_cast<QChildEvent*>(e);
ce->child()->installEventFilter(this);
}
return false;
}
};

qt create button when right click

I am new in qt I want to create a button when I right click
There is my code:
void MainWindow::right_clicked(QMouseEvent *event)
{
if(event->button() == Qt::RightButton)
{
QPushButton *item = new QPushButton();
item->setIcon(QIcon(":/images/7928748-removebg-preview(1).ico"));
item->setIconSize(QSize(32, 32));
item->setGeometry(QRect(QPoint(event->x(), event->y()), QSize(32, 32)));
}
}
But nothing appears
To capture any mouse event in a QWidget you must override the mousePressEvent method.
class MainWindow : public QMainWindow
{
Q_OBJECT
protected:
void mousePressEvent(QMouseEvent *event);
};
And in the mainwindow.cpp, implement it as follows:
void MainWindow::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::RightButton) {
// make mainwindow parent of this button by passing "this" pointer
QPushButton *item = new QPushButton(QIcon(":/images/close-button-icon"), "", this);
// set button position to the location of mouse click
item->setGeometry(QRect(QPoint(event->x()-16, event->y()-16), QSize(32, 32)));
item->show();
}
}
If you don't save the pointer to QPushButton, then you will not be able to use it afterwards.

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

How to make QMenu Item checkable in QT

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

Qt QWidget hide animation

I have a sub-class of QWidget that is a popup widget. I would like to add some animation when it shows and disappears. So I re-implemented showEvent(QShowEvent * event) and hideEvent and added some QPropertyAnimation in the functions. The showEvent works just fine for me but the hideEvent doesn't. Because
Hide events are sent to widgets immediately after they have been hidden.
Any idea about how to do it?
Update:
I don't think it's the right reason. When I use Nejat's solution. The show part works. But when I click outside the widget. It disappears immediately.
You should override QWidget::closeEvent() so when trying to close immediatly it will be ignored AND we start our animation and after finishing (QPropertyAnimation::finished()) we close the widget as normal.
Here is a demo to demonstrate:
class AnimatedWidget : public QWidget {
Q_OBJECT
Q_PROPERTY(qreal alpha READ alpha WRITE setAlpha)
public:
AnimatedWidget(QWidget* parent = nullptr) :QWidget{ parent }, opacityAnimation{ new QPropertyAnimation{this, "alpha",this} } {
setWindowFlags(windowFlags() | Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::Tool);
auto pal = palette();
pal.setColor(QPalette::Background, Qt::cyan);
setAutoFillBackground(true);
setPalette(pal);
setFixedSize(200, 200);
}
qreal alpha() const {
return windowOpacity();
}
void setAlpha(qreal level) {
setWindowOpacity(level);
update();
}
protected:
void closeEvent(QCloseEvent* e) override {
if (opacityAnimation->currentValue().toReal() == 1.0) { // Ignore event + start animation
e->ignore();
startHide();
QObject::connect(opacityAnimation, SIGNAL(finished()), this, SLOT(onAnimationCallBack()), Qt::UniqueConnection);
} else {
e->accept();
if (!isHidden())
hide();
QWidget::close(); // necessary actions
}
}
public Q_SLOTS:
void show() {
startShow();
QWidget::show(); // necessary actions
}
private Q_SLOTS:
void onAnimationCallBack() {
if (opacityAnimation->currentValue().toReal() == 0.0) { // we're finished so let's really close the widget
QCloseEvent ev;
QApplication::sendEvent(this, &ev);
qApp->sendEvent(this, &ev);
}
}
void startHide() {
opacityAnimation->setStartValue(1.0);
opacityAnimation->setEndValue(0.0);
opacityAnimation->setDuration(1500);
opacityAnimation->start();
}
void startShow() {
opacityAnimation->setStartValue(0.0);
opacityAnimation->setEndValue(1.0);
opacityAnimation->setDuration(1500);
opacityAnimation->start();
}
private:
QPropertyAnimation* opacityAnimation = nullptr;
};
class Base : public QWidget {
public:
Base(QWidget* parent = nullptr) :QWidget{ parent }, widget{ new AnimatedWidget{} } {
}
private:
AnimatedWidget* widget;
protected:
void mouseReleaseEvent(QMouseEvent* e) override {
if (widget->isHidden())
widget->show();
else
widget->close();
QWidget::mouseReleaseEvent(e);
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Base base;
base.show();
return app.exec();
}
You can override the eventFilter in your widget and check for QEvent::Show and QEvent::Close events.
bool MyWidget::eventFilter(QObject * obj, QEvent * event)
{
if(obj == this && event->type() == QEvent::Show)
{
//about to show
}
else if(obj == this && event->type() == QEvent::Close)
{
//about to close
}
return false;
}
You should also install the event filter in the constructor by:
this->installEventFilter(this);