カテゴリー

Plotly全般

【plotly&ボタン】plotlyのupdatemenusに2回押し対応のbuttonsを追加

2021年4月14日

こんな人にオススメ

plotlyでボタンを追加して動作を割り当てることはできたけど、もう一回押した時に何か動作を割り当てることってできる?

できるならボタンの数が減ってよりスタイリッシュになるのに...

ということで、今回はplotlyのボタン(updatemenus)でボタンを連続して2回押した時に、動作を割り当てる方法について解説する。以下のボタン追加の方法がわかっていれば簡単。

【plotly&ボタン】plotlyのupdatemenusに2回押し対応のbuttonsを追加

続きを見る

1引数コードを追加するだけで2回目のボタンの設定が可能。もう一回のボタンを作ることで、ボタンでの動作を元に戻したり追加の動作を設定可能。

基本的なボタンの作成方法とか考え方は上の記事を参照、本記事ではargs2を使った2回目のボタン作成方法とちょっとしたボタンの応用方法について解説する。

python環境は以下。

  • Python 3.10.1
  • plotly 5.4.0
  • plotly-orca 3.4.2

なお、新しく立ち上げたサイト「Pro天パ」ではリライト(書き直し)した記事を公開している。併せてご覧いただきたい。

運営者のメガネです。YouTubeTwitterInstagram、自己紹介はこちら、お問い合わせはこちらから。

運営者メガネ

作成したコード全文

下準備(import)

import sys
import numpy as np
import matplotlib.cm as cm
import plotly
import plotly.graph_objects as go
import plotly.io as pio

sys.path.append('../../')
import plotly_layout_template as template

まずは下準備としてのimport関連。今回はシンプルな配列からできるグラフを使用するのでplotlygoだけ。あとはグラフ保存用のpio.

goで散布図を作成する方法、orcaplotlyのグラフを保存する方法については以下。

【plotly&go.Scatter】plotlyの散布図グラフの描き方

続きを見る

plotlyでグラフを静止画として保存する際に出るポップアップ
【plotly&orca】plotlyで静止画保存(orca)

続きを見る

(復習)グラフにボタンを追加する


まずはシンプルにグラフにボタンを作成する方法について復習する。1ボタンごとにdictで作成し、全ボタンを一括でlistに入れて用意する。

それをグラフのどの位置に設置するのかなどを指定してから、go.Layoutの引数updatemenusに入れることでボタンを作成できた。

ここでのグラフは全プロットを表示するallボタン、y1だけ表示するy1ボタン、そしてy2だけ表示するy2ボタンの合計3ボタンを作成。

import plotly.graph_objects as go
import plotly.io as pio

# (復習)2つのグラフを切り替えられるボタンを作成

x = (0, 1, 2, 3, 4)
y1 = (0, 1, 4, 3, 2)
y2 = (1, 3, 2, 4, 0)

# 配列の要素にプロット内容を格納
plot = [
    go.Scatter(x=x, y=y1, name='y1'),
    go.Scatter(x=x, y=y2, name='y2'),
]

# 2プロット表示用のボタン作成
button_all = dict(
    label='all',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[True, True]),  # y1, y2の両方を表示
        dict(title='all plots'),  # グラフタイトル
    ]
)
# y1表示用のボタン作成
button1 = dict(
    label='y1',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[True, False]),  # y1のみ表示
        dict(title='y1 plot'),  # グラフタイトル
    ]
)
# y2表示用のボタン作成
button2 = dict(
    label='y2',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[False, True]),  # y2のみ表示
        dict(title='y2 plot'),  # グラフタイトル
    ]
)

# ボタンをひとまとめにする
buttons = [button_all, button1, button2]

# レイアウトに設置するためのupdatemenus作成
updatemenus = [
    dict(
        active=0,  # 初期グラフで見た目上、推されるボタン
        type='buttons',  # ボタンのタイプはボタンに
        direction='right',  # ボタンは右向きに配置
        x=0.5, y=1.01,  # ボタンの位置
        xanchor='center', yanchor='bottom',  # ボタンの位置の基準
        buttons=buttons,  # ここに設定したボタン情報を入れる
    )
]

# レイアウトの作成
layout = go.Layout(
    title='title',  # グラフタイトル
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20,  # ホバーのフォントサイズ
    updatemenus=updatemenus,  # ボタンを設置
)

# グラフの表示
fig = go.Figure(data=plot, layout=layout)
fig.show()

# グラフの保存
prefix = 'plotly-buttons-args2'
save_name = f"{prefix}_simple_buttons"
pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca'
pio.write_html(fig, f"{save_name}.html")
pio.write_image(fig, f"{save_name}.png")

args2で2回押した時のボタン操作を割り当て


ということで、本題のボタンを2回押した時のグラフを作成。と言っても作成方法はシンプルで、argsを使った階層と同じ階層に引数args2を入れたらいい。

具体的には以下。visibleなどの構造もargs2と全く同じでいい。内容は変えないと意味がないけど、作り方は同じ。

# args2を適用し全プロット表示と全プロット非表示のボタンを追加
button_all = dict(
    label='all or nothing',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    # aegsを1回押した時のボタンの挙動
    args=[
        dict(visible=[False, False]),  # y1, y2の両方を表示
        dict(title='No plots'),  # グラフタイトル
    ],
    # aegs2がもう一回押した時のボタンの挙動
    args2=[
        dict(visible=[True, True]),  # y1, y2の両方を表示
        dict(title='all plots'),  # グラフタイトル
    ]
)

ここで作成したボタンは以下の2種類。要するに1回押すと全プロットが非表示になり、再度押すと全プロットが表示されるということ。

  1. args(1回目): 全プロットを非表示
  2. args2(2回目): 全プロットを表示

ただ、気をつけないといけない点がある。それが変数updatemenusの引数active。これを-1にしておかないと初めからボタンが1回押された状態で初期グラフが作成される。

なので、1回目のボタン押下がargs2の処理となり、再度押した時が実質3回目の処理=1回目の処理となる。なので作成する際には注意が必要。

残りの部分については復習で作成したグラフと大差ない。visibleTrueだと表示でFalseだと非表示。そしてtitleでグラフタイトルを変更している。

import plotly.graph_objects as go
import plotly.io as pio

# 2つのプロットを2種類の機能を持つボタンで切り替える

x = (0, 1, 2, 3, 4)
y1 = (0, 1, 4, 3, 2)
y2 = (1, 3, 2, 4, 0)

# 配列の要素にプロット内容を格納
plot = [
    go.Scatter(x=x, y=y1, name='y1'),
    go.Scatter(x=x, y=y2, name='y2'),
]

# args2を適用し全プロット表示と全プロット非表示のボタンを追加
button_all = dict(
    label='all or nothing',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    # aegsを1回押した時のボタンの挙動
    args=[
        dict(visible=[False, False]),  # y1, y2の両方を表示
        dict(title='No plots'),  # グラフタイトル
    ],
    # aegs2がもう一回押した時のボタンの挙動
    args2=[
        dict(visible=[True, True]),  # y1, y2の両方を表示
        dict(title='all plots'),  # グラフタイトル
    ]
)

# ボタンをlistにまとめる
buttons = [button_all]

# レイアウトに設置するためのupdatemenus作成
updatemenus = [
    dict(
        # active=0にしていると、args2のボタンが1回目の処理扱いになる
        active=-1,  # 初期グラフで見た目上、推されるボタン
        type='buttons',  # ボタンのタイプはボタンに
        direction='right',  # ボタンは右向きに配置
        x=0.5, y=1.01,  # ボタンの位置
        xanchor='center', yanchor='bottom',  # ボタンの位置の基準
        buttons=buttons,  # ここに設定したボタン情報を入れる
    )
]

# レイアウトの作成
layout = go.Layout(
    title='title',  # グラフタイトル
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20,  # ホバーのフォントサイズ
    updatemenus=updatemenus,  # ボタンを設置
)

# グラフの表示
fig = go.Figure(data=plot, layout=layout)
fig.show()

# グラフの保存
prefix = 'plotly-buttons-args2'
save_name = f"{prefix}_double_buttons"
pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca'
pio.write_html(fig, f"{save_name}.html")
pio.write_image(fig, f"{save_name}.png")

args3はない

import plotly.graph_objects as go
import plotly.io as pio

x = (0, 1, 2, 3, 4)
y1 = (0, 1, 4, 3, 2)
y2 = (1, 3, 2, 4, 0)

# 配列の要素にプロット内容を格納
plot = [
    go.Scatter(x=x, y=y1, name='y1'),
    go.Scatter(x=x, y=y2, name='y2'),
]

# args3はない
button_all = dict(
    label='all or nothing',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    # aegsを1回押した時のボタンの挙動
    args=[
        dict(visible=[True, True]),  # y1, y2の両方を表示
        dict(title='all plots'),  # グラフタイトル
    ],
    # aegs2がもう一回押した時のボタンの挙動
    args2=[
        dict(visible=[False, False]),  # y1, y2の両方を表示
        dict(title='No plots'),  # グラフタイトル
    ],
    # aegs2がもう一回押した時のボタンの挙動
    args3=[
        dict(visible=[True, False]),  # y1, y2の両方を表示
        dict(title='1 plots'),  # グラフタイトル
    ]
)

args2があるならargs3があるのかというと実はそうではない。args3を指定すると以下のようにエラーが出る。あくまでもargsは2まで。

シンボルサイズや線の種類を変更したボタンを作成


最後にちょっとした応用として以下の機能を持つボタンを作成。

  • シンボルサイズを変更
  • 線の種類を変更
  • 線の太さ、マーカーの種類とサイズを変更

最終的なコードは以下。次より各ボタンについて解説する。

import plotly.graph_objects as go
import plotly.io as pio

# シンボルサイズや線の種類を変更したボタンを作成

x = (0, 1, 2, 3, 4)
y1 = (0, 1, 4, 3, 2)
y2 = (1, 3, 2, 4, 0)

# 配列の要素にプロット内容を格納
plot = [
    go.Scatter(x=x, y=y1, name='y1'),
    go.Scatter(x=x, y=y2, name='y2'),
]

# args2を適用しマーカーのシンボルのサイズを変更するボタンを作成
button_size = dict(
    label='symbol size',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    # aegsを1回押した時のボタンの挙動
    args=[
        {'marker.size': 30},  # プロットのマーカーのサイズを変更
        dict(title='symbol size: 30'),  # グラフタイトル
    ],
    # aegs2がもう一回押した時のボタンの挙動
    args2=[
        {'marker.size': 6},  # プロットのマーカーのサイズをデフォルトに
        dict(title='symbol size: 6'),  # グラフタイトル
    ]
)

# args2を適用し線の種類を変更するボタンを作成
button_dash = dict(
    label='line dash',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    # aegsを1回押した時のボタンの挙動
    args=[
        {'line.dash': 'dash'},  # プロット線の種類を破線に
        dict(title='line dash: dash'),  # グラフタイトル
    ],
    # aegs2がもう一回押した時のボタンの挙動
    args2=[
        {'line.dash': 'solid'},  # プロット線の種類を実線に
        dict(title='line dash: solid'),  # グラフタイトル
    ]
)

# args2を適用し線の太さ・マーカーの種類とサイズを変更するボタンを作成
button_width_symbol = dict(
    label='line width & symbol',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    # aegsを1回押した時のボタンの挙動
    args=[
        {
            'line.width': 0.5,  # 線の太さ
            'marker.symbol': 'star',  # マーカーの種類
            'marker.size': 10  # マーカーのサイズ
        },
        # グラフタイトルと横軸ラベル
        dict(title='line width: 0.5', xaxis=dict(title='X (1st button)')),
    ],
    # aegs2がもう一回押した時のボタンの挙動
    args2=[
        {
            'line.width': 2,  # 線の太さ
            'marker.symbol': 'square',  # マーカーの種類
            'marker.size': 10  # マーカーのサイズ
        },
        # グラフタイトルと縦軸ラベル
        dict(title='line width: 2', yaxis=dict(title='Y (2nd button)')),
    ]
)

# ボタンをlistにまとめる
buttons = [button_size, button_dash, button_width_symbol]

# レイアウトに設置するためのupdatemenus作成
updatemenus = [
    dict(
        # active=0にしていると、args2のボタンが1回目の処理扱いになる
        active=-1,  # 初期グラフで見た目上、推されるボタン
        type='buttons',  # ボタンのタイプはボタンに
        direction='right',  # ボタンは右向きに配置
        x=0.5, y=1.01,  # ボタンの位置
        xanchor='center', yanchor='bottom',  # ボタンの位置の基準
        buttons=buttons,  # ここに設定したボタン情報を入れる
    )
]

# レイアウトの作成
layout = go.Layout(
    title='title',  # グラフタイトル
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20,  # ホバーのフォントサイズ
    updatemenus=updatemenus,  # ボタンを設置
)

# グラフの表示
fig = go.Figure(data=plot, layout=layout)
fig.show()

# グラフの保存
prefix = 'plotly-buttons-args2'
save_name = f"{prefix}_double_buttons_ex"
pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca'
pio.write_html(fig, f"{save_name}.html")
pio.write_image(fig, f"{save_name}.png")

マーカーサイズを変更

# args2を適用しマーカーのシンボルのサイズを変更するボタンを作成
button_size = dict(
    label='symbol size',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    # aegsを1回押した時のボタンの挙動
    args=[
        {'marker.size': 30},  # プロットのマーカーのサイズを変更
        dict(title='symbol size: 30'),  # グラフタイトル
    ],
    # aegs2がもう一回押した時のボタンの挙動
    args2=[
        {'marker.size': 6},  # プロットのマーカーのサイズをデフォルトに
        dict(title='symbol size: 6'),  # グラフタイトル
    ]
)

go.Scatterのマーカーサイズを変更するにはgo.Scatterの引数markerの引数sizeを変更する必要がある。なのでargs, args2でアクセスする必要がある。

しかし、dict(marker_size=30)のように指定しても反映されないので、{'marker.size': 30}のように書く必要がある。

線の種類を変更

# args2を適用し線の種類を変更するボタンを作成
button_dash = dict(
    label='line dash',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    # aegsを1回押した時のボタンの挙動
    args=[
        {'line.dash': 'dash'},  # プロット線の種類を破線に
        dict(title='line dash: dash'),  # グラフタイトル
    ],
    # aegs2がもう一回押した時のボタンの挙動
    args2=[
        {'line.dash': 'solid'},  # プロット線の種類を実線に
        dict(title='line dash: solid'),  # グラフタイトル
    ]
)

線の種類を変更する際も考え方は同じ。線の種類はgo.Scatterの引数lineの引数dashで変更可能。{'line.dash': 'dash'}が破線で{'line.dash': 'solid'}が実線に対応する。

その他にもdot(点線)やdashdot(一点鎖線)などがある。詳しくは以下参照。

【plotly&size, width】Scatterのサイズやlineの太さ一覧表を作成

続きを見る

線の太さ、マーカーの種類とサイズを変更

# args2を適用し線の太さ・マーカーの種類とサイズを変更するボタンを作成
button_width_symbol = dict(
    label='line width & symbol',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    # aegsを1回押した時のボタンの挙動
    args=[
        {
            'line.width': 0.5,  # 線の太さ
            'marker.symbol': 'star',  # マーカーの種類
            'marker.size': 15  # マーカーのサイズ
        },
        # グラフタイトルと横軸ラベル
        dict(title='line width: 0.5', xaxis=dict(title='X (1st button)')),
    ],
    # aegs2がもう一回押した時のボタンの挙動
    args2=[
        {
            'line.width': 2,  # 線の太さ
            'marker.symbol': 'square',  # マーカーの種類
            'marker.size': 10  # マーカーのサイズ
        },
        # グラフタイトルと縦軸ラベル
        dict(title='line width: 2', yaxis=dict(title='Y (2nd button)')),
    ]
)

最後は複数の変更を適用する方法。線の太さはline.width、マーカーの種類はmarker.symbol、そしてマーカーのサイズはmarker.sizeで変更することが可能。

また、args, args2の2つ目のdictはレイアウト関連の情報を入れるんだけど、今回はプラスして横軸・縦軸のラベルも変更するように指定してみた。

これでボタンを押すと軸ラベルが追加されるようになる。

ボタンを押して変わるもの、変わらないもの

色んなボタンを押しまくると変更される箇所と変更されない箇所がある。これはボタンで指定した処理による。

というのも、line.widthは3つ目のボタン、line.dashは2つ目のボタンでしか適用されない。なので線の太さと線の種類はこれらのボタンを押さない限り変更されない。

一方で、marker.sizeは1つ目と3つ目のボタンで指定したので、ボタンを押す度に処理が行われて、内容が上書きされていく。

2回目のボタンで自由度がさらに増す

今回はplotyのボタン機能で2回ボタンを押した時に動作を設定する方法を解説した。1つの機能のボタンだけでもかなり実用的だが、そこに追加で機能を付加できるのは便利。

変更して元に戻すと言った使い方をすればボタンが1つで済むし、隠し機能として娯楽目的で作成することもできる。

plotlyはインタラクティブなライブラリなので、今回の記事をきっかけに様々なグラフを作成していただくと幸いだ。

関連記事

【plotly&go.Scatter】plotlyの散布図グラフの描き方

続きを見る

自作したplotlyのマーカーのシンボル一覧
【plotly&マーカー】plotlyのマーカーのシンボル

続きを見る

【plotly&config】グラフのツールバーを編集する

続きを見る

【plotly&ボタン】plotlyのupdatemenusにbuttonsを追加

続きを見る

【plotly&スライダー】plotlyのslidersにスライダーを追加

続きを見る

【plotly&Scattergl】大量のデータをplotlyで軽くグラフ化

続きを見る

plotlyでグラフを静止画として保存する際に出るポップアップ
【plotly&orca】plotlyで静止画保存(orca)

続きを見る

【plotly&table+scatter】subplotsで表と散布図を同時にグラフ化

続きを見る

【plotly&ボタン】グラフに複数種のボタンを追加

続きを見る

 

ガジェット

2023/9/18

【デスクツアー2022下半期】モノは少なく、でも効率的に Desk Updating #0

今回はガジェットブロガーなのにデスク環境を構築していない執筆者の ...

ライフハック

2023/9/16

【Audible vs YouTube Premium】耳で聴く音声学習コンテンツを比較

ワイヤレスイヤホンが普及し耳で学習することへのハードルが格段に下 ...

完全ワイヤレスイヤホン(TWS)

2023/9/18

【SENNHEISER MOMENTUM True Wireless 3レビュー】全てが整ったイヤホン

今回は高音質・高機能なSENNHEISERのフラグシップ完全ワイヤレスイヤホン「SENNH ...

ライフハック

2023/3/11

【YouTube Premiumとは】メリットしかないから全員入れ

今回はYouTube Premiumを実際に使ってみてどうなのか、どんなメリット/デメリット ...

マウス

2023/8/17

【Logicool MX ERGOレビュー】疲れない作業効率重視トラックボールマウス

こんな人におすすめ トラックボールマウスの王道Logicool MX ERGOが気になるけどऩ ...

ベストバイ

2023/9/18

【ベストバイ2022】今年買って良かったモノのトップ10

2022年ベストバイ この1年を振り返って執筆者は何を買ったのか。ガジェッ& ...

スマホ

2023/1/15

【楽天モバイル×povo2.0の併用】月1,000円の保険付きデュアルSIM運用

こんな人におすすめ 楽天モバイルとpovo2.0のデュアルSIM運用って実際のとこ ...

マウス

2023/9/16

【Logicool MX ERGO vs MX Master 3】ERGOをメインにした決定的な理由

こんな疑問・お悩みを持っている人におすすめ 執筆者はLogicoolのハイエンӠ ...

macOSアプリケーション

2022/9/30

【Chrome拡張機能】便利で効率的に作業できるおすすめの拡張機能を18個紹介する

こんな人におすすめ Chromeの拡張機能を入れたいけど、調べても同じような ...

macOSアプリケーション

2023/5/3

【Automator活用術】Macで生産性を上げる作業の自動化術

今回はMacに標準でインストールされているアプリ「Automator」を使ってできる ...

Pythonを学びたいけど独学できる時間なんてない人へのすゝめ

執筆者は大学の研究室・大学院にて独学でPythonを習得した。

でも社会人になったら独学で行うには時間も体力もなくて大変だ。

時間がない社会人だからこそプロの教えを乞うのが効率的。

ここでは色んなタイプに合ったプログラミングスクールの紹介をする。

  • この記事を書いた人

メガネ

Webエンジニア駆け出し。独学のPythonで天文学系の大学院を修了。常時金欠のガジェット好きでM2 Pro MacBook Pro(30万円) x Galaxy S22 Ultra(17万円)使いの狂人。自己紹介と半生→変わって楽しいの繰り返しレビュー依頼など→お問い合わせ運営者情報、TwitterX@m_ten_pa、 YouTube@megatenpa、 Threads@megatenpa

-Plotly全般
-, , ,