漸層(Gradient)

漸層是繪圖中很常見的一種功能,簡單說就是把幾種顏色混合在一起,讓它們自然地過渡,而不是馬上變成另一種顏色,漸層一般是用在圖案填充,所以通常是在QBrush裡面設置。 Qt提供了三種漸層:

  • 線性漸層(QLinearGradient)

Gradient


  • 輻射漸層(QRadialGradient)

Gradient


  • 角度漸層(QConicalGradient)

Gradient


widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPainter>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
protected:
    void paintEvent(QPaintEvent *);
};

#endif 

widget.cpp

#include "widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent)
{
    setWindowTitle(tr("繪圖"));
}

void Widget::paintEvent(QPaintEvent *){
    QPainter painter(this);
    QLinearGradient linearGradient(30, 30, 100, 100);
    linearGradient.setColorAt(0.2, Qt::white);  //設定漸層顏色
    linearGradient.setColorAt(0.6, Qt::yellow);
    linearGradient.setColorAt(1.0, Qt::black);
    painter.setBrush(QBrush(linearGradient));
    painter.drawRect(10, 10, 120, 120);

    QConicalGradient conicalGradient(200, 200, 0);
    conicalGradient.setColorAt(0.0, Qt::red);
    conicalGradient.setColorAt(1/6.0, Qt::yellow);
    conicalGradient.setColorAt(2/6.0, Qt::green);
    conicalGradient.setColorAt(3/6.0, Qt::cyan);
    conicalGradient.setColorAt(4/6.0, Qt::blue);
    conicalGradient.setColorAt(5/6.0, Qt::magenta);
    conicalGradient.setColorAt(1.0, Qt::red);

    QBrush brush(conicalGradient);
    painter.setPen(Qt::NoPen);
    painter.setBrush(brush);
    painter.drawEllipse(QPoint(200, 200), 80, 80);
}

main.cpp

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.resize(300,300);
    w.show();

    return a.exec();
}

Gradient

反鋸齒(Antialiasing)

  • 在顯示器上繪製非水平、非垂直的直線或多邊形邊界時,因為直線和多邊形的邊界是連續的,而像素則是由離散的點組成,所以或多或少會造成鋸齒狀外觀,用於減少這種效果的技術,稱為反鋸齒。
  • 包括Qt在內,很多系統的繪圖API裡面都內置了有關反鋸齒的設定,不過由於性能考量,一般預設是關閉的。
  • 這邊分別示範有無開啟反鋸齒的圖形,左側為預設樣式的橢圓,可看出邊緣的鋸齒外觀,右側開啟了反鋸齒設定。雖然反鋸齒的圖像品質較好,但是在對圖像品質要求不高,或在性能受限的環境下,比如嵌入式和手機環境,一般是不進行反鋸齒的。

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPainter>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
protected:
    void paintEvent(QPaintEvent *);
};

#endif 

widget.cpp

#include "widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent)
{
    setWindowTitle(tr("繪圖"));
}

void Widget::paintEvent(QPaintEvent *){
    QPainter painter(this);
    painter.setPen(QPen(Qt::black, 5, Qt::DashDotLine));
    painter.setBrush(Qt::green);
    painter.drawEllipse(20, 50, 200, 150);

    painter.setRenderHint(QPainter::Antialiasing, true);   //開啟反鋸齒
    painter.setPen(QPen(Qt::black, 5, Qt::DashDotLine));
    painter.setBrush(Qt::green);
    painter.drawEllipse(240, 50, 200, 150);
}

main.cpp

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.resize(460,250);
    w.show();

    return a.exec();
}

Antialiasing

調色盤(QPalette)

我們這邊使用QPalette改變元件顏色,分別改變窗口的前景、背景色,以及按鍵的顏色:


widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QHBoxLayout>
#include <QFrame>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
private:
    QFrame *mainFrame;
    QLabel *myLabel;
    QPushButton *myPushButton;
    QHBoxLayout *mainLayout;
};

#endif 

widget.cpp

#include "widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent)
{
    setWindowTitle(tr("按鍵組"));
    mainFrame = new QFrame(this);
    mainFrame->resize(200,100);
    mainFrame->setAutoFillBackground(true); //需加此行否則無法填充背景
    myLabel = new QLabel();
    myLabel->setText(tr("按鍵:"));
    myLabel->setAlignment(Qt::AlignCenter);
    myPushButton = new QPushButton();
    myPushButton->setText(tr("按鍵內容"));
    mainLayout = new QHBoxLayout(mainFrame);
    mainLayout->addWidget(myLabel);
    mainLayout->addWidget(myPushButton);

    QPalette p1 = mainFrame->palette();
    p1.setColor(QPalette::Window,Qt::yellow);    //變更背景色
    p1.setColor(QPalette::WindowText,Qt::red);   //變更前景色
    mainFrame->setPalette(p1);

    QPalette p2 = myPushButton->palette();
    p2.setColor(QPalette::ButtonText,Qt::blue); //變更按鍵前景色
    myPushButton->setPalette(p2);
    myPushButton->setStyleSheet("background:purple"); 
    //QPalette::Button無法生效,因此改用此方法變更按鍵背景色
}

main.cpp

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.resize(200,100);
    w.show();

    return a.exec();
}

QPalette


不同的元件使用QPalete不同的參數,大概有以下分類:

  • QPalete::Window,窗口元件的背景色。
  • QPalette::WindowText,窗口元件的前景色。
  • QPalette::Base,文本輸入元件(像QtextEdit,QLinedit)的背景色。
  • QPalette::Text,文本輸入元件的前景色。
  • QPalette::Button,按鈕元件的背景色。
  • QPalette::ButtonText,按鈕元件的前景色。

繪圖(QPixmap、QBitmap、QImage)

在處理影像資料上,Qt提供了QPixmap、QBitmap、QImage、QPicture等類別。

  • QPixmap繼承了QPaintDevice,可用QPainter於上進行繪圖,也可以載入BMP、GIF、JPG、JPEG、PNG等圖檔,並使用QPainter的drawPixmap()繪製在其它的繪圖裝置上。QPixmap與實際的底層顯示設備息息相關,所以在不同的平台上可能會有不一致的結果。
  • QBitmap是QPixmap的子類別,只有黑白兩色的圖像數據,提供單色圖像,佔用很少的存儲空間,適合於製作游標和筆刷物件。
  • QPixmap使用底層平台的繪製系統進行繪製,無法提供像素級別的操作,而QImage則是獨立於硬體的繪製系統,因此可以用setPixel()函式,對影像的像素進行設置,且QImage在不同系統上有相同的影像。

這邊分別示範QPixmap、QBitmap、QImage的使用。


widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPainter>
#include <QBitmap>
#include <QImage>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
protected:
    void paintEvent(QPaintEvent *);
};

#endif 

widget.cpp

#include "widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent)
{
    setWindowTitle(tr("繪圖"));
}

void Widget::paintEvent(QPaintEvent *){
    QPainter painter(this);
    QPixmap pixmap("icon.jpg");
    QBitmap bitmap("icon.jpg");
    painter.drawPixmap(10, 50, 100, 100, pixmap);   //將pixmap自行縮放,繪製於(x,y,width,height)範圍內
    painter.drawPixmap(140, 50, 100, 100, bitmap);

    QRectF rect(270,50,100,100);
    QImage image("icon.jpg");
    QRgb value = qRgb(0, 0, 0);
    image.setPixel(100, 100, value);  //對點(100,100)的顏色作變更
    painter.drawImage(rect, image);   //將image繪製於rect範圍內
}

main.cpp

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.resize(400,200);
    w.show();

    return a.exec();
}

QPixmap

繪圖(QPainter)

QPainter、QPaintEngine、QPaintDevice組成了Qt的繪圖系統,QPainter提供繪圖API,使用QPaintEngine作為介面,在QPaintDevice進行繪圖,所以像QWidget、QImage、QPicture、QPrinter這些QPaintDevice的子類別,只要建立QPainter就可在上面進行繪製,除非需要自定義一個設備,否則不需要關心QPaintEngine這個類別。

QPainter

通常會重新定義paintEvent()函式,當繪圖裝置(QPaintDevice)需要重繪時,就會發出QPaintEvent事件,並分派給paintEvent()來處理,例如元件出現、被覆蓋或又重現時。QPainter提供各種繪製圖形的API,從基本的線繪製、方塊、矩形、圓形、漸層到複雜的圖片等,這邊示範直線、矩形、橢圓、文字、圖形的繪製。


widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPainter>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
protected:
    void paintEvent(QPaintEvent *);
};

#endif 

widget.cpp

#include "widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent)
{
    setWindowTitle(tr("繪圖"));
}

void Widget::paintEvent(QPaintEvent *){
    QPainter painter(this);
    painter.drawLine(10, 10, 80, 60);        //指定x、y、width、height繪製線條
    painter.setPen(Qt::green);               //指定畫筆顏色
    painter.drawRect(100, 10, 50, 80);       //指定x、y、width、height繪製矩形
    painter.setPen(QPen(Qt::red, 5));        //指定畫筆顏色和粗細
    painter.setBrush(Qt::blue);              //指定筆刷顏色,可以想成物體的填充色
    painter.drawEllipse(10, 100, 200, 100);  //指定x、y、width、height的矩形,繪製橢圓於內

    painter.setFont(QFont("Arial", 30));
    painter.drawText(200, 10, 150, 80, Qt::AlignCenter, tr("哈囉! Qt"));

    painter.drawPixmap(220, 100, QPixmap("icon.jpg")); // 繪製圖片

    painter.setBrush(Qt::yellow);
    QPointF points[4] = {QPointF(10.0, 250.0), QPointF(5.0, 270.0), QPointF(50.0, 280.0), QPointF(70.0, 220.0)}; //給點逆時針畫出
    painter.drawConvexPolygon(points, 4);
} 

main.cpp

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.resize(400,300);
    w.show();

    return a.exec();
}

QPainter


  • QPainter依程式碼順序繪圖,假設位置有重疊,重疊處後者會覆蓋前者。
  • 當我們設置了QPainter參數,例如畫筆顏色,除非重新設置顏色,否則我們下次就是以此參數繪圖。

畫筆(QPen)、筆刷(QBrush)

Qt繪圖系統定義了畫筆和筆刷:

  • 畫筆QPen多用於繪製畫線或輪廓線,我們可定義樣式、連接方式和線末端的樣式。QBrush大多用於填充內部區域。
  • 筆刷QBrush多用於填充內部區域,可選擇樣式、顏色、漸層以及紋理等不同填充屬性,預設不進行任何填充(Qt::NoBrush)。

下面的圖示為各種QBrush填充樣式:

QBrush

QPen畫線樣式:

QPen


QPen筆帽樣式capStyle()定義了線的末端:

QPen


QPen連接樣式joinStyle()定義了兩條線如何連接起來:

QPen


widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPainter>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
protected:
    void paintEvent(QPaintEvent *);
};

#endif 

widget.cpp

#include "widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent)
{
    setWindowTitle(tr("繪圖"));
}

void Widget::paintEvent(QPaintEvent *){
    QPainter painter(this);
    QPen pen(Qt::green, 5, Qt::DashLine, Qt::RoundCap, Qt::RoundJoin);
    painter.setPen(pen);
    painter.drawLine(10, 10, 100, 100);

    painter.setBrush(QBrush(Qt::blue, Qt::Dense1Pattern));
    painter.drawRect(120, 120, 100, 100);
}

main.cpp

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.resize(250,250);
    w.show();

    return a.exec();
}

QPen


可以把QPen的屬性拆開個別賦值,例如:

QPen pen(Qt::green, 5, Qt::DashLine, Qt::RoundCap, Qt::RoundJoin);

可以寫成:

QPen pen;
pen.setColor(Qt:: green);
pen.setWidth(5);
pen.setStyle(Qt::DashLine);
pen.setCapStyle(Qt::RoundCap);
pen.setJoinStyle(Qt::RoundJoin);