顯示具有 GAS 標籤的文章。 顯示所有文章
顯示具有 GAS 標籤的文章。 顯示所有文章

2024年3月24日 星期日

Google Apps Script (GAS) 學習筆記 (四) : 試算表操作 (上)

本篇記錄用 GAS 操作 Google 試算表的測試結果.

試算表 (SpreadSheetApp) 是 Google 雲端硬碟最常用的 App, 除了可以取代 Excel 外, 它還可以做為一個小型資料庫. GAS 提供豐富的 API 來存取與操控試算表, 搭配觸發器可以打造免費的雲端自動化應用, 教學文件參考 :


GAS 程式可以是獨立放在 Google 雲端硬碟下的 .gs 檔; 也可以是寄生在各種 Google Apps (文件, 試算表, 或表單等), 它們的差別在於 GAS API 的存取範圍, 以存取試算表為例, SpreadSheetApp 物件提供了四個方法 (API) 來取得試算表 (SpreadSheet) 或工作表 (Sheet) 物件 : 


 SpreadSheetApp 物件方法 說明
 openById(id) 根據 ID 開啟試算表, 傳回 SpreadSheet 物件
 openByUrl(url) 根據 URL 開啟試算表, 傳回 SpreadSheet 物件
 getActiveSpreadsheet() 傳回使用中的試算表物件 (SpreadSheet), 僅寄生的 GAS 可用.
 getActiveSheet() 傳回使用中的工作表 (Sheet) 物件, 僅寄生的 GAS 可用.


注意, getActiveSpreadsheet() 與 getActiveSheet() 這兩個方法只能在寄生的 GAS 程式中可以呼叫, 而 openById() 與 openByUrl() 則不論是在獨立的 GAS 檔或寄生的 GAS 程式碼中均可呼叫. 


一. 新增試算表 : 

為了以下測試, 我們先在雲端硬碟建立一個 GAS 資料夾, 並在其下建立一個試算表, 並將其更名為 myproject : 






將底下預設的工作表更名為 scores, 然後在儲存格中輸入成績資料 :




點選上方 "擴充功能" 選單, 再點選 "Apps Script" 開啟一個 GAS 程式編輯視窗 :




把上方預設的 GAS 專案名稱改為 scores : 




二. 用 GAS 操作試算表 : 

操作試算表之前須先開啟試算表取得 Spreadsheet 物件, 然後利用其方法來存取儲存格內容. 對於一個已建立之試算表, 我們可以透過下列 SpreadSheetApp 物件的四個方法開啟它 : 
  • openById(id)  
  • openByUrl(url)
  • getActiveSpreadsheet() 
  • getActiveSheet()
如前所述, 前兩者不管是獨立的 GAS 程式或寄生於試算表內的 GAS 程式都可呼叫, 但後兩者只有寄生的 GAS 程式可呼叫. 它們都會傳回一個 Spreadsheet 物件, 它有許多方法, 其中最常用的是 getSheetByName(), 只要傳入工作表名稱就會傳回一個 Sheet 物件, 呼叫其 getRange() 方法就能定位出儲存格取得 Range 物件, 最後呼叫 Range 物件的 setValue() 與 getValue() 方法就可以存取儲存格內容了. 




注意, 若 getRange() 選取的是單一儲存格, 存取時是呼叫 getValue() 與 setValue(); 若選取一個區域, 則存取時是呼叫 getValues() 與 setValues(), 傳回值或傳入值均為陣列. 

首先來釐清試算表的 ID 與 URL, 在試算表視窗上方的網址列就是此試算表的 URL, 例如 : 

https://docs.google.com/spreadsheets/d/1ZEGvDt7JtglqVS3IuAPyelSOOjWe_BHN0jT4R7rHNk4/edit#gid=0

其中 https://docs.google.com/spreadsheets/d/ 後面一直到 /edit... 之間的字串就是其 id :




以下分別測試這三種開啟試算表的方式 : 


1. 呼叫 openByUrl() 開啟試算表並存取工作表 :   

只要將試算表的 URL 傳入 openByUrl() 方法即可開啟試算表, 它會傳回一個 SpreadSheet 物件. 在 GAS 程式編輯視窗輸入下列程式碼 : 

function myFunction() {
  var url="https://docs.google.com/spreadsheets/d/1ZEGvDt7JtglqVS3IuAPyelSOOjWe_BHN0jT4R7rHNk4/edit#gid=0";
  var ss=SpreadsheetApp.openByUrl(url); //傳回 Spreadsheet 物件
  var sheet=ss.getSheetByName('scores'); //傳回 Sheet 物件
  var range=sheet.getRange(2, 1); //取得第 2 列第 1 欄儲存格 Range 物件
  Logger.log(range.getValue()); //取得第 2 列第 1 欄儲存格內容 ('金智媛')
}

此程式用 openByUrl() 取得試算表物件 ss, 然後呼叫 ss 的 getSheetByName() 取得名為 scores 的工作表物件 sheet, 接著呼叫 sheet 的 getRange() 方法取得第 2 列第 1 欄的儲存格物件 range, 最後呼叫 range 物件的 getValue() 取得儲存格內容. 

注意 getRange() 的參數結構有兩種, 第一種是使用索引來選取 :

getRange(row, column [, numRows, numColumns]) 

前兩個參數分別是列索引與欄索引 (都是 1 起始, A 欄為 1, B 欄為 2, ...); 後兩個為可有可無參數, 分別為列數與欄數 (用來選取一個矩形區域之儲存格). 

第二種是使用試算表的欄列名稱標示法 (傳入值為字串), 例如 : 

getRange('A2:C6')   //選取 A2~C6 範圍內之儲存格

也可以跳過取得工作表物件步驟, 直接呼叫 Spreadsheet 物件的 getRange() 方法, 但在傳入參數前面添加工作表名稱 (用 ! 隔開欄列名稱), 例如 :

ss.getRange('scores!A2:C6')  //選取 scores 工作表的 A2~C6 範圍內之儲存格

執行結果如下 : 




上面程式碼是取得單一儲存格, 如果 getRange() 時是選取一個區域, 則要呼叫 getValues(), 它會傳回一個陣列, 例如將上面的程式碼改成如下 :

function myFunction() {
  var url="https://docs.google.com/spreadsheets/d/1ZEGvDt7JtglqVS3IuAPyelSOOjWe_BHN0jT4R7rHNk4/edit#gid=0";
  var ss=SpreadsheetApp.openByUrl(url); //傳回 Spreadsheet 物件
  var sheet=ss.getSheetByName('scores'); //傳回 Sheet 物件
  var range=sheet.getRange(2, 1, 3, 4); //取得第 2 列第 1 欄起 3 列 4 欄的區域儲存格 Range 物件
  Logger.log(range.getValues()); //取得第 2 列第 1 欄起 3 列 4 欄的區域內容 (陣列)
}

注意, 此處是選取一個區域的儲存格, 所以應該呼叫 getValues() 而非 getValue(), 結果會傳回三個學生的全部成績, 結果如下 :




2. 呼叫 openById() 開啟試算表並存取工作表 :   

將上面程式碼改為呼叫 openById() 來開啟試算表, 同樣會傳回一個 Spreadsheet 物件 : 

function myFunction() {
  var id="1ZEGvDt7JtglqVS3IuAPyelSOOjWe_BHN0jT4R7rHNk4";
  var ss=SpreadsheetApp.openById(id); //傳回 Spreadsheet 物件
  var sheet=ss.getSheetByName('scores'); //傳回 Sheet 物件
  var range=sheet.getRange(2, 1, 3, 4); //取得第 2 列第 1 欄起 3 列 4 欄的區域儲存格 Range 物件
  Logger.log(range.getValues()); //取得第 2 列第 1 欄起 3 列 4 欄的區域內容 (陣列)
}

執行結果與上面相同 : 




3. 呼叫 getActiveSpreadsheet() 開啟試算表並存取工作表 :  

將上面程式碼改為呼叫 getActiveSpreadsheet() 來開啟試算表, 同樣會傳回一個 Spreadsheet 物件 : 

function myFunction() {
  var ss=SpreadsheetApp.getActiveSpreadsheet(); //傳回 Spreadsheet 物件
  var sheet=ss.getSheetByName('scores'); //傳回 Sheet 物件
  var range=sheet.getRange(2, 1, 3, 4); //取得第 2 列第 1 欄起 3 列 4 欄的區域儲存格 Range 物件
  Logger.log(range.getValues()); //取得第 2 列第 1 欄起 3 列 4 欄的區域內容 (陣列)
}

結果如下 : 




也可以呼叫 getActiveSheet() 方法直接取得使用中的工作表, 例如 :

function myFunction() {
  var sheet=SpreadsheetApp.getActiveSheet(); //傳回使用中的 Sheet 物件 (工作表)
  var range=sheet.getRange(2, 1, 3, 4); //取得第 2 列第 1 欄起 3 列 4 欄的區域儲存格 Range 物件
  Logger.log(range.getValues()); //取得第 2 列第 1 欄起 3 列 4 欄的區域內容 (陣列)
}

執行結果與上面相同 : 




注意, getActiveSpreadsheet() 與 getActiveSheet() 方法只能在寄生於試算表中的 GAS 程式碼可呼叫, 無法於獨立的 GAS 程式中使用. 


4. 於獨立的 GAS 程式中開啟試算表 :  

上面的測試中所使用的 GAS 是寄生於試算表的程式碼, 可以使用 SpreadsheetApp 物件的openByUrl(), openById(), 與 getActiveSpreadsheet() 方法開啟試算表, 但在獨立於 App 外的 .gs 檔案中, 只能呼叫 openByUrl() 與 openById() 這兩個方法來開啟試算表. 

首先按雲端硬碟底下的 "+ 新增" 鈕, 點選 "更多..." 後再點選 "Google Apps Script" : 



 
這時會彈出一個視窗, 提醒此資料夾的所有協作者皆能存取此 GAS 檔, 按 "建立指令碼" 鈕會開啟一個 "未命名的專案" 的 Google Apps 程式編輯視窗 : 






這時切回雲端硬碟視窗會發現底下出現一個 "未命名的專案.gs" 程式檔 :




點選 Google Apps 程式編輯視窗左上角的 "未命名的專案" 將其重新命名為 "myproject" : 





這時切回雲端硬碟視窗, 原本的 "未命名的專案.gs" 也會更名為 "myproject.gs" :



 
然後編輯 GAS 程式專案 myproject 裡面的預設函式 myFunction(), 輸入如下程式碼 : 

function myFunction() {
  var url="https://docs.google.com/spreadsheets/d/1ZEGvDt7JtglqVS3IuAPyelSOOjWe_BHN0jT4R7rHNk4/edit#gid=0"; 
  var ss=SpreadsheetApp.openByUrl(url); //傳回 Spreadsheet 物件
  var sheet=ss.getSheetByName('scores'); //傳回 Sheet 物件
  var range=sheet.getRange(2, 1, 3, 4); //取得第 2 列第 1 欄起 3 列 4 欄的區域儲存格 Range 物件
  Logger.log(range.getValues()); //取得第 2 列第 1 欄起 3 列 4 欄的區域內容 (陣列)  
}




按 "存檔" 鈕後按 "執行" 會出現授權頁面, 按 "審查權限" 鈕 :




選擇自己的 Google 帳戶 : 



在警示視窗中按 "進階" : 




按右下角的 "允許" 即完成授權 :




完成授權即馬上會執行 GAS 程式檔, 結果與上面寄生於試算表的 GAS 程式相同 : 




也可以更改 myproject.gs 檔改用 openById() 開啟試算表 :

function myFunction() {
  var id="1ZEGvDt7JtglqVS3IuAPyelSOOjWe_BHN0jT4R7rHNk4";
  var ss=SpreadsheetApp.openById(id); //傳回 Spreadsheet 物件
  var sheet=ss.getSheetByName('scores'); //傳回 Sheet 物件
  var range=sheet.getRange(2, 1, 3, 4); //取得第 2 列第 1 欄起 3 列 4 欄的區域儲存格 Range 物件
  Logger.log(range.getValues()); //取得第 2 列第 1 欄起 3 列 4 欄的區域內容 (陣列)
}

結果與上面相同.

2024年3月19日 星期二

2024年3月18日 星期一

Google Apps Script (GAS) 學習筆記 (三) : 使用者介面元件 (上)

本篇要來測試 Google Apps 的常用 UI 元件. 


UI 元件是用來與 Google Apps 互動的介面, 本測試主要是以試算表為例, 其實每一種 Google Apps (文件, 試算表, 表單等) 上都可以放 UI 元件. 


一. 按鈕與訊息盒 : 

在 Google Apps 上放置按鈕主要是用來觸發執行一個函式. 首先如前一篇測試所示, 在雲端硬碟的自訂資料夾 GAS 下新增一個試算表, 並更名為 ui_test : 




然後點選 "擴充功能" 再選 "Apps Script" : 




 將產生的未命名 GAS 專案名稱與預設函式 myFunction() 都改為 hello :




然後在 hello() 函式中輸入下列程式碼 : 

Browser.MsgBox('Hello World!');

此處 Browser 為 GAS 的瀏覽器物件, 可在網頁中顯示按鈕, 訊息框與輸入框等元件. 其實在輸入函式的第一個字元時就會顯示下拉式選單讓我們點選物件或方法名稱, 輸入方法的括號時也會顯示參數說明 :






按 "儲存專案" 鈕存檔後, 右邊的 "執行" 鈕才會變成可按 :




按 "執行" 鈕先測試程式碼效果, 這時會彈出一個 "需要授權" 視窗, 請按 "審查權限" 鈕 : 




然後會彈出 Google 帳戶選擇視窗, 點選自己的 Google 帳戶 :




接著會彈出 "這個應用程式未經 Google 驗證" 的警告, 按左下角的 "進階" :



這時底下會出現一段若不信任開發人員 (就是自己啦) 勿繼續操作警語, 請按底下的 "前往 「hello 」 (不安全)" 超連結進行授權 :



在彈出視窗中按右下角的 "允許" 鈕即完成執行此程式之授權 :




這時再次按下專案視窗中的 "執行" 鈕, 就會在試算表視窗中彈出一個訊息框 (注意, 執行結果不是顯示在 GAS 專案視窗, 而是它寄生的試算表視窗) : 





按下 "確定" 鈕即可關閉訊息框, 此按鈕為 msgBox() 方法第二參數所控制, 如果沒有傳入第二參數, 預設為 Browser.Buttons.OK (確定), 其他可傳入值如下 :
  • Browser.Buttons.OK_CANCEL
  • Browser.Buttons.YES_NO
  • Browser.Buttons.YES_NO_CANCEL
以上只是測試此 GAS 專案程式碼是否可順利執行 (並授予該帳戶執行權), 但這次目的不是要在 GAS 專案視窗上直接按執行鈕來執行程式碼, 而是要在試算表 App 上放一個按鈕, 讓使用者按下按鈕時執行 hello(), 所以接下來要在試算表視窗上繪製一個按鈕. 

回到試算表 App 視窗, 點選上方的 "插入" 選單, 再點選 "繪圖" :




這樣會彈出一個繪圖板視窗, 點選圖案按鈕, 選擇 "圖案" 第一排適合做按鈕的圖形, 在繪圖板上畫出一個按鈕圖形 : 





點選此按鈕, 按滑鼠右鍵, 點選 "編輯文字", 輸入按鈕上的文字例如 "按我執行" :




按上方的 "儲存並關閉鈕" : 關閉繪圖板視窗 : 




這時試算表上就會出現一個浮動的按鈕 (可拖曳到任何位置, 不屬於任一儲存格) :




接下來是要將此按鈕綁定按下動作 (click) 要執行的函式, 重點來了, 先點選此按鈕, 這時按鈕右上發會出現三個垂直小點, 按它會彈出一個選單, 點選其中的 "指派指令碼" :




這時會彈出一個視窗, 請在輸入框中輸入欲執行的函式名稱 (不要加括號) 即完成指派 : 




這時按下試算表上的 "按我執行" 鈕就會直接執行 hello() 了 : 




以上雖然只是簡單的功能, 但實際應用時函式可能會很複雜, 但只要按一個按鈕就可以執行此函式, 而不需要開啟 GAS 專案視窗來執行. 

參考 : 


GAS 的 UI 除了輸入外也可以用 Browser 物件的 InputBox() 方法來取得使用者輸入, 我們修改上面的 hello 專案, 將 hello() 函式改成如下 :

function hello() {
  var name=Browser.inputBox('請輸入名字');
  Browser.msgBox('Hello, ' + name);
}




此處 Browser.InputBox() 的傳入參數為輸入框的提示語 (prompt) 或標題, 其傳回值為輸入框內的字串, 我們先將其存在 name 變數中, 然後傳入 msgBox() 的組合字串中輸出. 將此專案存檔後, 切回試算表視窗, 按下 "按我執行" 鈕, 這時會彈出一個輸入框, 填入名字按確定, 就會在談出的訊息框李顯示名字了 :