【MATLAB】多重視窗程式的資料交換

  • 802
  • 0
  • 2019-06-20

一般我們在開發UI程式的時候不會只有單一個畫面。
因此多視窗程式間的資料交換方法,多少還是要懂一些。

首先,夏恩必須說:
MATLAB 是可以寫視窗程式的!

很多人還停留在 MATLAB 就是拿來算算數學而已...
錯了錯了,MATLAB 能做的事情可多著呢!

先寫一個小程式

在一開始,夏恩先用 GUIDE 做一個簡單的小程式,叫做 main 檔:

由於本文並非 GUIDE 的入門教學,所以基礎用法請參閱官方網站。
Create a Simple App Using GUIDE

該程式的功能就是畫出 magic 函數,其輸入參數為 p,預設值為 5。
執行結果如下圖,使用者可以透過修改 p 值繪出不同的結果:

其中,OpeningFcn 函數的功能為設定預設值:

% 繪製 magic 函數,預設值 = 5
plot(handles.axes1, magic(5), 'LineWidth', 2)
axis(handles.axes1, 'tight')

set(handles.edit1, 'String', 5);

接著 set1 按鈕之 callback 函數的功能是讓使用者調整參數:

% 取回設定值,重新繪製圖形
N = str2double(handles.edit1.String);
plot(handles.axes1, magic(N), 'LineWidth', 2)
axis(handles.axes1, 'tight')

該程式執行後,即可透過 set1 按鈕來更換圖形。

多視窗程式

上述的說明如果沒問題的話,再來就是本文的主題了。

在多數應用程式中,若有許多參數要設定,通常會單獨拉出另一個視窗。
除了方便使用者之外,開發者也比較容易管理自己的程式。

現在夏恩要透過第二個按鈕,set2,單獨再開一個視窗來設定 p 值。

Step1: 先規劃一個新的視窗,parm_setting 檔:

Step2: 設定主視窗按鈕輸入參數

回到主視窗。

還記得剛才預留一個 set2 按鈕對吧!
我們在這個按鈕底下進行新視窗的呼叫,
呼叫時把主視窗的參數都傳送給新視窗,並準備承接新視窗之回傳值。

% call parm_setting
N = parm_setting(handles);

註:主視窗的按鈕函數

Step3: 在新視窗程式接收輸入值

若使用 GUIDE 自動建立的視窗程式,我們可以看到 OpeningFcn 最後有個 varargin。
這個 varargin 就是給使用者輸入外部參數用的。

​註:新視窗的 OpeningFcn 函數

在程式中,varargin 就像是一般的參數輸入;
若把程式包裝成執行檔的話,則可以在 cmd 中代入參數。

所以我們在這邊添加一行程式來處理 varargin。

handles.ps_edit1.String = varargin{1}.edit1.String;

另一方面,有時候我們會希望在開啟新視窗的同時,
主視窗必須停止所有動作,直到新視窗的工作結束再繼續執行。
若有以上的需求,則在 OpeningFcn 最後面加上 uiwait 命令即可。

(備註:不停止也是可以,只是停下來讓使用者輸入資料什麼的,是比較常見的案例。)

Step4: 撰寫新視窗的 ps_button1 按鈕函數

這邊要取得使用者輸入的「新值」,並回傳至主視窗。
在這一步驟,除了必要的程式碼之外,還要使用 uiresume 鬆開程式的鎖定。

handles.output = handles.ps_edit1.String;
guidata(hObject, handles);

% 解除畫面鎖定
uiresume(handles.figure1);

另外在函數回傳後,我們希望直接關閉新視窗,而這一步驟寫在 OutputFcn 內。

delete(hObject)

Step5: 更新主視窗畫面

在收到回傳值之後,最後就是把它更新到相關元件上。

需要更新的有兩個,
第一:圖面,繪製最新的 magic 圖形。
第二:p 值,把從新視窗中得到的值,更新到主視窗的 p 值上。

% 更新回傳值到主視窗的物件上
N = str2double(N);

plot(handles.axes1, magic(N), 'LineWidth', 2)
axis(handles.axes1, 'tight')

set(handles.edit1, 'String', N);

完成

立刻來看看實作的成果。

最後,再統整一次本文幾個重點:

1. 如何開啟新視窗,並傳入資料。

在 MATLAB 中是透過 varargin 來接收資料。
在剛才的範例中:N = parm_setting(handles);

即表示開啟 parm_setting 並傳入 handles。
如果我們改成:N = parm_setting(handles, A, B);

這樣的話,在新視窗的接收端則是:

varargin{1} = handles
varargin{2} = A
varargin{3} = B

就是不管怎樣,輸入值都只有一個 varargin 就對了。
輸入參數們都會以細胞陣列的方式進行傳送。

2. 如何從新視窗回傳資料。

回傳值在 OutputFcn 函數中設定,由 varargout 來控制。

在本範例中,varargout{1} = handles.output;
表示只有一個回傳值,讀者也可以自行設定多個回傳值,

varargout{1} = handles.output;
varargout{2} = something1;
varargout{3} = something2;

不限資料型態,自己知道自己在寫什麼就好。

3. 請善用 uiwait 與 uiresume 來調控不同視窗間的控制權。

小結

其實 MATLAB 除了使用 GUIDE 來構建 UI 程式之外,
最近幾年也推出新版的 UI 製作方式:appdesigner。

有別於 GUIDE 是用函數導向的構建法,
appdesigner 是架構在類別之上,以物件導向的方式來建立 UI 介面。

夏恩覺得 appdesigner 更直觀,更好用!但需要在 R2018b 之後才會比較有感,
在這之前幾個版本的 appdesigner 成熟度不足,使用上不容易。

藉由以上所分享的內容,希望有遇到類似困難的讀者都能夠成功排除障礙。
之後空閒時,夏恩再把 appdesigner 的多視窗交換資料的方法一併更新上來。

附錄

範例程式可於 github 下載:MutilWindow-example