2009年11月19日 星期四

如何使用 PyQwt 畫圖 (三)

上一篇文章的重點在介紹 QwtPlot 物件的設定,本篇文章將焦點放在 QwtPlotItem 物件的設定上。QwtPlotItem 物件是放在 QwtPlot 的畫布上,用來畫出數據或輔助數值判讀的繪圖元件,常用的 QwtPlotItem 物件有 QwtPlotCurve、QwtPlotMarker、以及 QwtPlotGrid 等。以下圖為例,紅色虛線為 QwtPlotCurve 物件,用來顯示 y 軸的參數如何隨 x 軸參數而變化;藍色菱形與右側的文字屬於 QwtPlotMarker 物件,用來標示特定值的資訊;格線則幫助我們判讀圖形。以下將依序介紹這些 QwtPlotItem 物件的使用方法。



1. QwtPlotCurve 物件:
QwtPlotCurve 物件的主要功能是以連續資料點所形成的曲線來顯示 y 軸參數與 x 軸參數間的關係。此物件以四個列舉常數成員 Lines、Steps、Sticks、以及 Dots 來分別代表線型、階梯型、長柱型、以及點型等四種曲線樣式,我們可以依照實際的需求選擇合適的曲線樣式來作圖。QwtPlotCurve 預設的曲線樣式為最常用的線型,要改用其它的樣式只要呼叫其物件方法 .setStyle(),將想用曲線樣式的列舉常數名稱做為引數代入即可。下圖展示函數 y = sin(x) 分別用以上四種樣式畫出來的結果:


在一般作圖中,我們可能需要改變曲線的外觀特微,例如指定曲線的顏色、寬度、或者畫出像是虛線(dashed line)、虛點線(dash-dotted line) 等不同形態的曲線,此時可以使用 Qt 的 QPen 物件來設定這些曲線外觀的參數,再以 QwtPlotCurve 的 .setPen() 方法將設定套用到 QwtPlotCurve 物件上。以下程式示範如何畫出線寬為 2 pt 的藍色 y = sin(x) 虛線:
#!/usr/bin/env python

import sys
import numpy as np
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.Qwt5 import *
        

class Ex03(QwtPlot):

    def __init__(self):
        QwtPlot.__init__(self)

        step = 0.1
        x = np.arange(0, 2*np.pi+step, step)
        y = np.sin(x)

        curve = QwtPlotCurve()
        curve.setData(x, y)
        curve.setStyle(curve.Lines)
        curve.setPen(QPen(Qt.blue, 2, Qt.DashLine))
        curve.attach(self)

        self.replot()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    frame = Ex03()
    frame.show()
    app.exec_()
在上面的程式碼,我們建立了一個 QwtPlot 的子類別:Ex03,來呈現我們想要畫出的圖。EX03 編碼的主要步驟如下:
  1. 設定 x 變數與 y 變數的值。
  2. 產生一個 QwtPlotCurve 的物件:curve。
  3. 利用 curve 的物件方法 .setData() 將 x 與 y 的值設定給 curve 物件。
  4. 以 .setStyle() 設定曲線的樣式為線型 (在實作上並不需要做此敘述,因為預設的樣式即為線型)。
  5. 然後以 .setPen() 方法套用 QPen 對曲線外觀的設定。
  6. 因為 Ex03 本身就是一個 QwtPlot 的子類別,因此我們用 curve 的物件方法 .attach() 將 curve 貼附在 Ex03 上。
執行的結果如下圖所示:


2. QwtPlotMarker:
當我們想在圖上的特定位置作上記號,或加些文字註解時,可以使用 QwtPlotMarker 來達成這項任務。以下程式碼示範如何在上面完成的 y = sin(x) 圖示中標出 sin(3π/4) 的位置,並且在一旁加上文字說明:
        xp = np.pi*3/4
        yp = np.sin(xp)
        symbol = QwtSymbol(QwtSymbol.Diamond, QBrush(Qt.red),
                           QPen(Qt.red), QSize(10, 10))
        mk = QwtPlotMarker()
        mk.setValue(xp, yp)
        mk.setSymbol(symbol)
        mk.attach(self)

        tx = QwtText(u" <--sin(3\u03c0/4)")
        tx.setColor(Qt.red)
        tx_mk = QwtPlotMarker()
        tx_mk.setValue(xp, yp)
        tx_mk.setLabel(tx)
        tx_mk.setLabelAlignment(Qt.AlignRight)
        tx_mk.attach(self)
在這段程式碼中,產生了兩個 QwtPlotMarker 物件:mk 與 tx_mk,分別用來製造一個紅色的菱形標誌與說明文字,並將它們放在指定的座標位置 (xp, yp) 上。我們先來看有關 mk 物件的設定程序:
  1. 首先我們必須產生一個 QwtSymbol 物件來做為標示的符號。在範例中我們的 QwtSymbol 物件名稱為 symbol,同時以引數 QwtSymbol.Diamond、QBrush(Qt.red) 與 QPen(Qt.red)、以及 QSize(10, 10) 初始化 symbol 的外形、表面與外框的顏色、與 symbol 大小。
  2. 建立 QwtPlotMarker 物件: mk。
  3. 使用物件方法 .setValue() 以及 .setSymbol() 設定標記的位置與套用的標誌外觀。
  4. 以 .attach() 物件方法將 mk 物件貼附在圖上。
將文字註記在圖上的編碼過程如下:
  1. 首先建立一個 QwtText 的文字物件:tx,並設定顯示的文字內容與外觀。
  2. 然後建立一個 QwtPlotMarker 物件:tx_mk,並使用其物件方法 .setValue() 以及 .setLabel() 來設定註記的位置與套用的文字內容。
  3. 使用 .setLabelAlignment() 物件方法選擇對齊文字的方式。
  4. 將 tx_mk 物件貼附在圖上。
因為這兩個 QwtPlotMarker 物件:mk 與 tx_mk,都用來顯示同一個座標位置 (xp, yp) 的資訊,所以在實作上可以合併成一個 QwtPlotMarker 物件。以下是將上面的程式碼加到第一段的程式碼後執行的結果:


3. QwtPlotGrid:
另外我們也常用 QwtPlotGrid 物件在圖上產生格線以協助圖形的判讀。預設上格線是畫在 x 軸與 y 軸的 major ticks 上,我們可以使用其物件方法 .enableX(bool) 及 .enableY(bool) 來開關 x 軸與 y 軸的格線;物件方法 .enableXMin(bool) 與 .enableYMin(bool) 則用來開關 x 軸與 y 軸 minor ticks 上的格線。格線的外觀則由 .setPen() 物件方法套用 QPen 物件對格線的設定。以下示範上圖加上格線後的結果以及需要加入的程式碼:


        gd = QwtPlotGrid()
        gd.setPen(QPen(Qt.black, 0, Qt.DotLine))
        gd.enableXMin(True)
        gd.attach(self)
在預設上 Qwt 不會自動清除先前畫過的圖,因此我們可以直接畫多條曲線在同一張圖上。如果希望每次畫圖時只有新的曲線出現,則可以叫用 QwtPlot 的物件方法 .clear() 將之前畫的圖與標記清除。

4. 曲線參考軸的設定:
在前一篇文章中,我們提到 QwtPlot 物件最多可以有四個座標軸:yLeft、yRight、xBottom、與 xTop。在預設上只出現 yLeft 與 xBottom 兩個座標軸,因此圖上的 QwtPlotItem 物件在預設上是參照到這兩個座標軸。如果我們想將兩個不同單位(或相同單位但值域相差很大)的 QwtPlotItem 物件放在同一張圖上,那麼我們可以開啟另外兩個座標軸:yRight 與 xTop,讓它們成為另外一個物件的參考軸。設定 QwtPlotItem 物件參考軸的方式是叫用 QwtPlotItem 的物件方法 .setAxis(xAxis, yAxis),因為上面介紹過的 QwtPlotCurve、QwtPlotMarker、以及 QwtPlotGrid 物件都是 QwtPlotItem 的子類別,所以這些物件都繼承了這個物件方法。下圖展示分別參照到 yLeft 軸的 y1 = sin(x) 曲線與參照到 yRight 軸的 y2 = exp(x) 曲線:


程式碼如下:
#!/usr/bin/env python

import sys
import numpy as np
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.Qwt5 import *
        

class Ex03_2(QwtPlot):

    def __init__(self):
        QwtPlot.__init__(self)
        step = 0.1
        x = np.arange(0, 2*np.pi+step, step)
        y1 = np.sin(x)
        y2 = np.exp(x)

        c1 = QwtPlotCurve()
        c1.setData(x, y1)
        c1.setPen(QPen(Qt.blue, 2, Qt.DashLine))
        c1.attach(self)

        #-- Enable the yRight axis.
        self.enableAxis(self.yRight, True)
        c2 = QwtPlotCurve()
        #-- Set the referred coordinates of c2.
        c2.setAxis(self.xBottom, self.yRight)
        c2.setData(x, y2)
        c2.setPen(QPen(Qt.red, 2, Qt.SolidLine))
        c2.attach(self)

        self.setAxisTitle(self.xBottom, "x")
        self.setAxisTitle(self.yLeft, "sin(x)")
        self.setAxisTitle(self.yRight, "exp(x)")

        y1_tx = QwtText("  y1 = sin(x)")
        y1_tx.setColor(Qt.blue)
        y1_mk = QwtPlotMarker()
        y1_mk.setValue(np.pi*3/4, np.sin(np.pi*3/4))
        y1_mk.setLabel(y1_tx)
        y1_mk.setLabelAlignment(Qt.AlignRight)
        y1_mk.attach(self)

        y2_tx = QwtText("y2 = exp(x)  ")
        y2_tx.setColor(Qt.red)
        y2_mk = QwtPlotMarker()
        #-- Set the referred coordinates of y2_mk.
        y2_mk.setAxis(self.xBottom, self.yRight)
        y2_mk.setValue(6, np.exp(6))
        y2_mk.setLabel(y2_tx)
        y2_mk.setLabelAlignment(Qt.AlignLeft)
        y2_mk.attach(self) 
        
        self.replot()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    frame = Ex03_2()
    frame.show()
    app.exec_()
有關 QwtPlotItem 的使用方式就介紹到此為止,其它更多相關的功能與設定可以到 Qwt 的首頁查看。

(發佈日期:2009/11/19)
(修改日期:2010/02/04)

沒有留言:

張貼留言