PyQtGraphは、Qt(PyQt・PySide)とNumPyの上に作られたPython製のグラフ描画ライブラリです。計測データを毎秒何十回も描き直す、マウスで拡大縮小しながら波形を見る、といったデスクトップアプリ向けの用途に適しています。再描画の速さはデータや描画設定によって異なるため、後述の条件付き比較を選定の参考にしてください。最新版は2025年11月16日公開の0.14.0で、Python 3.10以上とNumPy 1.25以上が必要です。この記事のコードは、Python 3.12・PyQtGraph 0.14.0・PySide6 6.12.0(2026年10月8日公開)で実行しています。2Dの例はoffscreen環境で実行確認しましたが、3Dの例はOpenGLコンテキストを作成できず、描画を確認できていません。
まとめ:PyQtGraphの要点と選びどころ
- 最新版は0.14.0(2025年11月16日)。対応はPython 3.10以上、NumPy 1.25以上、Qt 5.15または6.2以上。ライセンスはMIT
pip install pyqtgraphではQtバインディングが入らない。pip install pyqtgraph PySide6のように必ず一緒に入れる- グラフ1枚ならPlotWidget、複数のグラフを格子状に並べるならGraphicsLayoutWidget。Qt Designerでは QGraphicsView をどちらかに「昇格」して埋め込む
- 今回のoffscreen環境で10万点の乱数波形を描き直した時間は、各ライブラリの既定の描画設定でMatplotlibの約10分の1だった。リアルタイム表示ならPyQtGraph、論文や資料に貼る静止画ならMatplotlibを選ぶ
- 0.14.0でMultiPlotWidget・MultiPlotItem・MetaArray・canvasモジュールが削除された。古いサンプルを動かすときは差し替えが必要
PyQtGraphの特徴とMatplotlibとの使い分け
公式ドキュメントの説明では、PyQtGraphの目的は「データを表示する高速で対話的なグラフィックス」と「アプリを素早く作るための道具」の2つです。折れ線・散布図・画像の表示に加えて、範囲選択のウィジェット、画像の関心領域(ROI)切り出し、ドッキング、ParameterTree(設定項目のツリー表示)まで入っています。3D表示はOpenGLで行います。
Pythonだけで書かれているのに速いのは、数値の処理をNumPyの配列演算に任せ、画面への描画をQtのGraphicsViewフレームワークに任せているためです(公式サイトの説明)。データはPythonのリストではなくNumPy配列で渡すと、この仕組みを生かせます。
公式が示すMatplotlibとの違い
公式のIntroductionは、Matplotlibとの違いをこう書いています。プロット機能の網羅性と成熟度ではMatplotlibに及ばないが、動作ははるかに速い。Matplotlibは出版品質の図を作ることに重きを置き、PyQtGraphはデータ取得・解析のアプリケーションで使うことを想定している。画像の対話操作、ボリュームレンダリング、パラメータツリー、フローチャートはMatplotlibに無い機能として挙げられています。
再描画時間の実測比較
正規分布の乱数で作った5組の波形を順に描き直す時間を測りました。PyQtGraphは既定でアンチエイリアス無効、Matplotlibは既定の描画設定を使用しており、描画品質を揃えた比較ではありません。条件はIntel Core i9-9880H、PySide6 6.12.0、画面を持たないoffscreen描画、800×500ピクセル、50回の平均を2回測った範囲です。PyQtGraphは setData() 後に grab() で1枚描かせた時間、MatplotlibはAggバックエンドの fig.canvas.draw() の時間です。
| 点数 | PyQtGraph 0.14.0 | Matplotlib 3.11.2 |
|---|---|---|
| 1,000点 | 約11〜12ms | 約78〜80ms |
| 100,000点 | 約82ms | 約720〜785ms |
| 100,000点(間引き+表示範囲を2割に) | 約26〜28ms | ― |
Matplotlibを、変化する線だけを描き直すblitting方式にしても、10万点では1回あたり約710msかかりました。点数が多いと、線そのものを描く処理が重くなるためです。表の3行目は、後述の間引き設定を有効にし、X方向の表示範囲を全体の2割に絞った条件です。ただし、実際に30fpsを維持できるかは、データ処理や画面更新も含めて確認する必要があります。
PyQtGraph側は次のコードで測りました。Matplotlib側は、同じ5組の配列を set_ydata() で差し替えて fig.canvas.draw() を50回測っています。
import os, time
os.environ["QT_QPA_PLATFORM"] = "offscreen"
import numpy as np
import pyqtgraph as pg
app = pg.mkQApp()
w = pg.PlotWidget()
w.resize(800, 500)
w.show()
curve = w.plot(pen="y")
data = [np.random.normal(size=100_000) for _ in range(5)]
curve.setData(data[0])
w.grab() # 初回の準備は計測から外す
ts = []
for i in range(50):
t = time.perf_counter()
curve.setData(data[i % 5])
w.grab()
ts.append(time.perf_counter() - t)
print(f"{np.mean(ts) * 1000:.1f} ms")
PyQtGraphを選ばないほうがよい場面
論文・報告書に貼る図や、細かい書式を詰めた静止画を作るならMatplotlibを使ってください。PyQtGraph 0.14.0ではPNGなどの画像・SVG・CSV・HDF5へ書き出せますが、標準のエクスポートメニューでは印刷が無効化されており、PDF専用のエクスポーターもありません。凡例や目盛りの体裁を細かく整える設定もMatplotlibほど揃っていません。JupyterのノートブックやWebブラウザで見せたい場合も不向きです。PyQtGraphはQtのウィンドウを前提にしており、ノートブックのセル内にPyQtGraphのグラフを埋め込む場合は、jupyter_rfb という追加パッケージを使います。ブラウザで操作できるグラフが必要なら、PlotlyやBokehのほうが素直です。Matplotlib側の使い方と日本語表示はMatplotlibとは?使い方・インストール・日本語化を3.11対応で解説で扱っています。
PyQtGraph 0.14.0の対応環境とインストール
対応するPython・NumPy・Qtのバージョン
PyQtGraphはNumPyのNEP 29という方針に沿って対応範囲を決めています。NEP 29は、プロジェクトのリリース予定日を基準に、過去42か月のPythonと過去24か月のNumPyのマイナー版を少なくともサポートする方針で、それぞれ最低2世代・3世代という条件もあります。0.14.0での具体的な範囲は次のとおりです。
| 項目 | 0.14.0の要件 |
|---|---|
| Python | 3.10以上 |
| NumPy | 1.25以上 |
| Qt | 5.15、または6.2以上 |
| Qtバインディング | PyQt5・PyQt6・PySide2・PySide6 |
| 3D表示 | PyOpenGL(任意) |
| ライセンス | MIT |
Python 3.9の環境で pip install pyqtgraph を実行すると、pipは3.9でも入る0.13.7を黙って選びます(3.8なら0.13.3)。この記事のコードが動かない場合は、まず python -c "import pyqtgraph; print(pyqtgraph.__version__)" で版を確かめてください。
pip install pyqtgraphだけでは動かない理由
PyPIに登録されたpyqtgraph 0.14.0の依存パッケージは numpy>=1.25.0 と colorama だけで、Qtバインディングは含まれていません。PyQt5・PyQt6・PySide2・PySide6のどれを使うかを利用者に任せているためです。Qtを入れずに import pyqtgraph すると、次のエラーで止まります。
ImportError: PyQtGraph requires one of PyQt5, PyQt6, PySide2 or PySide6; none of these packages could be imported.
PySide6と一緒に入れるのが、いま始めるなら素直な選択です。PySide6はQtの開発元が提供するバインディングで、PyPI上のライセンス表記は「LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only」です。
python -m venv .venv
source .venv/bin/activate # Windowsは .venv\Scripts\activate
pip install pyqtgraph PySide6
# condaの場合
conda install -c conda-forge pyqtgraph pyside6
グラフ描画だけならWebEngineなどを含まない PySide6-Essentials でも動きます(この記事の検証はこちらで行いました)。ModuleNotFoundError: No module named 'pyqtgraph' が出る場合は、実行中のPythonがpyqtgraphを見つけられていません。未インストールやインストール失敗、pipと実行環境の不一致などを確認してください。python -m pip install pyqtgraph のように、実行に使うPythonからpipを呼んでください。仮想環境の作り方はuvとは?Pythonの環境構築・パッケージ管理・バージョン管理の使い方を徹底解説、pipとcondaの混在で起きる問題はpipとcondaの違いと使い分け|Anaconda・Miniconda・商用ライセンスまで解説にまとめています。
使うQtバインディングの選ばれ方とPYQTGRAPH_QT_LIB
複数のバインディングが入っている環境では、PyQtGraphは次の順で使うものを決めます(0.14.0の pyqtgraph/Qt/__init__.py の実装)。
- 環境変数
PYQTGRAPH_QT_LIBが設定されていれば、その名前のバインディングを使う - すでにimport済みのバインディングがあれば、PyQt6 → PySide6 → PyQt5 → PySide2 の順に探して最初のものを使う
- どれもimportされていなければ、同じ順でimportを試し、最初に成功したものを使う
PyQt6とPySide6が両方入っていると、何もしなければPyQt6が選ばれます。PySide6を使いたいなら、import pyqtgraph より前に from PySide6 import QtWidgets を書くか、環境変数で指定します。環境変数に、実行環境へインストールされていないバインディング名を指定すると、ModuleNotFoundError: Environment variable PYQTGRAPH_QT_LIB is set to 'PyQt6', but no module with this name was found. で止まります。どれが選ばれたかは pyqtgraph.Qt.VERSION_INFO で確認できます。
export PYQTGRAPH_QT_LIB=PySide6 # Windowsは set PYQTGRAPH_QT_LIB=PySide6
python -c "import pyqtgraph as pg; print(pg.Qt.VERSION_INFO)"
# PySide6 6.12.0 Qt 6.12.0
python -m pyqtgraph.examplesによるサンプル集の起動
インストールできたら、同梱のサンプル集を開いて動作を確かめます。次のどちらでも同じランチャーが起動します。
python -m pyqtgraph.examples
# Pythonのコードから開く場合
import pyqtgraph.examples
pyqtgraph.examples.run()
0.14.0のランチャーでは、左の一覧の最上位に「Basic Plotting」「ImageView」「Scrolling plots」「3D Graphics」「Flowcharts」など33項目が並びます。項目を選ぶと右側にそのサンプルのソースコードが表示され、ダブルクリックか「Run Example」ボタンで実行できます。表示中のコードは書き換えられ、書き換えると現れる「Run Edited Code」ボタンで改変版を試せます。「Qt Library」欄では実行に使うバインディングを選べますが、インストールされていないものは赤字で選べません。「3D Graphics」の項目はPyOpenGLが入っていないと動きません。
サンプルの実体は、インストール先の site-packages/pyqtgraph/examples フォルダにある.pyファイル(0.14.0では118本)です。自分のコードの土台にするなら、ランチャーで動きを見て、そのファイルをコピーして書き換えるのが早道です。
0.14.0で削除・追加されたAPI
0.13.7(2024年4月29日)から約1年半ぶりのリリースで、古いAPIが整理されました(0.14.0のリリースノート)。ネット上のサンプルは0.13系以前のものが多いので、次の変更に当たったら書き換えてください。
| 区分 | 内容 |
|---|---|
| 削除 | MultiPlotWidget・MultiPlotItem・MetaArray |
| 削除 | canvasモジュール |
| 追加 | 箱ひげ図のBoxplotItem |
| 追加 | 凡例のダブルクリック・サンプルクリックのシグナル |
| 強化 | PlotCurveItem・PColorMeshItemのOpenGL描画 |
| 変更 | OpenGL利用にenableExperimentalが不要に |
| 要件 | NumPy 1.25以上に引き上げ |
MultiPlotWidgetで複数の系列を縦に並べていたコードは、後述のGraphicsLayoutWidgetで addPlot() と nextRow() を繰り返す形に置き換えます。
PlotWidgetとGraphicsLayoutWidgetの使い分け
PlotWidgetは、グラフ1枚(PlotItem)を持つQtウィジェットです。GraphicsLayoutWidgetは、中に格子状のレイアウトを持ち、そこへ複数のPlotItemや画像表示用のViewBoxを並べられます。アプリの画面にグラフを1枚置くならPlotWidget、上下や左右に複数並べるならGraphicsLayoutWidget、と決めてかまいません。
pg.plotによる単一グラフ表示の最小構成
試しに表示するだけなら、pg.plot() 1行でウィンドウが開きます。戻り値はPlotWidgetです。
import numpy as np
import pyqtgraph as pg
x = np.linspace(0, 10, 1000)
plt = pg.plot(x, np.sin(x), pen="y", title="sin(x)")
pg.exec()
背景は既定で黒です。白くしたいときは、ウィジェットを作る前に pg.setConfigOption("background", "w") と pg.setConfigOption("foreground", "k") を実行します。線を滑らかにするアンチエイリアスも既定では無効なので、見た目を優先するなら pg.setConfigOptions(antialias=True) を加えます。
軸ラベル・表示範囲・線の種類の設定
グラフの体裁は、PlotWidgetのメソッドと pg.mkPen() で整えます。次の例は、白背景に軸ラベル・グリッド・凡例を付け、2本目を破線と丸印で描きます。
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore
pg.setConfigOption("background", "w")
pg.setConfigOption("foreground", "k")
app = pg.mkQApp("style")
w = pg.PlotWidget(title="温度の推移")
w.setLabel("left", "温度", units="°C")
w.setLabel("bottom", "経過時間", units="s")
w.showGrid(x=True, y=True, alpha=0.3)
w.setXRange(0, 60, padding=0)
w.setYRange(15, 35)
w.addLegend()
t = np.arange(61)
w.plot(t, 25 + 3 * np.sin(t / 8), name="センサーA",
pen=pg.mkPen("b", width=2))
w.plot(t, 22 + 2 * np.cos(t / 6), name="センサーB",
pen=pg.mkPen("r", width=2, style=QtCore.Qt.PenStyle.DashLine),
symbol="o", symbolSize=6, symbolBrush="r")
w.show()
pg.exec()
setXRange() は既定で範囲の両端に少し余白を足します(0〜60を指定すると、実際の表示は約-2.4〜62.4でした)。指定した値ちょうどで切りたいときは padding=0 を付けてください。setLabel() の units を指定すると、値の桁に応じて「k」「m」などの接頭辞が自動で付きます。
GraphicsLayoutWidgetによる複数グラフの配置
addPlot() で横に、nextRow() で次の行に進みます。setXLink() でX軸を連動させると、片方を拡大したときにもう片方も同じ範囲になります。
import numpy as np
import pyqtgraph as pg
app = pg.mkQApp("layout")
win = pg.GraphicsLayoutWidget()
p1 = win.addPlot(title="top")
p1.addLegend()
p1.plot(np.random.normal(size=100), pen="y", symbol="o", symbolSize=5, name="raw")
win.nextRow()
p2 = win.addPlot(title="bottom")
p2.plot(np.random.normal(size=100), pen=pg.mkPen("r", width=2))
p2.setXLink(p1)
p2.showGrid(x=True, y=True)
win.show()
pg.exec()
線の色と太さは pg.mkPen()、点の形は symbol(”o”・”s”・”t”・”+” など)と symbolSize で指定します。凡例に出すには、addLegend() を先に呼んでから name 付きで描きます。
Qt DesignerでGraphicsLayoutWidgetを埋め込む手順
Qt Designerの部品一覧にPyQtGraphのウィジェットはありません。代わりに、QGraphicsViewを置いてPyQtGraphのクラスへ「昇格(Promote)」させます。PySide6には pyside6-designer コマンドでQt Designerが同梱されています。
QGraphicsViewを昇格させる設定
- フォームにGraphics View(QGraphicsView)を配置する
- 右クリックして「Promote to…」(昇格先を指定)を選ぶ
- Promoted class nameに
GraphicsLayoutWidget(1枚だけならPlotWidget)、Header fileにpyqtgraphと入力する - 「Add」を押してから「Promote」を押す
保存した.uiファイルには、次の定義が入ります。Header fileはPythonのモジュール名として扱われるので、クラス名まで含めず pyqtgraph とだけ書きます。
<customwidget>
<class>GraphicsLayoutWidget</class>
<extends>QGraphicsView</extends>
<header>pyqtgraph</header>
</customwidget>
pyside6-uicによるUIファイルの変換と読み込み
pyside6-uic main.ui -o ui_main.py で変換すると、生成ファイルに from pyqtgraph import GraphicsLayoutWidget が入り、QGraphicsViewだった部品がGraphicsLayoutWidgetとして作られます。次のコードでは、Designerの「Main Window」フォームを使い、フォームのクラス名をMainWindow、昇格したGraphicsLayoutWidgetのobjectNameをgraphWidgetにします。さらにQPushButtonを配置し、そのobjectNameをpushButtonにしてmain.uiを保存・変換してください。
import numpy as np
from PySide6 import QtWidgets
import pyqtgraph as pg
from ui_main import Ui_MainWindow
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# Designerで名前を graphWidget にした部品
self.plot = self.ui.graphWidget.addPlot(title="designer")
self.curve = self.plot.plot(pen="g")
self.ui.pushButton.clicked.connect(self.redraw)
def redraw(self):
self.curve.setData(np.random.normal(size=200))
app = pg.mkQApp()
w = MainWindow()
w.show()
pg.exec()
変換せずに.uiを直接読むなら、QUiLoaderにクラスを登録してから読み込みます。登録を忘れるとdefaulting to base class 'QGraphicsView' という警告が出て、部品がただのQGraphicsViewとして作られます。当然 addPlot() は使えません。
from PySide6.QtCore import QFile
from PySide6.QtUiTools import QUiLoader
import pyqtgraph as pg
app = pg.mkQApp()
loader = QUiLoader()
loader.registerCustomWidget(pg.GraphicsLayoutWidget)
f = QFile("main.ui")
f.open(QFile.ReadOnly)
window = loader.load(f)
window.findChild(pg.GraphicsLayoutWidget, "graphWidget").addPlot()
f.close()
window.show()
pg.exec()
PySide6そのもののウィジェットやシグナル・スロットの書き方はPySide6とは?ライセンス・商用利用・インストール・使い方まで【6.12対応】で扱っています。
リアルタイム更新の実装と高速化
QTimerとsetDataで更新する基本形
公式のサンプルと同じく、QTimerで一定間隔ごとに関数を呼び、既存の曲線に setData() で新しい配列を渡します。plot() を毎回呼んで曲線を足していくと、描画対象が増え続けて遅くなります。
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore
app = pg.mkQApp("realtime")
win = pg.PlotWidget(title="realtime")
win.setClipToView(True)
win.setDownsampling(auto=True, mode="peak")
curve = win.plot(pen="c")
buf = np.zeros(2000)
def update():
buf[:-1] = buf[1:]
buf[-1] = np.random.normal()
curve.setData(buf, connect="all", skipFiniteCheck=True)
timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(16) # 約60回/秒
win.show()
pg.exec()
センサーやシリアル通信の受信でブロッキングAPIを使う場合は、別スレッドで動かしてください。GUIのスレッドで受信を待つと、その間は画面が固まります。受け取ったデータはキューやシグナルでGUIスレッドへ渡し、update() の中では描画だけを行います。QSerialPortのように受信をシグナルで知らせる非同期APIを使い、受信時の処理が短時間で終わるなら、GUIスレッドのままでも扱えます。計測機器との通信からデータ保存・画面までを含むデスクトップアプリを一から作る場合は、弊社のフルスクラッチ開発でもご相談を受けています。
大量データ描画の間引き・表示範囲・有限値検査設定
何万点もの波形を毎フレーム描くときは、データの条件に合わせて次の設定を組み合わせます。表示範囲の切り詰めはX値が昇順であること、自動間引きはX値が等間隔であることを前提にしています。前述の実測では、表示範囲を2割に絞った条件で、10万点の再描画が約82msから約26〜28msに縮みました。
| 設定 | 効果 |
|---|---|
| setDownsampling(auto=True, mode=”peak”) | 画面の幅に合わせて点を間引く |
| setClipToView(True) | 表示中のX範囲だけを描く |
| setData(…, skipFiniteCheck=True) | NaN・無限大の検査を省く |
| pg.setConfigOptions(useOpenGL=True) | 描画にOpenGLを使う(既定はFalse) |
mode だけを渡しても間引きは有効になりません(0.14.0で確認。10万点の曲線は10万点のまま描かれ、auto=True を足すと約7千点に減りました)。auto=True か、間引き率 ds を一緒に指定してください。PlotWidget・PlotItemでは引数名が mode、曲線ごとのPlotDataItemでは method です。後者の仕様はPlotDataItemのリファレンスにあります。modeは、”peak” なら区間ごとの最大値と最小値を残すので、ノイズのとがりが消えません。”mean” や “subsample” は波形の山を削ることがあるため、異常値を目で探す用途では “peak” を選びます。skipFiniteCheck=True は、X・Yの両方にNaNも正負の無限大も含まれないと保証できる場合に限って使ってください。既定のconnect=”auto”では検査設定が自動管理されるため、明示的に検査を省く場合はconnect=”all”も指定します。
ヒートマップとカラーマップ
2次元配列はImageItemで表示し、ColorBarItemでカラーマップと値の範囲を決めます。カラーバーの端をドラッグすると、表示する値の範囲をその場で変えられます。
import numpy as np
import pyqtgraph as pg
app = pg.mkQApp("heatmap")
win = pg.GraphicsLayoutWidget()
p = win.addPlot()
data = np.random.normal(size=(200, 100)) # 行=縦、列=横
img = pg.ImageItem(data, axisOrder="row-major")
p.addItem(img)
bar = pg.ColorBarItem(values=(-3, 3), colorMap=pg.colormap.get("viridis"))
bar.setImageItem(img, insert_in=p)
win.show()
pg.exec()
ImageItemは既定で配列の1次元目を横軸として扱います。NumPyの行列をそのまま見た目どおりに表示したいときは、axisOrder="row-major" を指定してください。指定しないと縦横が入れ替わって表示されます。ただし、row-majorは行と列の軸への対応を指定する設定で、上下方向は変えません。この例で配列の先頭行を画面上側に表示するには、p.getViewBox().invertY(True) も指定してください。
使えるカラーマップは pg.colormap.listMaps() で一覧できます。PyQtGraph 0.14.0では追加パッケージなしで71種類(viridis・inferno・CET-L9など)を一覧でき、Matplotlibが入っていれば listMaps("matplotlib") でMatplotlib側のカラーマップも一覧できます。Matplotlib 3.11.2の検証環境では182種類でした。画像を拡大縮小しながら断面を見たい場合は、ヒストグラムとROIが最初から付いたImageViewウィジェットのほうが手早く作れます。
pyqtgraph.openglでの3D描画
3D表示には、PyOpenGLを追加で入れ(pip install PyOpenGL)、pyqtgraph.opengl のGLViewWidgetに描画項目を足します。
import numpy as np
import pyqtgraph as pg
import pyqtgraph.opengl as gl
app = pg.mkQApp("3d")
view = gl.GLViewWidget()
view.setCameraPosition(distance=40)
view.addItem(gl.GLGridItem())
x = np.linspace(-10, 10, 100)
y = np.linspace(-10, 10, 100)
z = np.sin(np.hypot(*np.meshgrid(x, y, indexing="ij")))
view.addItem(gl.GLSurfacePlotItem(x=x, y=y, z=z, shader="heightColor"))
view.show()
pg.exec()
OpenGLの描画には、Qtが利用できるOpenGL実装と対応する表示環境が必要です。ソフトウェア実装やリモート表示もあるため、物理画面の有無だけでは成否を判断できません。本記事のoffscreen検証環境では、コードは例外なく終わっても「QOpenGLWidget: Failed to create context」が出て何も描かれません。3D表示を試すときは、デスクトップ上で実行してください。
よくある質問
PyQtGraphの最新バージョンはいくつですか?
2026年10月15日時点の最新版は0.14.0で、2025年11月16日に公開されました。Python 3.10以上とNumPy 1.25以上が必要です。新しい版が出ているかはPyPIのpyqtgraphのページで確認してください。
PyQtGraphはPyQtが無いと使えませんか?
PyQt5・PyQt6・PySide2・PySide6のどれか1つがあれば使えます。PyQtである必要はなく、PySide6でも動きます。ただし pip install pyqtgraph ではどれも入らないため、別途インストールが必要です。
PyQt6とPySide6はどちらを使えばよいですか?
PyQtGraphの使い方はどちらでも同じです。違いはライセンスで、PyPI上の表記はPyQt6がGPL-3.0-only、PySide6がLGPL-3.0を含む3択です。ソースを公開しないアプリでは、利用するQtモジュールのライセンスとLGPLの条件を確認したうえでPySide6を検討できます。PyQt6にも商用ライセンスという選択肢があります。
PyQtGraphのライセンスは商用利用できますか?
PyQtGraph本体はMITライセンスなので、商用のアプリに組み込めます。ただし一緒に使うQtバインディングには、それぞれのライセンスが別に適用されます。
PyQtGraphで棒グラフは描けますか?
描けます。BarGraphItemに棒の位置 x・高さ height・幅 width を渡し、PlotWidgetに addItem() で追加します。横軸を文字のラベルにするには getAxis("bottom").setTicks() を使います。
import numpy as np
import pyqtgraph as pg
app = pg.mkQApp("bar")
win = pg.PlotWidget(title="bar")
x = np.arange(5)
win.addItem(pg.BarGraphItem(x=x, height=[3, 5, 2, 6, 4], width=0.6, brush="c"))
win.getAxis("bottom").setTicks([[(i, s) for i, s in enumerate("ABCDE")]])
win.show()
pg.exec()