PySide (Qt for Python) は、Qt(キュート)の Python バインディングで、GUI などを構築するためのクロスプラットフォームなライブラリです。Linux/X11, macOS および Microsoft Windows をサポートしています。配布ライセンスは LGPL で公開されています。
mplfinance のチャートを PySide6 のウィジェットに埋め込んだ時に、表示するチャートを動的に増やしたり減らしたりしたかったので、FigureCanvas インスタンスを変えずに、表示するチャートの数を増減するサンプルを作ってみました。
下記の OS 環境で動作確認をしています。
![]() |
RHEL 9.4 | x86_64 |
Python | 3.12.1 | |
PySide6 | 6.7.2 | |
mplfinance | 0.12.10b0 | |
yfinance | 0.2.41 |
サンプルを以下に示しました。サンプルでは、日経平均株価の過去3ヶ月の日足データを yfinance で取得しています。
qt_mpl_finance_3.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import mplfinance as mpf | |
import sys | |
import yfinance as yf | |
from matplotlib.backends.backend_qtagg import ( | |
FigureCanvasQTAgg as FigureCanvas, | |
NavigationToolbar2QT as NavigationToolbar, | |
) | |
from matplotlib.figure import Figure | |
from PySide6.QtCore import Qt, Signal | |
from PySide6.QtWidgets import ( | |
QApplication, | |
QCheckBox, | |
QMainWindow, | |
QToolBar, | |
) | |
class MyChart(FigureCanvas): | |
def __init__(self): | |
self.fig = Figure() | |
super().__init__(self.fig) | |
self.fig.subplots_adjust( | |
left=0.12, | |
right=0.87, | |
top=0.9, | |
bottom=0.05, | |
) | |
self.ax = dict() | |
def initAxes(self, ax, n: int): | |
if n > 1: | |
gs = self.fig.add_gridspec( | |
n, 1, | |
wspace=0.0, hspace=0.0, | |
height_ratios=[3 if i == 0 else 1 for i in range(n)] | |
) | |
for i, axis in enumerate(gs.subplots(sharex='col')): | |
ax[i] = axis | |
ax[i].grid() | |
else: | |
ax[0] = self.fig.add_subplot() | |
ax[0].grid() | |
def initChart(self, n: int): | |
self.removeAxes() | |
self.initAxes(self.ax, n) | |
def clearAxes(self): | |
axs = self.fig.axes | |
for ax in axs: | |
ax.cla() | |
def refreshDraw(self): | |
self.fig.canvas.draw() | |
def removeAxes(self): | |
axs = self.fig.axes | |
for ax in axs: | |
ax.remove() | |
self.ax = dict() | |
def setTitle(self, title: str): | |
self.ax[0].set_title(title) | |
class MyToolBar(QToolBar): | |
volumeCheckChanged = Signal() | |
def __init__(self): | |
super().__init__() | |
self.chk_volume = chk_volume = QCheckBox('Volume') | |
chk_volume.checkStateChanged.connect(self.volume_check_changed) | |
self.addWidget(chk_volume) | |
def isVolumeChecked(self) -> bool: | |
return self.chk_volume.isChecked() | |
def volume_check_changed(self): | |
self.volumeCheckChanged.emit() | |
class Example(QMainWindow): | |
def __init__(self): | |
super().__init__() | |
self.setWindowTitle('Dynamic charts') | |
self.toolbar = toolbar = MyToolBar() | |
toolbar.volumeCheckChanged.connect(self.draw) | |
self.addToolBar(Qt.ToolBarArea.TopToolBarArea, toolbar) | |
self.chart = chart = MyChart() | |
self.setCentralWidget(chart) | |
self.navigation = navigation = NavigationToolbar(chart) | |
self.addToolBar(Qt.ToolBarArea.BottomToolBarArea, navigation) | |
self.symbol = symbol = '^N225' | |
ticker = yf.Ticker(symbol) | |
self.df = ticker.history(period='3mo') | |
self.draw() | |
def draw(self): | |
if self.toolbar.isVolumeChecked(): | |
n = 2 | |
else: | |
n = 1 | |
self.chart.initChart(n) | |
param = dict( | |
data=self.df, | |
style='yahoo', | |
type='candle', | |
datetime_format='%m/%d', | |
xrotation=0, | |
ax=self.chart.ax[0], | |
) | |
if self.toolbar.isVolumeChecked(): | |
param['volume'] = self.chart.ax[1] | |
mpf.plot(**param) | |
self.chart.setTitle(self.symbol) | |
self.chart.refreshDraw() | |
self.navigation.update() | |
def main(): | |
app = QApplication(sys.argv) | |
ex = Example() | |
ex.show() | |
sys.exit(app.exec()) | |
if __name__ == '__main__': | |
main() |
qt_mpl_finance_3.py の実行例
ツールバーの Volume チャックボックスにチェックを入れると、出来高の棒グラフが下に追加されます。プロットは切り替える度に再描画しています。
qt_mpl_finance_3.py の実行例(ツールバーの Volume チャックボックスをチェック)
FigureCanvas クラスを継承した MyChart クラスで、軸数の設定や再描画の処理をしています。今回の用途では縦方向にチャートを増減させるだけですが、メインのチャートを常に大きく表示したかったので、処理を簡単にするために GridSpec で複数チャートの表示レイアウトを決めています。
class MyChart(FigureCanvas): def __init__( self ): self .fig = Figure() super ().__init__( self .fig) self .fig.subplots_adjust( left = 0.12 , right = 0.87 , top = 0.9 , bottom = 0.05 , ) self .ax = dict () def initAxes( self , ax, n: int ): if n > 1 : gs = self .fig.add_gridspec( n, 1 , wspace = 0.0 , hspace = 0.0 , height_ratios = [ 3 if i = = 0 else 1 for i in range (n)] ) for i, axis in enumerate (gs.subplots(sharex = 'col' )): ax[i] = axis ax[i].grid() else : ax[ 0 ] = self .fig.add_subplot() ax[ 0 ].grid() def initChart( self , n: int ): self .removeAxes() self .initAxes( self .ax, n) def clearAxes( self ): axs = self .fig.axes for ax in axs: ax.cla() def refreshDraw( self ): self .fig.canvas.draw() def removeAxes( self ): axs = self .fig.axes for ax in axs: ax.remove() self .ax = dict () def setTitle( self , title: str ): self .ax[ 0 ].set_title(title) |
MyChart の使い方は(mplfinance には依存せず)以下のような流れになります。
# MyChart クラスのインスタンスを配置 chart = MyChart() self .setCentralWidget(chart) # チャート用のナビゲーションツールバーを配置 navigation = NavigationToolbar(chart) self .addToolBar(navigation) ... ... # 表示するチャート数を n で指定して初期化 chart.initChart(n) # チャートを描画 # 軸は chart.ax[0], chart.ax[1], ..., chart.ax[n-1] で指定 ... ... # チャートの表示を更新 chart.refreshDraw() # ナビゲーションツールバーの更新 navigation.update() |
参考サイト

にほんブログ村
#オープンソース

0 件のコメント:
コメントを投稿