How to make a floating widget in Qt
Table of contents
I tend to make the applications I develop to be as customizable as possible because I personally enjoy customizable software. Not everyone has the same workflow.
But If you decide to accommodate a large number of workflows, you run into the risk of making your application too complicated, messy and crammed.
I encounter this problem especially when making a toolbar. Adding widgets such as combo/spin boxes would cost a lot of space, and tool buttons do not offer a lot of interaction, which made me think of using the latter as an anchor instead. A way to display and hide a larger set of settings and widgets.
Take a look:
Basic template
A floating widget is not contained in any layout, it…well…floats, meaning its position is relative to the screen, not the application’s window.
To achieve that, we could use Qt::popup
window flag:
setWindowFlag(Qt::Popup,true);
This would take care of making the widget a top-level one, modal and hide when clicked outside of it. Now remains controlling where it appears.
Let’s use a QToolButton
in QToolBar
, and make the widget popup where its center would align with the tool button’s center.
We could implement that by calculating its (x,y) coordinates in showEvent
:
Calculating x:
Get the x coordinate of the tool button’s center.
Subtract half of the widget width from it.
int parentGlobalX = parentWidget()->mapToGlobal( parentWidget()->rect().center() ).x();
//push it to the left by half of its width
int x = parentGlobalX - width() / 2;
Calculating y:
Use the tool button’s height as the y coordinate of a QPoint
, so it can be mapped to global.
int y = parentWidget()->mapToGlobal( QPoint(0, parentWidget()->geometry().height() ) ).y();
Setting new Position :
Use QWidget::move, and pass it the new (x,y) coordinates:
move(x,y);
Here’s the resulting showEvent
:
void showEvent(QShowEvent *event)
{
int parentGlobalX = parentWidget()->mapToGlobal( parentWidget()->rect().center() ).x();
//push it to the left by half of its width
int x = parentGlobalX - width() / 2;
int y = parentWidget()->mapToGlobal( QPoint(0, parentWidget()->geometry().height() ) ).y();
move(x,y);
}
Here’s how it looks:
Usage
Full Class:
class FloatWidget : public QWidget
{
public:
FloatWidget(QWidget *parent) : QWidget(parent)
{
setWindowFlag(Qt::Popup,true);
setStyleSheet("background: white;");
}
protected:
void showEvent(QShowEvent *event)
{
int parentGlobalX = parentWidget()->mapToGlobal( parentWidget()->rect().center() ).x();
//push it to the left by half of its width
int x = parentGlobalX - width() / 2;
int y = parentWidget()->mapToGlobal( QPoint(0, parentWidget()->geometry().height() ) ).y();
move(x,y);
}
};
Here’s an example of how to use it in a QMainWindow
:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
floating_widgetA = new FloatWidget(ui->toolBar->widgetForAction(ui->actionA));
}
//use QAction triggered signal and connect it to a slot
//to call the floating widget's show in
void MainWindow::on_actionA_triggered()
{
floating_widgetA->show();
}
From this, you can customize it however you like, I made mine animated and wrote a custom paintEvent
for it; I’ve also added a pointer widget to help identify which widget it belongs to.
You can take a look at my public repository on Bitbucket to get some ideas.
Subscribe to my newsletter
Read articles from Abderrahmene Rayene Mihoub directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Abderrahmene Rayene Mihoub
Abderrahmene Rayene Mihoub
I'm passionate about technology and an enthusiastic contributor to the programming community. Despite not being a seasoned professional (yet!), I bring a fresh perspective to problem-solving, mainly because I love solving puzzles, and making things looks neat. Work: Coding journey documented on Bitbucket. Developing small applications available on Pling. Technical Articles: Medium Qt Programming Support: Regular participant on the Qt Forum. Here's my Qt Forum profile. Get in Touch: Reach out for any inquiries, assistance, or job offers at: abderrahmene_mhb@proton.me Support: If you find my contributions helpful, consider supporting my work: Buy me a Coffee Page