![見出し画像](https://assets.st-note.com/production/uploads/images/174156599/rectangle_large_type_2_0a89a9514077a7b2db8e8afe82f14814.jpeg?width=1200)
MATLAB でマルチウィンドウアプリを作る (1)
まえがき
MATLAB では、App Designer を使って簡単に GUI アプリを作ることができます。
今回は、複数のウィンドウを持つアプリの作り方を解説します。
続きがある予定です。たぶん。
もちろん公式ドキュメントにも説明があるのですが、最初は分かりにくいと思いますし、「ダイアログ ボックスでしかできない?」とか思ってしまいそうなので、順を追って説明していきます。
公式サンプルはコマンドウィンドウで以下を実行すれば開くことができます。
> openExample('matlab/AppsThatShareDataExample')
また Web アプリでの複数アプリウィンドウ作成はサポートされていないので、その場合はタブ等を使うことになります。
App Designer
簡単な例として以下のようなアプリを作ってみます。
・app1
1. 各 n1 点のデータを持つ n2 種類のデータセットのグラフを表示
2. ”Parameters” に n1, n2 を表示
3. ”Option” ボタンクリックで app2 起動
4. ウィンドウを閉じると app2 も閉じる
![](https://assets.st-note.com/img/1739263566-F1hz8GafNOuMjBwbHXvVe3o5.jpg?width=1200)
・app2
1. app1 の n1, n2 をスピナーの初期値として設定
2. スピナーで n1, n2 変更可能
3. 変更した n1, n2 を app1 の ”Parameters” にリアルタイム反映
4. "OK" ボタンクリックで app1 のグラフ更新
![](https://assets.st-note.com/img/1739263585-T7dOJPew6zivsD9IquyoAEcf.jpg?width=1200)
これを実現するには、基本的には
app1:
app2 構造体宣言
それを戻り値に必要な引数付けてapp2 呼び出し
app2:
app1 構造体宣言
startupFcn に引数追加して構造体と共に受取り
app1 の GUI 操作、app1 の public 関数呼び出し
とこれだけです。
複雑そうに思えるかもしれませんが、一度分かってしまえばシンプルです。
アプリは、デフォルトでは左上に MATLAB アイコン と"MATLAB App" が表示されます。
変更したい場合は、背景の UIFigure をクリック、コンポーネントブラウザー一番下の「識別子」で Name にアプリ名を入れ、 Icon でアイコンファイルを指定します。
以下の記事を参照してください。
それでは、実際に詳しく見ていきましょう。
・app1 設計ビュー
まずは「新規 → アプリ」で App Designer を立ち上げます。
![](https://assets.st-note.com/img/1739262689-et7RokyqQJMV5XsZrz1mp0FY.png)
左ペインのコンポーネントライブラリから、「座標軸」「ボタン」「テキストエリア」を適当なところにドロップして適当にサイズ変更等を行います。
![](https://assets.st-note.com/img/1739264236-xh4XDAs6VUb25RL3PpvtjdGB.jpg?width=1200)
「ラベル」で名前を変更できます。コンポーネント名も、ダブルクリックすれば変更でき、ソースコードに自動で反映されます。
ここでは、テキストエリアのラベルは "Parameters”、コンポーネント名も "Parameters” に変更しています。
同一アプリ内では自動更新されますが、他アプリから参照している場合は注意しましょう。
![](https://assets.st-note.com/img/1739264606-sYQ8H47lLq35UhAN9jikObRf.jpg?width=1200)
「テキストエリア」のラベルは、ラベルだけを選択すれば好きな場所に移動できます。
同様に、「ボタン」は Text を "Option" に変更しました。
![](https://assets.st-note.com/img/1739264918-G02RjPCt4kqvNKOlHWVTu96S.jpg?width=1200)
ここで一旦ファイルを保存しましょう。ファイル名がアプリ名になり、ソースコードも自動的に更新されます。
今回はそのまま app1 としています。
以降は、MATLAB 上で mlapp ファイルをダブルクリックすれば App Designer で開かれ、MATLAB コマンドウィンドウで > app1 と打つか、エクスプローラーでダブルクリックすればアプリが実行されます。
(エクスプローラーからの場合は MATLAB が立ち上がり、起動済みであれば即アプリが実行されます)
・app1 コードビュー
それではコードの方を書いていきます。
1.プロパティの追加
コードブラウザーのプロパティで、プライベートプロパティを追加します。
アプリ内で共通に使える変数等の宣言です。
![](https://assets.st-note.com/img/1739265347-k39xrRfuIKGN0qdjYy54QmPw.png?width=1200)
以下のように書き直します。
properties (Access = private)
propApp2 % for sub app
n1 = 10; % num of data points per dataset
n2 = 3; % num of datasets
end
2.関数の追加
次は app2 から呼ぶグラフ更新関数を定義します。
アプリ外からも呼べるように、パブリック関数として宣言します。
![](https://assets.st-note.com/img/1739265508-4zFxJn7avkMK068BwCNGoQXZ.png?width=1200)
以下のように書き直します。
methods (Access = public)
function updateplot(app, p1, p2)
plot(app.UIAxes, rand(p1,p2), '-o')
end
end
3.StartupFcn コールバックの追加
アプリ名を「右クリック → コールバック → StartupFcn コールバックの追加」で、アプリ起動時に実行される関数を追加します。
![](https://assets.st-note.com/img/1739265776-Jf3UlQ4MWqnwVhe57ND9RmHG.jpg)
以下のようにして、グラフの初期描画、”Parameters” テキストエリアへの表示を行います。
function startupFcn(app)
rng(0,"twister"); % for reproducibility
updateplot(app, app.n1, app.n2)
app.Parameters.Value = string(app.n1) + ", " + string(app.n2);
end
4.OptionButtonPushed コールバックの追加
Option ボタンをクリックしたときの動作を追加します。
ボタンを右クリック → コールバック → ButtonPushed コールバックの追加 でコールバックが追加されます。
![](https://assets.st-note.com/img/1739266763-Qy2HeGphdWEg4L7qUJrz9Kws.jpg?width=1200)
以下のコードを追加します。
function OptionButtonPushed(app, event)
app.propApp2 = app2(app, app.n1, app.n2);
end
app2 を引数付きで呼び出すと共に、app2 の構造体を保存します。
以降は、同じ操作でコールバックコードへの直接移動ができます。
![](https://assets.st-note.com/img/1739267023-ywci9utl23JIXEZLoB6NMDad.png)
5.UIFigureCloseRequest コールバックの追加
最後に、app1 を閉じたときに app2 が開いたままなら閉じたいのでその処理を入れます。
コールバック → ➕ で CloseRequestFcn を選択して追加します。
![](https://assets.st-note.com/img/1739267407-hPZdDLK8FzOSNGo54bCxRYQm.jpg?width=1200)
以下のように、app2 の delete を追加します。
function UIFigureCloseRequest(app, event)
delete(app.propApp2)
delete(app)
end
app 自体は app1 内で宣言されているので、app2 の状態にかかわらず(開いているか閉じているかチェックせずに)実行が可能です。
・app2 設計ビュー
それでは app2 の方に移ります。
app1 タブの右の + をクリックすれば、新たなアプリが作成されます。
そこに、「スピナー」x 2、「ボタン」をドロップし、右ペイン下の「コードオプション」に引数 "app1,n1,n2" を追加します。
StartupFcn コールバック が自動で追加されます。
これでアプリ起動時に引数を受け取ることができるようになります。
![](https://assets.st-note.com/img/1739267797-QeDFBkj9OyEWv5rAZGdsKT1w.jpg?width=1200)
ボタンの "Text" は "OK" に、スピナーの "Limits" は "1,100" に変更しています。
![](https://assets.st-note.com/img/1739268191-r5hBvKiAMefSPXtb3J1NOlc2.jpg?width=1200)
これらはコード中からも、例えば以下のように設定・変更可能です。
app.n1Spinner.Limits = [1,10];
・app2 コードビュー
1.プロパティの追加
先ほどと同様にプロパティを追加します。
properties (Access = private)
propApp1 % for main app
n1New, n2New
end
2.StartupFcn コールバックへの追加
コールバック自体は既に追加されているので、以下のように書き換えます。
function startupFcn(app, app1, n1, n2)
app.n1Spinner.Value = n1;
app.n2Spinner.Value = n2;
app.n1New = n1;
app.n2New = n2;
app.propApp1 = app1;
end
app1 から受け取った初期値を各スピナーと変数にセット、app1 の構造体も受け取ります。
これで、app2 から app1 のパブリック関数呼び出し、app1 の GUI 操作もできるようになります。
3.各コールバックの追加
各コンポーネントを右クリックしてコールバックを追加し、それぞれ以下のようにコードを追加します。
function n1SpinnerValueChanged(app, event)
app.n1New = app.n1Spinner.Value;
app.propApp1.Parameters.Value = string(app.n1New) + ", " + string(app.n2New);
end
function n2SpinnerValueChanged(app, event)
app.n2New = app.n2Spinner.Value;
app.propApp1.Parameters.Value = string(app.n1New) + ", " + string(app.n2New);
end
function OKButtonPushed(app, event)
updateplot(app.propApp1, app.n1New, app.n2New)
end
スピナーで数値を変更したら app1 のテキスト表示をリアルタイムで変更、"OK" でapp1 の表示更新関数を変更された引数付きで呼び出します。
アプリの動作として app1 のテキスト表示をリアルタイム変更する意味はありませんし、お互い GUI パラメーターも直接参照できますが、あくまでも例として。
また、プロパティ、関数・コールバック、入力引数の設定は、コードビューのエディターツールバーからも行えます。
![](https://assets.st-note.com/img/1739269520-2JZNit6Gdw0lCjFWp9SfVaMO.png)
こちらでも、「アプリの入力引数」をクリックすると自動で StartupFcn が追加されます。
・実行
app1 を実行します。
app2 からは引数不足で実行できません。
nargin で引数の数を参照できるので、app2 の startupFcn にチェックを入れておいてもよいでしょう。
コード
R2024b で作りました。
あとがき
書きたいことがいっぱいあるのですが、今回はこの辺で・・。