PDA

View Full Version : Main Window Title Bar with Date / Clock



Habibie
24th September 2020, 17:05
I am trying to learn Qt on an uBuntu 20.04 Linux Desktop Computer by means of writing a simple program to add a date/clock on its main window title bar. So, I did a google search and found only this https://www.qtcentre.org/threads/56410-Placing-Clock-on-Title-Bar?highlight=Clock+in+Window+Title (Placing Clock on Title Bar) (which drove me to this very forum) article. Unfortunately, the article didn't help at all. So, I decided to write my own as shown below. The program works just fine (no blinking on the separated "". However, the date/clock seems to have replaced the original Window Title. The question is how to I add the date/clock sans losing the original title? While at it, I sure would like to make the separated ":" to blink every second if possible. Anyone?

windowtitleclock.h

#ifndef WINDOWTITLECLOCK_H
#define WINDOWTITLECLOCK_H

#include <QMainWindow>
#include <QTimer>
#include <QDateTime>

QT_BEGIN_NAMESPACE
namespace Ui { class WindowTitleClock; }
QT_END_NAMESPACE

class WindowTitleClock : public QMainWindow
{
Q_OBJECT

public:
WindowTitleClock(QWidget *parent = nullptr);
~WindowTitleClock();

public slots:
void windowTitleClock ();

private:
Ui::WindowTitleClock *ui;
QTimer *timer;
};
#endif // WINDOWTITLECLOCK_H

windowtitleclock.cpp

#include "windowtitleclock.h"
#include "ui_windowtitleclock.h"

WindowTitleClock::WindowTitleClock(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::WindowTitleClock)
{
ui->setupUi(this);
timer = new QTimer (this);
connect (timer, SIGNAL (timeout ()), this, SLOT (windowTitleClock ()));
timer->start (1000);
}

WindowTitleClock::~WindowTitleClock()
{
delete ui;
}

void WindowTitleClock::windowTitleClock()
{
this->setWindowTitle (QDate::currentDate().toString() + " " + QTime::currentTime().toString("[hh:mm:ss]"));
}


main.cpp

#include "windowtitleclock.h"

#include <QApplication>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
WindowTitleClock w;
w.show();
return a.exec();
}

WindowTitleClock.pro

QT += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0

INCLUDEPATH += include
SOURCES += \
src/main.cpp \
src/windowtitleclock.cpp

HEADERS += \
include/windowtitleclock.h

FORMS += \
resources/windowtitleclock.ui

TRANSLATIONS += \
resources/WindowTitleClock_en_US.ts

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

d_stranz
24th September 2020, 18:40
However, the date/clock seems to have replaced the original Window Title.

When you call QWidget::setWindowTitle() it is doing exactly what you are telling it to do: set the title to be the string you are passing in.

If you want the window title to contain something else in addition to your clock, then before you set the new window title for the first time, you need to retrieve the current one (QWidget::windowTitle()) and save it in a QString member variable of your QWindow-based class. When you want to update the clock, you need to take this string, add your clock string on the end, and set that as the new title. You have to use a member variable - you can't simply retrieve the title each time and add the clock. (Try it, you'll see).

As an alternative, you might think about creating a clock widget and adding it to the status bar. QMainWindow::statusBar(), QStatusBar::addWidget()

Habibie
24th September 2020, 20:25
When you call QWidget::setWindowTitle() it is doing exactly what you are telling it to do: set the title to be the string you are passing in.

If you want the window title to contain something else in addition to your clock, then before you set the new window title for the first time, you need to retrieve the current one (QWidget::windowTitle()) and save it in a QString member variable of your QWindow-based class. When you want to update the clock, you need to take this string, add your clock string on the end, and set that as the new title. You have to use a member variable - you can't simply retrieve the title each time and add the clock. (Try it, you'll see).Thank you for your quick response. I tried your suggestion and it partially works in the sense it repeatedly shows the original title + date / clock @every second to completely fill the title.


As an alternative, you might think about creating a clock widget and adding it to the status bar. QMainWindow::statusBar(), QStatusBar::addWidget()As a newbie, this ain't gonna be easy for me. However, this sounds better to me and I sure will look into this approach.

If anyone here has a better approach, I sure will welcome some feedback. Thank you all.

d_stranz
24th September 2020, 21:58
and it partially works

Well, what else do you want it to do? The title is simply a string. If you want it to do something different, then write code to change the string you have stored and let it update the title the next time the timer fires.

If other parts of your app are also calling setWindowTitle for the main window, then that is no good because whatever they set the title to will change the next clock tick. This is why a clock in the status bar makes more sense - it is a standalone widget that doesn't change the behavior of anything else.

Here is some code I wrote to make a digital clock with a transparent background that I can stick in a corner of my Desktop. All of the QSettings and mouse event stuff is just so I can have the clock appear in the same place on the screen after I move it, every time it starts up.

You can easily modify the DigitalClock class to make it work as a status bar widget. See below.



// main.cpp

#include "DigitalClock.h"
#include <QApplication>
#include <QSettings>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);

QCoreApplication::setApplicationName( "Digital Clock" );
QCoreApplication::setOrganizationName( "D_Stranz" );
QCoreApplication::setOrganizationDomain( "Domains R Us" );
QSettings::setDefaultFormat( QSettings::IniFormat );

DigitalClock w;
w.show();
return a.exec();
}




// DigitalClock.h

#ifndef DIGITALCLOCK_H
#define DIGITALCLOCK_H

#include <QtWidgets/QWidget>

class QLabel;
class QTimer;

class DigitalClock : public QWidget
{
Q_OBJECT

public:
DigitalClock(QWidget *parent = 0);
~DigitalClock();

protected slots:
void onUpdateDateTime();

protected:
void mousePressEvent( QMouseEvent * pEvent ) override;
void mouseMoveEvent( QMouseEvent * pEvent ) override;
void mouseReleaseEvent( QMouseEvent * pEvent ) override;
void showEvent( QShowEvent * pEvent ) override;

private:
QLabel * mpTimeLabel;
QLabel * mpDateLabel;
QTimer * mpTimer;

QPoint mPos;
bool mbMoving = false;
};

#endif // DIGITALCLOCK_H





// DigitalClock.cpp

#include "DigitalClock.h"

#include <QVBoxLayout>
#include <QLabel>
#include <QTimer>
#include <QDateTime>
#include <QTime>
#include <QDate>
#include <QFont>
#include <QMouseEvent>
#include <QAction>
#include <QSettings>

DigitalClock::DigitalClock(QWidget *parent)
: QWidget( parent , Qt::FramelessWindowHint )
{
setAttribute( Qt::WA_TranslucentBackground, true );
setAttribute( Qt::WA_TransparentForMouseEvents, false );

QVBoxLayout * pLayout = new QVBoxLayout( this );

QFont curFont = font();
curFont.setPointSize( 52 );

mpTimeLabel = new QLabel( "" );
mpTimeLabel->setFont( curFont );
mpTimeLabel->setAlignment( Qt::AlignCenter );

curFont.setPointSize( 32 );
mpDateLabel = new QLabel( "" );
mpDateLabel->setAlignment( Qt::AlignCenter );
mpDateLabel->setFont( curFont );

pLayout->addWidget( mpTimeLabel );
pLayout->addWidget( mpDateLabel );
setLayout( pLayout );

QAction * pExitAction = new QAction( "Exit", this );
connect( pExitAction, &QAction::triggered, this, &QWidget::close );

curFont.setPointSize( 12 );
setFont( curFont );
setContextMenuPolicy( Qt::ActionsContextMenu );
addAction( pExitAction );

mpTimer = new QTimer( this );
mpTimer->setInterval( 1000 );
connect( mpTimer, &QTimer::timeout, this, &DigitalClock::onUpdateDateTime );
mpTimer->start();

mPos = mapToGlobal( pos() );
}

DigitalClock::~DigitalClock()
{

}

void DigitalClock::onUpdateDateTime()
{
QDateTime now = QDateTime::currentDateTime();
QTime nowTime = now.time();

int hour = nowTime.hour();
hour = ( hour <= 12 ? hour : hour - 12 );
QString timeStr = QString( "%1:%2:%3" ).arg( hour, 2, 10, QChar(' ') ).arg( nowTime.minute(), 2, 10, QChar( '0' ) ).arg( nowTime.second(), 2, 10, QChar( '0' ) );
mpTimeLabel->setText( timeStr );

QDate nowDate = now.date();
QString dateStr = QString( "%1/%2/%3" ).arg( nowDate.day(), 2, 10, QChar( ' ' ) ).arg( nowDate.month(), 2, 10, QChar( '0' ) ).arg( nowDate.year(), 4 );
mpDateLabel->setText( dateStr );
}

void DigitalClock::mousePressEvent( QMouseEvent * pEvent )
{
if ( pEvent->button() == Qt::LeftButton )
{
mPos = mapToGlobal( pEvent->pos() );
mbMoving = true;
}
}

void DigitalClock::mouseMoveEvent( QMouseEvent * pEvent )
{
if ( mbMoving )
{
mPos = mapToGlobal( pEvent->pos() );
move( mPos );
}
}

void DigitalClock::mouseReleaseEvent( QMouseEvent * pEvent )
{
mbMoving = false;

QSettings settings;
settings.beginGroup( "Global" );
settings.setValue( "Position", QVariant( mPos ) );
settings.endGroup();
}

void DigitalClock::showEvent( QShowEvent * pEvent )
{
QSettings settings;
settings.beginGroup( "Global" );
QVariant var = settings.value( "Position" );
settings.endGroup();

if ( !var.isNull() )
{
mPos = var.toPoint();
move( mPos );
}

}



You can try that code, and then modify it to work as a statusbar widget. This code makes an ordinary widget, changes the layout to horizontal rather than vertical (so the date and time are side-by-side). If you don't want the date, then simply delete that QLabel and change the update slot to remove the formatting for the date label. Adding the widget as a permanent widget in the status bar means that it will always be visible and never be overwritten with a status bar message.



#ifndef DIGITALCLOCK_H
#define DIGITALCLOCK_H

#include <QtWidgets/QWidget>

class QLabel;
class QTimer;

class DigitalClock : public QWidget
{
Q_OBJECT

public:
DigitalClock(QWidget *parent = 0);
~DigitalClock();

protected slots:
void onUpdateDateTime();

private:
QLabel * mpTimeLabel;
QLabel * mpDateLabel;
QTimer * mpTimer;

};




#include "DigitalClock.h"

#include <QHBoxLayout>
#include <QLabel>
#include <QTimer>
#include <QDateTime>
#include <QTime>
#include <QDate>

DigitalClock::DigitalClock(QWidget *parent)
: QWidget( parent )
{
QHBoxLayout * pLayout = new QHBoxLayout( this );

mpTimeLabel = new QLabel( "" );
mpTimeLabel->setAlignment( Qt::AlignLeft );

mpDateLabel = new QLabel( "" );
mpDateLabel->setAlignment( Qt::AlignRight );

pLayout->addWidget( mpTimeLabel );
pLayout->addWidget( mpDateLabel );
setLayout( pLayout );

mpTimer = new QTimer( this );
mpTimer->setInterval( 1000 );
connect( mpTimer, &QTimer::timeout, this, &DigitalClock::onUpdateDateTime );
mpTimer->start();
}

DigitalClock::~DigitalClock()
{

}

void DigitalClock::onUpdateDateTime()
{
QDateTime now = QDateTime::currentDateTime();
QTime nowTime = now.time();

int hour = nowTime.hour();
hour = ( hour <= 12 ? hour : hour - 12 );
QString timeStr = QString( "%1:%2:%3" ).arg( hour, 2, 10, QChar(' ') ).arg( nowTime.minute(), 2, 10, QChar( '0' ) ).arg( nowTime.second(), 2, 10, QChar( '0' ) );
mpTimeLabel->setText( timeStr );

QDate nowDate = now.date();
QString dateStr = QString( "%1/%2/%3" ).arg( nowDate.day(), 2, 10, QChar( ' ' ) ).arg( nowDate.month(), 2, 10, QChar( '0' ) ).arg( nowDate.year(), 4 );
mpDateLabel->setText( dateStr );
}





#include "DigitalClock.h"
#include <QApplication>
#include <QMainWindow>
#include <QStatusBar>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);

QMainWindow w;

DigitalClock * pClock = new DigitalClock( &w );
QStatusBar * pBar = w.statusBar();
pBar->addPermanentWidget( pClock );

w.show();
return a.exec();
}

Habibie
25th September 2020, 01:27
Well, what else do you want it to do? The title is simply a string. If you want it to do something different, then write code to change the string you have stored and let it update the title the next time the timer fires.You are right. Thank you for your explanation and I fixed the issue now.


If other parts of your app are also calling setWindowTitle for the main window, then that is no good because whatever they set the title to will change the next clock tick. This is why a clock in the status bar makes more sense - it is a standalone widget that doesn't change the behavior of anything else.I sure have appreciated this advice and will look into this as I will progress to learn more about Qt.


Here is some code I wrote to make a digital clock with a transparent background that I can stick in a corner of my Desktop. All of the QSettings and mouse event stuff is just so I can have the clock appear in the same place on the screen after I move it, every time it starts up.Again, thank you and I will learn something from your code.