03.08 想編寫一個有界面的工程速算小程序,我是該學MATLAB GUI呢,還是從0開始學Python呢?

祺禎亦寶


其實兩者皆可,如果考慮到跨平臺、可移植以及靈活性,建議還是Python,第一個回答詳細介紹了Matlab GUI,這裡我重點介紹一下Python GUI,感興趣的朋友可以嘗試一下:

簡單輕便tkinter

這是Python自帶的一個標準GUI開發庫,完美支持3大操作平臺,基於Tk接口,簡便靈活,非常易於操作,基本組件和容器可以完全滿足日常開發,如果你只是開發一個簡單的桌面程序(類似於速算小程序),只關注具體功能實現,不考慮界面佈局以及美觀程度,可以使用一下這個模塊,非常不錯,也非常易於學習和掌握,唯一不足就是中文參考資料太少:

優秀開源wxpython

這是一個非常優秀的Python GUI開發庫,免費、開源、跨平臺,基於wxwidgets接口,可以輕鬆運行在各大主流操作平臺,允許程序員快速、方便的創建功能齊全的GUI用戶界面,相比較輕量級的tkinter,wxpython提供的組件和容器更多,也更豐富,如果你需要開發一個比較專業的桌面程序,需要一定的界面佈局和美觀程度,可以使用一下這個模塊,非常不錯:


專業強大pyqt

這是一個非常專業、功能強大的Python GUI開發庫,基於qt接口封裝而來,藉助於qt強大的可視化功能,Python也可以輕鬆創建各種桌面應用,可用的組件和容器更多,也可直接拖拽佈局,如果你熟悉qt編程,那麼pyqt學習起來會非常容易,開發效率也更高,如果你需要開發一個功能強大的專業軟件,可以學習一下這個模塊,非常不錯,相關參考資料非常多:

當然,除了以上3個不錯的Python GUI開發庫,還有許多其他庫,像kivy、easygui等也都非常不錯,只要你有一定Python基礎,熟悉一下相關文檔和示例,很快就能掌握的,網上也有相關教程和資料,介紹的非常詳細,感興趣的話,可以搜一下,希望以上分享的內容能對你有所幫助吧,也歡迎大家評論、留言進行補充。


小小猿愛嘻嘻


在MATLAB的命令窗口(Command Window)中運行guide命令,來打開GUIDE界面,如下:


然後,選擇空模板(Blang

GUI),點擊OK,即可打開GUIDE的設計界面,如下:


Editor),打開菜單編輯器,如下:


在Menu

Bar中新建一個菜單項,名字為“文件”,其他設置請看下圖:


在“文件”菜單下添加菜單項:“打開”,“保存”,“退出”。見下圖:


如果需要在菜單項“退出”上面添加一個分割線的話,選中“Separator

above this item”就行了。

保存我的界面為pjimage.fig.

保存完畢之後,會自動打開

pjimage.m

文件,而我們所有的程序都是要寫在這個M文件裡面的。在編程中,我們的每一個鼠標動作都對應一個Callback函數。那麼我們的菜單項也是如此的。

在界面上,單擊鼠標右鍵選擇“Property

Inspector”,即可打開屬性窗口。當我們點擊不同的控件時,其對應的屬性都會在這裡顯示,我們可以進行修改。最主要的屬性莫過於Tag屬性和String屬性。

設置當前Figure窗口的Tag屬性為:figure_pjimage,窗口的標題(Name屬性)為:圖像處理實例。如下:


然後,點擊工具欄的保存按鈕。之後,點擊工具欄的運行按鈕(Run

Figure)。注意,工具欄的圖標都會有提示的,像運行按鈕的提示就是Run Figure.

我們會看到如下的界面:


那說明,我們保存的

.fig

文件的目錄不是當前目錄,但是沒關係啊,我們只要點擊“Change

Directory”來改變當前目錄。當然,如果你想把當前目錄添加到MATLAB路徑也可以,那就點擊“Add to

Path”就OK了。我在這裡推薦點擊“Change

Directory”,因為沒有什麼太大必要把其添加到MATLAB路徑中,一般是工具箱需要添加或者我們的函數或程序寫完了,而在MATLAB的命令窗口找不到我們的函數的時候,我們可以將函數或程序所在的目錄添加到MATLAB路徑。

總之吧,點那個按鈕,要看個人的愛好了。不管點擊兩個按鈕的那一個按鈕,都會正確的運行程序的。

我們的程序運行時的樣子,是這樣的:


文件下面的菜單項和快捷鍵我們都能看到,但是我們沒有寫程序,所以就算點也沒有什麼響應。還有如果不想設置快捷鍵,可以在Menu

Editor中設置,只要把其選擇為Ctrl+none就行了,如下:


這樣的話,保存項就沒有了快捷鍵了。我們可以通過上面的按鈕“View”來查看該菜單項的響應函數,也就是Callback函數。也可以在

pjimage.m

中看,比如保存的Tag屬性是m_file_save,那麼它對應的Callback函數的名字就是m_file_save_Callback。依次類推了。

下面我們來寫打開菜單項的函數,要打開一個圖片,當然要用打開對話框了。在界面編程中,打開對話框的函數是uigetfile.

關於它的詳細的說明用help uigetfile命令查看。下面是打開菜單的響應函數:

function

m_file_open_Callback(hObject, eventdata, handles)

[filename, pathname] =

uigetfile( ...

{'*.bmp;*.jpg;*.png;*.jpeg', 'Image Files (*.bmp,

*.jpg, *.png, *.jpeg)'; ...

'*.*', 'All Files

(*.*)'}, ...

'Pick an

image');

保存

.m

文件,並運行程序。點擊“文件”下的“打開”,會打開如下的打開對話框:


選擇一個文件之後,程序中的filename就是你選擇的文件的文件名,pathname就是該文件所在的目錄的路徑。比如:filename

=

5.jpg

,pathname =C:\\Documents and Settings\\Administrator\\My

Documents\\。

那麼獲得路徑之後,我們要怎麼樣才能讀入和顯示一個圖片呢?讀入圖片可以用imread函數,而顯示可以在一個座標軸上。那麼我們需要在界面上畫上一個座標軸,為了對比,我們畫兩個座標軸,一個顯示處理前,一個顯示處理後的。並且將處理前的座標軸的Tag屬性改為axes_src,處理後的座標軸的Tag屬性為axes_dst。更改之後,保存。如下:


然後在m_file_open_Callback程序原來的基礎上,再添加如下的程序:

axes(handles.axes_src);%用axes命令設定當前操作的座標軸是axes_src

fpath=[pathname

filename];%將文件名和目錄名組合成一個完整的路徑

imshow(imread(fpath));%用imread讀入圖片,並用imshow在axes_src上顯示

運行程序,通過“打開”菜單項,打開一個圖片。效果如下:


那麼如何來保存一副圖片?用imwrite命令。但imwrite命令的第一個參數就是你讀入的圖片數據,也就是imread的返回值。這樣的話,我們就要將m_file_open_Callback中的程序做一點小小的改動。將最後一句(imshow(imread(fpath))),更改為兩句,如下:img_class="lazy" data-original=imread(fpath);imshow(img_src);

不僅如此,我們的保存菜單的Callback函數,如何去獲得打開菜單的Callback函數下的img_src變量呢?這裡就要將img_src來作為一個共享的數據。許多界面編程的朋友,喜歡用global聲明。我個人不喜歡這樣用,因為有更好的方法。那就是用setappdata和getappdata兩個函數。我們可以為界面上面的任何一個具有Tag屬性的空間添加應用程序數據。當然我比較喜歡將這些共享的應用程序數據統一添加到Figure窗口上,因為這樣容易記,如果一個控件一個,感覺不容易記。

你在

.m

文件中會發現除了各個菜單項的Callback函數以外,還有兩個函數:pjimage_OpeningFcn和pjimage_OutputFcn.而pjimage_OpeningFcn就相當於界面的初始化函數,而pjimage_OutputFcn則是界面的輸出函數,也就是當你不運行fig,而調用

.m

文件時的返回值。

所以,我們要在pjimage_OpeningFcn中添加如下的程序,來共享這個img_src矩陣。代碼如下:

setappdata(handles.figure_pjimage,’img_src’,0);

然後,在m_file_open_Callback函數的最後寫上如下程序:

setappdata(handles.figure_pjimage,’img_src’,img_src);

那麼,我們在m_file_save_Callback函數中就可以像這樣的來提取img_src,如下:

img_class="lazy" data-original=getappdata(handles.figure_pjimage,’img_src’);

那麼保存的時候,自然會用到保存對話框了。要用保存對話框,就要用到uiputfile函數了,具體的請用help

uiputfile查看。

那麼,保存菜單項下的程序(m_file_save_Callback),可以這樣寫:

[filename,

pathname] = uiputfile({'*.bmp','BMP files';'*.jpg;','JPG files'}, 'Pick an

Image');

if isequal(filename,0) || isequal(pathname,0)


return;%如果點了“取消”

else

fpath=fullfile(pathname,

filename);%獲得全路徑的另一種方法

end

img_class="lazy" data-original=getappdata(handles.figure_pjimage,'img_src');%取得打開圖片的數據

imwrite(img_src,fpath);%保存圖片

下面是退出菜單項的程序的。要退出界面,只要用close函數就行了,但是通常都會有提示的。比如你如果進行了處理圖片,而又沒有保存處理後的圖片,那麼在關閉的時候就應該給出提示,詢問是否進行保存。不過,在這裡,我們先不做這個工作,等後面有需要的時候再寫吧。因此,這裡的退出菜單項的程序就是一句,如下:

close(handles.figure_pjimage);

其實,用delete函數也是可以的,就是:delete(handles.figure_pjimage);看你的心情了。

但是運行程序的時候,你會發現,當你打開圖片的時候,如果點“取消”按鈕,那麼在MATLAB的命令窗口會彈出錯誤,那是因為我們沒有處理取消的情況。下面我們來處理下這個問題,只要把m_file_open_Callback下面的程序更改為如下程序即可:

[filename,

pathname] = uigetfile( ...

{'*.bmp;*.jpg;*.png;*.jpeg', 'Image Files

(*.bmp, *.jpg, *.png, *.jpeg)'; ...

'*.*', 'All Files

(*.*)'}, ...

'Pick an image');

if isequal(filename,0) ||

isequal(pathname,0),


return;

end

axes(handles.axes_src);

fpath=[pathname

filename];

img_class="lazy" data-original=imread(fpath);

imshow(img_src);

setappdata(handles.figure_pjimage,'img_src',img_src);

下面我們來做一個圖像二值化的一個圖像處理。用上面的方法添加一個“圖像處理”菜單,如下:


在其下面添加一個“圖像二值化”的菜單項,如下:


然後,點擊“OK”關閉菜單編輯器,並保存整個界面。如果我們的

.m

文件中沒有對應的Callback時,我們可以點擊上圖中的“View”按鈕來生成一個Callback函數。圖像二值化,有一個閾值的設置,那麼我們可以新建一個界面,在這個界面上放一個滑動條來設置圖像二值化的閾值。同時,有一個文本,顯示當前滑動條的值。那麼我們新建一個空白界面,在它上面畫一個Static

Text和Slider控件,然後用工具欄的對齊工具(Align

Objects),來對其這兩個空間。如下:


然後,將這個界面保存為

im2bw_args.fig

。整個設計如下:


你可以設置Static

Text的FontSize屬性為10,這樣字體會更大一點。設置Static

Text的Tag屬性為txt_display,設置滾動條的Tag屬性為slider_val。為了能夠在滾動條滾動時,Static

Text顯示滾動條的值,需要在滾動條的Callback中寫下如下程序,你可以在滾動條上點擊右鍵,選擇“View

Callbacks”下的“Callback”直接進入滾動條的Callback函數(slider_val_Callback)。

val=get(hObject,'Value');

set(handles.txt_display,'String',num2str(val));

保存,運行程序,就可以滑動滾動條,而Static

Text就會顯示相應的值。在figure上雙擊打開figure(有方塊的底層窗口)的屬性窗口,將其Tag屬性設置為“figure_im2bw”,將其Name屬性設置為“設置圖像二值化閾值”。然後,保存界面。運行時,如下:


那麼,我們想的是,當滑動條滑動時,將二值化的圖像顯示在

pjimage.fig

中的axes_dst座標軸上的。那麼怎麼辦呢?首先,要做的是,當點擊

pjimage.fig

菜單“圖像處理”下的“圖像二值化”的時候,會打開

im2bw_args.fig

。這個時候就是我們要調用

im2bw_args.m

的時候了。當我們調用它的時候,會返回一個句柄,而這個句柄就是指向打開的

im2bw_args.fig

的。關於更詳細的,你可以參看

im2bw_args.m

文件的最前面的註釋,其中有這樣寫:

%

H = IM2BW_ARGS returns the handle to a new IM2BW_ARGS or the handle to

% the

existing

singleton*.

那就說明,我們可以如上的方式打開

im2bw_args.fig

。所以在“圖像二值化”的Callback函數(m_image_2bw_Callback)下,寫上如下的程序:

h=im2bw_args;

然後,保存pjimage.fig.還有就是,最好將

im2bw_args.fig

pjimage.fig

保存在一個目錄下面。然後,運行

pjimage.fig

,可以看到,當點擊“圖像二值化”的時候會打開im2bw_args.fig,同時滑動條滑動時也會顯示響應的值。

下面來說說如何在滑動條滑動時,將滑動後的二值化圖像顯示到pjimage的axes_dst座標軸中。

首先,我們要獲得pjimage的figure的句柄,這個可以通過findobj函數來完成,之後將返回值用guihandles來轉換成一個句柄。之後,就可以用這個轉化後的句柄來引用

pjimage.fig

中的任何一個控件了。所以,我們在

im2bw_args.fig

下的滑動條的Callback函數中添加如下函數:

h_pjimage=getappdata(handles.figure_im2bw,'h_pjimage');

axes(h_pjimage.axes_dst);

img_class="lazy" data-original=getappdata(h_pjimage.figure_pjimage,'img_src');

bw=im2bw(img_src,val);

imshow(bw);

然後,在im2bw_args_OpeningFcn中添加:

h_pjimage=findobj('Tag','figure_pjimage');

h_pjimage=guihandles(h_pjimage);

setappdata(handles.figure_im2bw,'h_pjimage',h_pjimage);

然後,保存,運行。效果如下:


但是,如果在我們沒有打開圖片的情況下,要是點擊了“圖像二值化”會出現什麼問題呢?可以看到顯示的圖像是全黑的,完全沒有意義。所以,我們可以在沒有點擊“打開”菜單項的時候,使“圖像處理”菜單不可用。

那麼在

pjimage.m

的OpeningFcn中,添加如下程序:

set(handles.m_image,'Enable','off');

在“打開”菜單項的Callback函數的最後,添加如下程序:

set(handles.m_image,'Enable','on');

這樣的話,只要你不點“打開”,就不能用“圖像處理”菜單中的命令,效果如下:


點擊“打開”之後,就能使用了。

下面,我們來說說前面的問題,就是詢問是否保存圖片的問題。首先,我們要設置兩個標誌:一個是圖片是否被處理過了,二是圖片是否被保存了。那麼我們在pjimage_OpeningFcn中,添加如下的兩個應用程序數據。

setappdata(handles.figure_pjimage,'bSave',false);

setappdata(handles.figure_pjimage,'bChanged',false);

然後在“圖像二值化”菜單項的Callback函數中,改變bChanged的值為true,即添加如下程序:

setappdata(handles.figure_pjimage,'bChanged',true);

由於我們要保存的是座標軸axes_dst中的圖像,而我們“文件”下的“保存”,實質上保存的是座標軸axes_src中的圖像,那怎麼辦呢?只好再添加一個“保存”菜單項了。這次,我們在座標軸axes_dst中添加右鍵菜單。

打開工具欄的菜單編輯器,選擇Context

Menu(上下文菜單),如下:


然後,新建一個Context

Menu,其Tag屬性為:axes_dst_menu,如下:


然後為其添加菜單項:“保存”,其Tag屬性為axes_dst_menu_save.如上圖。然後,在座標軸axes_dst上右鍵,選擇“Property

Inspector”。將該座標軸的UIContextMenu屬性更改為axes_dst_menu.

如下圖:

然後,保存,運行。在axes_dst上點右鍵就能看到“保存”菜單了。下面來寫其函數。

[filename, pathname] =

uiputfile({'*.bmp','BMP files';'*.jpg;','JPG files'}, 'Pick an Image');

if

isequal(filename,0) || isequal(pathname,0)

return;

else


fpath=fullfile(pathname,

filename);

end

img_dst=getimage(handles.axes_dst);

imwrite(img_dst,fpath);

setappdata(handles.figure_pjimage,’bSave’,true);

但是你會發現,沒有讀入圖片之前,在axes_dst點右鍵是有菜單的,一旦二值化之後,再次點右鍵就沒有菜單了。

但是,當我們把右鍵菜單axes_dst_menu,添加到figure窗口(在沒有控件的地方,雙擊,即可打開figure的屬性窗口)的UIContextMenu的時候,就不會出現上面的問題,而且一切運行正常。因為,當你添加到axes_dst之後,一旦座標軸的內容改變,就會將右鍵菜單附加到父對象上。因此,如果一定需要在座標軸上顯示右鍵菜單,就要通過程序創建了。如何創建,咱們先不說,先說說把座標軸axes_dst保存完畢,退出程序的時候的處理。

將原來的m_file_exit_Callback更改為如下程序:

bChanged=getappdata(handles.figure_pjimage,'bChanged');%獲得是否更改

bSave=getappdata(handles.figure_pjimage,'bSave');%獲得是否保存

if

bChanged==true && bSave==false,%更改了,而沒保存時


btnName=questdlg('您已經更改了圖片,但沒有保存。要保存嗎?','提示','保存','不保存','保存');%用提問對話框


switch btnName,

case '保存', %執行axes_dst_menu_save_Callback的功能

feval(@axes_dst_menu_save_Callback,handles.axes_dst_menu_save,eventdata,handles);


case '不保存',%什麼也不做


end

end

h=findobj('Tag','figure_im2bw');%查找是否打開設置圖像二值化參數窗口

if

~isempty(h),%找到的話,則關閉


close(h);

end

close(findobj('Tag','figure_pjimage'));%關閉主窗口

下面來為程序添加一個工具欄,單擊工具欄上那個的Toolbar

Editor,打開如下:


選擇“Predefined

Tools”下的Open,點擊“Add”。再次選擇“Save”,點擊“Add”。並將Open按鈕的Tag屬性更改為tbl_open,Save按鈕的Tag屬性更改為tbl_save,如下:


點“View”,來找到Open按鈕的Callback,在它的下面來調用菜單中的打開菜單項的Callback,需要在Open按鈕的Callback下寫下如下程序:

feval(@m_file_open_Callback,handles.m_file_open,eventdata,handles);

用同樣的方法,找到Save按鈕的Callback,並在它的下面寫上保存程序,但是,我們要判斷一下是不是第一次保存,如果是,則用保存對話框;如果不是,我們直接保存在第一次保存的路徑中就可以了。那麼,我們還是需要設置幾個應用程序數據的,第一個就是記錄是否是第一次保存,第二個是記錄第一次保存的路徑。這樣的話,我們在pjimage_OpeningFcn中添加如下的代碼:

setappdata(handles.figure_pjimage,'fstSave',true);

setappdata(handles.figure_pjimage,'fstPath',0);

然後,在Save按鈕的Callback下,寫下如下的程序:

fstSave=getappdata(handles.figure_pjimage,'fstSave');

if(fstSave==true)


[filename, pathname] = uiputfile({'*.bmp','BMP files';'*.jpg;','JPG files'},

'Pick an Image');

if isequal(filename,0) ||

isequal(pathname,0)

return;

else


fpath=fullfile(pathname, filename);

end


img_dst=getimage(handles.axes_dst);

imwrite(img_dst,fpath);


setappdata(handles.figure_pjimage,'fstPath',fpath);


setappdata(handles.figure_pjimage,'bSave',true);


setappdata(handles.figure_pjimage,'fstSave',false);

else


img_dst=getimage(handles.axes_dst);


fpath=getappdata(handles.figure_pjimage,'fstPath');


imwrite(img_dst,fpath);

end

並且,我們還需要在沒有打開圖片之前的“文件”下的“保存”和工具欄的“Save”按鈕都不可用,只有點擊“文件”下的“打開”或工具欄下的“打開”的時候,它們才可用。那麼需要在pjimage_OpeningFcn中添加如下代碼:

set(handles.tbl_save,'Enable','off');

set(handles.m_file_save,'Enable','off');

並且在m_file_open_Callback下,添加如下代碼:

set(handles.tbl_save,'Enable','on');

set(handles.m_file_save,'Enable','on');

這樣一個小程序,算是完成了。

如果您能按照上面的步驟做完這個小小的程序的話,那麼界面編程中的問題你已經掌握的差不多了。


用戶6255694147972


MATLAB GUI基本上只能在研究的時候用用。如果你要工程速算,那麼建議你用用別人的,別從頭開始寫。

如果你想要藉此機會去學一些寫程序,那麼Python也不是一個特別好的選擇,應為你要做界面,python做界面並不強大,這是一個服務端腳本語言。也許從頭開始學Java更適合你。

當然你也可以做一個網絡應用,比如自己搭建一個網頁來完成這個任務,它的優點就是隨時隨地都能用,不需要安裝在某個設備上。那麼python+JavaScript也是個好的嘗試。

還有一條編程的路,就是微信小程序,也是可以的啊,寫好以後手機難出來就能用。


機器學習小兵


python學起來簡單,用起難,不是想象中的完美!


都市忍者


我建議你學aarido,這個軟件開發桌面軟件非常方便


亮哥


如果你沒有基礎,然後只是想做一個簡單的類似計算器的功能的話,可以考慮LabVIEW。都是圖形化操作,我曾經用它做過一個計算MOS管損耗的計算器,基本沒難度。


分享到:


相關文章: