Qt Framework’ün sunduğu standart butonlar, etiketler ve kaydırma çubukları genel masaüstü yazılımları için fazlasıyla yeterlidir. Ancak endüstriyel otomasyon arayüzleri, telemetri ekranları veya otomotiv panelleri geliştirirken dairesel hız göstergeleri (gauge), dalga formu monitörleri veya özel durum ışıkları tasarlamanız gerekir. Bu tür ileri düzey grafiksel gereksinimlerde QPainter ile custom widget oluşturmak, piksel düzeyinde tam kontrol ve yüksek donanımsal çizim performansı sunar.
Bu rehberde; bir QWidget sınıfından türeyerek paintEvent metodunu ezmeyi (override), kenar yumuşatmayı (Antialiasing), QPainter koordinat sistemi dönüşümlerini ve dinamik bir dairesel analog gösterge (Gauge Widget) yapımını adım adım inşa edeceğiz.
Özel Widget Çiziminin Yaşam Döngüsü
Qt’de bir widget ekrana her çizildiğinde veya pencere yeniden boyutlandırıldığında (resize), Qt olay döngüsü ilgili nesneye bir QPaintEvent iletir.
Kendi görselimizi çizmek için:
QWidgetsınıfından yeni bir C++ sınıfı türetiriz.paintEvent(QPaintEvent *event)sanal fonksiyonunu ezerek (override) içineQPainternesnesi bağlarız.- Çizimi güncellemek istediğimizde (örneğin yeni bir sensör verisi geldiğinde) widget üzerinde
update()fonksiyonunu çağırırız.
Altın Kural:
paintEventiçinde asla ağır hesaplamalar, dosya okuma veya ağ istekleri yapmayın! Sadece mevcut durumu ekrana çizin. Yeniden çizim talebi için doğrudanpaintEventçağırmayın, daimathis->update()metodunu kullanın.
Örnek Proje: Dairesel Analog Hız Göstergesi (Speedometer Gauge)
Gelin sıfırdan değer alan, iğnesi dönen ve renkli kadranı olan bir AnalogGauge sınıfı tasarlayalım.
Başlık Dosyası: analoggauge.h
#ifndef ANALOGGAUGE_H
#define ANALOGGAUGE_H
#include <QWidget>
#include <QPainter>
class AnalogGauge : public QWidget {
Q_OBJECT
Q_PROPERTY(double value READ value WRITE setValue NOTIFY valueChanged)
public:
explicit AnalogGauge(QWidget *parent = nullptr);
double value() const { return m_value; }
void setValue(double val);
void setRange(double min, double max);
signals:
void valueChanged(double newValue);
protected:
void paintEvent(QPaintEvent *event) override;
QSize sizeHint() const override { return QSize(200, 200); }
private:
double m_value = 0.0;
double m_minValue = 0.0;
double m_maxValue = 100.0;
};
#endif // ANALOGGAUGE_H
Kaynak Dosyası: analoggauge.cpp
#include "analoggauge.h"
#include <QtMath>
AnalogGauge::AnalogGauge(QWidget *parent) : QWidget(parent) {
// Arka planın şeffaf olmasına veya otomatik silinmesine izin ver
setAttribute(Qt::WA_OpaquePaintEvent, false);
}
void AnalogGauge::setValue(double val) {
if (qFuzzyCompare(m_value, val)) return;
// Değeri sınırlar içine hapsedelim
m_value = qBound(m_minValue, val, m_maxValue);
emit valueChanged(m_value);
// Qt'ye widget'ın yeniden çizilmesi gerektiğini bildir
update();
}
void AnalogGauge::setRange(double min, double max) {
m_minValue = min;
m_maxValue = max;
update();
}
void AnalogGauge::paintEvent(QPaintEvent *event) {
Q_UNUSED(event);
QPainter painter(this);
// 1. Kenar yumuşatmayı (Antialiasing) aç: Pürüzsüz çizgiler ve yaylar için şarttır!
painter.setRenderHint(QPainter::Antialiasing);
// 2. Widget boyutuna göre merkezleme ve ölçekleme
int side = qMin(width(), height());
painter.setViewport((width() - side) / 2, (height() - side) / 2, side, side);
painter.setWindow(-100, -100, 200, 200); // Koordinat merkezini (0,0) tam ortaya alır
// 3. Kadran Arka Plan Çemberi
QRadialGradient bgGradient(0, 0, 100);
bgGradient.setColorAt(0.0, QColor(45, 52, 54));
bgGradient.setColorAt(1.0, QColor(29, 31, 33));
painter.setBrush(bgGradient);
painter.setPen(QPen(QColor(99, 110, 114), 4));
painter.drawEllipse(-90, -90, 180, 180);
// 4. Renkli Skala Yayı Çizimi (Yeşilden Kırmızıya)
QPen arcPen;
arcPen.setWidth(8);
arcPen.setCapStyle(Qt::FlatCap);
// Güvenli bölge (Yeşil)
arcPen.setColor(QColor(46, 204, 113));
painter.setPen(arcPen);
painter.drawArc(-75, -75, 150, 150, -45 * 16, -150 * 16);
// Tehlike bölgesi (Kırmızı)
arcPen.setColor(QColor(231, 76, 60));
painter.setPen(arcPen);
painter.drawArc(-75, -75, 150, 150, -195 * 16, -90 * 16);
// 5. Değer İbresi (İğne - Needle) Çizimi
// Mevcut değere göre açıyı hesaplayalım (225 derecelik yay alanı)
double ratio = (m_value - m_minValue) / (m_maxValue - m_minValue);
double angle = 135.0 + (ratio * 270.0);
painter.save(); // Koordinat durumunu yedekle
painter.rotate(angle); // İğne açısı kadar ekseni döndür
// İğne Geometrisi (İnce Poligon)
static const QPoint needle[3] = {
QPoint(-3, 0),
QPoint(3, 0),
QPoint(0, 70)
};
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(230, 126, 34)); // Turuncu ibre
painter.drawPolygon(needle, 3);
painter.restore(); // Ekseni orijinal haline getir
// 6. Merkez Göbek Pimi
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(236, 240, 241));
painter.drawEllipse(-8, -8, 16, 16);
// 7. Sayısal Değer Metni Yazımı
painter.setPen(QColor(245, 246, 250));
painter.setFont(QFont("Arial", 12, QFont::Bold));
painter.drawText(QRect(-50, 35, 100, 30), Qt::AlignCenter, QString::number(m_value, 'f', 1));
}
QPainter Performans İpuçları
setWindow()vesetViewport()Gücü: Çizim kodlarında piksel hesapları yapmak yerinesetWindow(-100, -100, 200, 200)tanımlayarak sanal bir koordinat uzayı kurun. Pencere boyutu değişse bile çiziminiz kalitesinden ve oranlarından hiçbir şey kaybetmeden otomatik ölçeklenir.save()verestore()Kullanımı: Dönüşüm matrislerini (rotate,translate,scale) kullanırken mutlakapainter.save()ile durumu kaydedip işlem bitincepainter.restore()ile sıfırlayın.- Pikselleri Önbelleğe Almak (QPixmap Cache): Her karede değişmeyen statik arka plan kadranlarını veya cetvel çizgilerini bir
QPixmapiçerisine bir kez çizippaintEventiçinde sadecedrawPixmapile basmak, CPU yükünü dramatik şekilde düşürür.
Signals ve Slots ile Entegrasyon
Hazırladığımız bu widget’ı QSerialPort rehberimizde geliştirdiğimiz seri port istemcisine tek satırla bağlayabiliriz:
// Arduino'dan gelen analog potansiyometre verisini göstergeye bağla
connect(serialWorker, &SerialWorker::temperatureReceived,
gaugeWidget, &AnalogGauge::setValue);
Özet
QPainter, Qt GUI dünyasının fırçasıdır. paintEvent içinde gradyanlar, yaylar ve koordinat dönüşümleri kullanarak endüstri standartlarında göstergeler, özel butonlar ve etkileyici veri grafikleri inşa edebilirsiniz.