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;
}
};
Related
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.
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
I have a simple Qt toolbar with text only button Action:
MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
{
QToolBar* toolBar = new QToolBar(this);
QAction* action = toolBar->addAction("&Action");
QObject::connect(action, SIGNAL(triggered()), this, SLOT(onAction()));
action->setShortcut(QKeySequence("ctrl+a"));
addToolBar(toolBar);
}
I would like to have A in Action underlined to reflect its role as a shortcut key. How to accomplish that?
Standard QAction widget (it is a QToolButton actually) uses stripped version of its text for display: "&Menu Option..." becomes "Menu Option".
You can create a custom QAction widget which does not use stripped text by subclassing QWidgetAction:
MyAction::MyAction(QObject *parent) :
QWidgetAction(parent)
{
}
QWidget* MyAction::createWidget(QWidget *parent)
{
QToolButton *tb = new QToolButton(parent);
tb->setDefaultAction(this);
tb->setText(this->text());// override text stripping
tb->setFocusPolicy(Qt::NoFocus);
return tb;
}
In your MainWindow constructor use it as follows:
MainWindow(QWidget* parent=0) : QMainWindow(parent)
{
QToolBar* toolBar = new QToolBar(this);
MyAction* action = new MyAction();
action->setText("&Action");
action->setShortcut(QKeySequence(tr("ctrl+a","Action")));
toolBar->addAction(action);
QObject::connect(action, SIGNAL(triggered()), this, SLOT(onAction()));
addToolBar(toolBar);
}
Appearence of underline shortcut letters depends on your application style.
Here is an example of a custom style that will force shortcut underline display:
class MyStyle : public QProxyStyle
{
public:
MyStyle();
int styleHint(StyleHint hint,
const QStyleOption *option,
const QWidget *widget,
QStyleHintReturn *returnData) const;
};
int MyStyle::styleHint(QStyle::StyleHint hint,
const QStyleOption *option,
const QWidget *widget,
QStyleHintReturn *returnData) const
{
if (hint == QStyle::SH_UnderlineShortcut)
{
return 1;
}
return QProxyStyle::styleHint(hint, option, widget, returnData);
}
Then you should set that style to your application:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setStyle(new MyStyle);
Widget w;
w.show();
return a.exec();
}
Can I prevent non-modal QDialog from closing when the user hits Esc, other than by overriding reject() or using event filter / event method?
If not, is it possible with a modal QDialog?
If you don't want to use eventFilter, you can use QShortcut to create a keyboard shortcut for Escape and call the dialog's show slot. This can be done like:
MyDialog::MyDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::MyDialog)
{
ui->setupUi(this);
QShortcut * shortcut = new QShortcut(QKeySequence(Qt::Key_Escape),this,SLOT(show()));
shortcut->setAutoRepeat(false);
}
You can override eventFilter in your dialog and manage it on your own when Escape is pressed :
MyDialog::MyDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::MyDialog)
{
ui->setupUi(this);
this->installEventFilter(this);
}
bool MyDialog::eventFilter(QObject *obj, QEvent * event)
{
if((MyDialog *)obj == this && event->type()==QEvent::KeyPress && ((QKeyEvent*)event)->key() == Qt::Key_Escape )
{
return true;
}
return false;
}
Create a QObject-based class to receive the events: -
class MyEvents : public QObject
{
Q_OBJECT
public:
MyEvents(QObject* parent);
protected:
bool eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if(keyEvent->key() == Qt::Key_Escape)
return true;
else
return QObject::eventFilter(obj, event);
}
else
{
// standard event processing
return QObject::eventFilter(obj, event);
}
}
};
Then install the object as an event filter for the dialog
MyEvents* myEvents = new MyEvents;
QDialog myDialog(parent);
myDialog->installEventFilter(myEvents);
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.