How to set a custom font size on a widget that adapts accordingly after an application font size change? - c++

I am using Qt4 (I know), and have a custom widget that sets a bold font, with a point size that is 1.5 times the point size of the application font. This works without problems.
The problem now is that in case the application font size is changed, the widget's font size is not updated accordingly (as expected). My initial though was to do something along the following lines:
void MyCustomWidget::changeEvent(QEvent* e)
{
if (e->type() == QEvent::FontChange)
{
setFont(boldAndBigFont());
}
return QWidget::changeEvent(e);
}
This does not work, since the setFont() call will trigger a FontChange event resulting in endless calls to the change event handler. I noticed there is also ApplicationFontChange, which is promising, however that event is not delivered to my custom widget (I verified using an event listener). Looking at the Qt code, the code responsible for propagating the ApplicationFontChange event will only deliver this event to a few select widgets (the main window for example).
So my question is; how to solve this in a nice way? One limitation I have is that I cannot use style sheets.
My current solution leans towards a custom font change event, fired from the main window after receiving ApplicationFontChange, but surely, I cannot be the first person to have this problem...?
Update: I found that calling QApplication::setFont(bigAndBoldFont(), "MyCustomWidget"); works as well. I do not particularly like it since I would rather keep this styling behavior tied to the implementation of the custom widget.

I can't vouch for Qt4 but the following changeEvent implementation appears to work as expected with Qt5...
virtual void changeEvent (QEvent *event) override
{
if (event->type() == QEvent::FontChange) {
/*
* Run the default handler for the event.
*/
super::changeEvent(event);
/*
* Now get the application font and create the desired font based on
* that.
*/
auto app_font = qApp->font();
auto desired_font = QFont(app_font.family(), 1.5 * app_font.pointSize(), QFont::Bold);
/*
* If the font we now have is the desired font then fine, otherwise
* set it.
*/
if (font() != desired_font) {
setFont(desired_font);
}
event->accept();
}
super::changeEvent(event);
}

Related

Call button click function from grandchild

I'm creating my first C++ wxWidgets application. I'm trying to create some kind of split button where the options are displayed in a grid. I have a custom button class which, when right-clicked on, opens a custom wxPopupTransientWindow that contains other buttons.
When I click on the buttons in the popup, I want to simulate a left click on the main button. I'm trying to achieve this through events, but I'm kinda confused.
void expandButton::mouseReleased(wxMouseEvent& evt)
{
if (pressed) {
pressed = false;
paintNow();
wxWindow* mBtn = this->GetGrandParent();
mBtn->SetLabel(this->GetLabel());
mBtn->Refresh();
wxCommandEvent event(wxEVT_BUTTON);
event.SetId(GetId());
event.SetEventObject(mBtn);
mBtn-> //make it process the event somehow?
wxPopupTransientWindow* popup = wxDynamicCast(this->GetParent(), wxPopupTransientWindow);
popup->Dismiss();
}
}
What is the best way to do this?
You should do mBtn->ProcessWindowEvent() which is a shorter synonym for mBtn->GetEventHandler()->ProcessEvent() already mentioned in the comments.
Note that, generally speaking, you're not supposed to create wxEVT_BUTTON events from your own code. In this particular case and with current (and all past) version(s) of wxWidgets it will work, but a cleaner, and guaranteed to also work with the future versions, solution would be define your own custom event and generate it instead.

Qt event when widget goes out of sight

Is there a way in Qt to handle situation when any widget of Window goes out of sight. I.e if a widget was in tab control and user have changed active tab, or if user just scrolls and widget goes offscreen, and also when it goes back on screen.
Is that possible to add some code to this two events?
Best if this can be done globally...
Is there a way in Qt to handle situation when any widget of Window goes out of sight. I.e if a widget was in tab control and user have changed active tab, or if user just scrolls and widget goes offscreen, and also when it goes back on screen.
The way the question asked makes one think that the widget show-hide-expose state changes need to be handled:
bool MyWidget::event(QEvent* pEvent)
{
if (pEvent->type() == QEvent::Show)
{
// event "shown"
}
else if (pEvent->type() == QEvent::Hide)
{
// event "hidden"
}
else if (pEvent->type() == QEvent::Expose)
{
// event "exposure changed"
// deal with QExposeEvent and evaluate the exposed region
// QExposeEvent* pExposeEvent = reinterpret_cast<QExposeEvent*>(pEvent);
}
return QWidget::event(pEvent);
}
Best if this can be done globally...
Event filter at the top level widget may solve that. Or you can override event() function for the top level widget but finding what exact widget was affected is another thing.
Refer to QExposeEvent description.

Qt event when anything changed on the window/screen + Screenshot

I'm thinking of extending a QT4 application with some debug possibilities, to make it easier analyzing customer issues. The application already has a "Debug" mode, when this is enabled, a lot of log entries generated, which is hard to read.
What I would like to achive is taking a screenshot of the application, whenever something is changed on the GUI. I know that it may take a lot of pictures, but generally Debug mode is not enabled for a long time. The problem is I cannot find such an event/signal. So I have two question:
Is there such an event I could subscribe? I mean, an event that is
fired whenever anything changes on the screen.
Can I take a screenshot of the application using Qt?
Thanks in advance!
I'd do it using an event filter and a QTimer, something like this:
class MyEventFilter : public QObject
{
public:
MyEventFilter() : _screenshotPending(false) {/* empty */}
virtual bool eventFilter(QObject * o, QEvent * e)
{
if (e->type() == QEvent::Paint)
{
if (_screenshotPending == false)
{
// we'll wait 500mS before taking the screenshot
// that way we aren't trying to take 1000 screenshots per second :)
_screenshotPending = true;
QTimer::singleShot(500, this, SLOT(TakeAScreenshot()));
}
}
return QObject::eventFilter(o, e);
}
public slots:
void TakeAScreenshot()
{
_screenshotPending = false;
// add the standard Qt code for taking a screenshot here
// see $QTDIR/examples/widgets/desktop/screenshot for that
}
private:
bool _screenshotPending; // true iff we've called QTimer::singleShot() recently
};
int main(int argc, char ** argv)
{
MyEventFilter filter;
QApplication app(argc, argv);
app.installEventFilter(&filter);
[...]
return app.exec();
}
Generally, when some widget changes Qt needs to repaint it, so the event you would be interested in is QEvent::Paint. The problem here is that there will be tons of these events for widgets that overlap each other. You can override QApplication::notify() to catch all paint events before they are even delivered to receivers.
As for making screenshots of Qt application - there are several similar questions here on SO, for example screenshot of a qt application from inside the application or Taking screenshot of a specific window - C++ / Qt
Here is also a thread discussing dumping widgets to images in paintEvent().
As for your second question, here is some of my old code that can take a screenshot of a window. You can use this code like so:
HDC WinDC = GetDC(HWND_OF_YOUR_WINDOW);
HBITMAP image = ScreenshotUtility::fromHDC(WinDC);
Then you can convert the HBITMAP to a Qt Pixmap object and work with it how you like: QPixmap pixmap = QPixmap::fromWinHBITMAP(image);.
EDIT: this is Windows-specific code, not sure what the equivalent on other systems may be.

How to set text below the QToolButton in QT not below the icon

I'm using QToolButton and I set the icon.
Now I want Text "below the QToolButton", "Not below the icon".
Is there any way to achieve this in C++,QT in Linux ?
I've found myself in the same position a while back ago while I was making an application for an embedded Linux system.
I haven't found a straight forward solution (I was searching for a way to achieve it with CSS).
What I ended up doing, was creating a new QWidget (using the designer). Then placing the button in it with a QLabel under it.
Then added a simple static function
static void wdgCustomButton::create(const QString iconPath, const QString text)
{
// create a new button here, create some modification functions for
// text, image and optionally QStyleSheets.
// Call those here (pass the arguments)
// Then return the button
// pseudo code, (not tested):
wdgCustomButton button = new wdgCustomButton( /* could pass a parent */ );
button->setIcon( iconPath ); // function simply calls the ui->button->setIcon
button->setText( text ); // function simply calls the ui->label->setText
return button;
}
And then add those new QWidgets to your pannel using code (maybe someone knows how to get it in the default toolbar, but I haven't searched for that myself yet since I never needed it).
this->menuButtons[menuBtnsCount] = wdgCustomButton::create( ":/Images/Warning.png", "Delete everything" );
this->menuButtons[menuBtnsCount]->setGeometry( QRect( /* size and position here */ ) );
this->menuButtons[menuBtnsCount]->show();
I hope this might give you an idea to fix it in an easy way!
Edit:
I'm sorry, I forgot to add something about the click event. The click event was mainly why I made a QWidget out of it!
I just used the connect function [I belive on the whole button like: connect(this->menuButtons[0], ...]

qt resize window after widget remove

I'm adding widget in layout
ui->horizontalLayout->addWidget(tabwidget);
and qmainwindow resizes itself. But then I do
tabwidget->setVisible(false);
qs = sizeHint();
resize(qs);
I get the size like tabwidget was not removed from window.
I've made new button
void MainWindow::on_pushButton_2_clicked()
{
qs = sizeHint();
resize(qs);
}
and it gives correct size.
Seems I need some update function but I can't find it. Please advice
This is caused by a long-time internal Qt issue (I remember experiencing it first with Qt3). The top widget needs to receive an event to truly update its geometry, and know its boundaries. This event seems to be always received after the resize event generated by the layout, and therefore it is always too late to shrink the widget.
The solution posted in the accepted answer works. However a simpler solution posted below also works, when added after the layout :
layout()->removeWidget( widget );
QApplication::processEvents( QEventLoop::ExcludeUserInputEvents );
resize( sizeHint() );
Basically all we need is to let the event loop run and deliver the necessary events so the top widget geometry is updated before resize() is run.
Note that this code might have side effects if you have multiple threads running, or there are events delivered to your slots. Hence it is safer not to have any code in this function after resize().
If the button slot gives you the correct result then you can always call the sizeHint() and subsequent resize() in a slot which is called by a single shot timer:
void MainWindow::fixSize()
{
QSize size = sizeHint();
resize(size);
}
void MainWindow::methodWhereIHideTheTabWidget()
{
tabwidget->setVisible(false);
QTimer::singleShot(0, this, SLOT(fixSize()));
}
This timer is set to zero delay. This means that the slot will be called immediatelly when the program returns to the main loop and hopefully after the internal widget state gets updated. If this doesn't resolve your problem you may try replacing zero with 1.