Building a Customizable Mini Clock Widget with Qt
Window Structure Analysis
Creating miniature widgets like those found in trading platforms requires understanding three core components:
- Window Frame: The outer boundary that enables resizing functionality
- Title Bar: Custom implementation replacing native Windows title bar
- Client Area: The content region where custom widget are displayed
Interactive Window Resizing
The key to creating resizable mini-widgets lies in overriding mouse event handlers to detect edge interactions and modify window dimensions accordingly.
class ResizableWidget : public QWidget {
protected:
void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
};
Cursor State Management
Determining weather the cursor is positioned over a resizeable edge requires checking boundary conditions:
void CustomWidget::updateEdgeDetection(const QPoint& mousePos) {
QRect widgetRect = rect();
int edgeThreshold = 5;
bool atLeft = mousePos.x() <= edgeThreshold;
bool atRight = mousePos.x() >= widgetRect.width() - edgeThreshold;
bool atTop = mousePos.y() <= edgeThreshold;
bool atBottom = mousePos.y() >= widgetRect.height() - edgeThreshold;
// Corner detection combinations
topLeftCorner = atTop && atLeft;
topRightCorner = atTop && atRight;
bottomLeftCorner = atBottom && atLeft;
bottomRightCorner = atBottom && atRight;
canResize = atLeft || atRight || atTop || atBottom;
}
Dynamic Cursor Appearance
Based on edge detection results, the cursor appearance changes to indicate available resize directions:
void CustomWidget::adjustCursor(const QPoint& position) {
updateEdgeDetection(position);
if (topLeftCorner || bottomRightCorner) {
setCursor(Qt::SizeFDiagCursor);
} else if (topRightCorner || bottomLeftCorner) {
setCursor(Qt::SizeBDiagCursor);
} else if (atLeft || atRight) {
setCursor(Qt::SizeHorCursor);
} else if (atTop || atBottom) {
setCursor(Qt::SizeVerCursor);
} else {
unsetCursor();
}
}
Window Dimension Adjustment
Resizing calculations use the initial press position as reference point to maintain consistent behavior:
void CustomWidget::handleResize(QMouseEvent* event) {
QPoint currentGlobal = event->globalPos();
QPoint offset = currentGlobal - pressStartPosition;
QRect newGeometry = initialGeometry;
if (atLeft) newGeometry.setLeft(newGeometry.left() + offset.x());
if (atRight) newGeometry.setRight(newGeometry.right() + offset.x());
if (atTop) newGeometry.setTop(newGeometry.top() + offset.y());
if (atBottom) newGeometry.setBottom(newGeometry.bottom() + offset.y());
setGeometry(newGeometry);
}
Clock Display Implementation
The clock widget features dynamic text scaling based on window size using QPainter transformations.
Layout Structure
A simple vertical arrangement contains the time display and date information:
// Create separator line
QFrame* separator = new QFrame();
separator->setFrameShape(QFrame::HLine);
separator->setStyleSheet("color: #474F57;");
// Setup layout
QVBoxLayout* layout = new QVBoxLayout();
layout->addWidget(timeDisplay);
layout->addWidget(separator);
layout->addWidget(dateDisplay);
setLayout(layout);
Scalable Text Rendering
The core innovation uses scene transformation to achieve smooth font scaling:
void ScalableTextLabel::paintEvent(QPaintEvent* event) {
QWidget::paintEvent(event);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
QFont font = painter.font();
font.setPixelSize(14);
painter.setFont(font);
// Calculate optimal scale factor
double scaleFactor = qMin(width(), height()) / 100.0;
// Center and scale drawing area
painter.translate(width() / 2, height() / 2);
painter.scale(scaleFactor, scaleFactor);
// Measure text dimensions
int textWidth = painter.fontMetrics().horizontalAdvance(displayText);
int textHeight = painter.fontMetrics().height();
// Draw centered text
QRect textRect(-textWidth/2, -textHeight/2, textWidth, textHeight);
painter.drawText(textRect, displayText);
}
Real-time Updates
A timer updates the display every second with current time information:
// Initialize update timer
QTimer* updateTimer = new QTimer(this);
connect(updateTimer, &QTimer::timeout, this, &ClockWidget::refreshTime);
updateTimer->start(1000);
void ClockWidget::refreshTime() {
QDateTime currentTime = QDateTime::currentDateTime();
// Update time display
timeLabel->setText(currentTime.toString("HH:mm:ss"));
// Update date display
QString dateString = QString("Beijing(CN) %1")
.arg(currentTime.toString("yyyy/MM/dd"));
dateLabel->setText(dateString);
// Trigger repaint
update();
}