如何編寫可在 OBS 中執行的 Python 腳本
訂閱 480
先決條件
閱讀本節的先決條件是已經掌握了如何在 OBS 中執行 Python 腳本,你可以檢視如何設定 OBS 執行 Python 腳本一節來了解相關資訊。
將 Python 腳本函式匯出至 OBS
當一個 Python 腳本擁有特定名稱和參數的函式時,OBS 將發現並使用這些函式來實作某些效果,比如,在 OBS 中顯示一段描述腳本作用的文字。Python 腳本與 OBS 之間的這種聯系,被官方稱為腳本函式匯出(Script Function Exports)。
將腳本函式匯出至 OBS 是選擇性行為
將腳本函式匯出至 OBS 是一種選擇性行為,你所編寫的 Python 腳本可以沒有任何函式匯出,這不會影響 OBS 對腳本的執行。
在 OBS 中展示 Python 腳本描述
每一個成功載入 OBS 的 Python 腳本,都會在腳本視窗中擁有一段描述資訊。通過在腳本中定義一個名為script_description的函式,你可以設定該腳本的相關描述文字,當然,script_description應傳回一個字串。
OBS 支援在腳本描述中包含空白字元
script_description函式傳回的字串中可以包含某些空白字元,比如,定位字元(\t),他們可以正常顯示在腳本視窗中。
# 匯入模組 obspython
import obspython as obs
def script_description():
return '這是一個簡單但沒有任何效果的腳本\n作者:\t哎呦餵\n版本:\t0.1\n聯系:\txxx'在 OBS 中展示 Python 腳本屬性
函式script_properties是非常重要的,當你希望 Python 腳本與使用者可以互動時,該函式需要傳回一個包含腳本相關屬性資訊的屬性集物件。OBS 將根據script_properties傳回的屬性集物件,在腳本視窗中建立一系列的控製項,比如文字方塊,按鈕,核取方塊等。通過這些控製項,使用者可以修改調整腳本的屬性,從而改變腳本所實作的功能。
OBS 中的腳本屬性是什麽?
OBS 中的腳本屬性(Script Properties)可以指表現在腳本視窗中的控製項,也可以指用於建立這些控製項的物件(腳本屬性物件)。每一個腳本屬性都對應了一個腳本設定(Script Settings)項,用於儲存屬性對應的值,不過,按鈕控製項除外。
OBS 中的腳本設定是什麽?
腳本設定被儲存在一個資料設定物件中,該物件采用類似於鍵值組的方式來讀取及寫入一些資料。在 OBS 官方文件中,並沒有類似於「腳本設定」這樣的正式稱呼,我們采用他只是為了方便講述。腳本設定中的一個項未必會對應一個腳本屬性,雖然你可以依據腳本設定來影響 Python 腳本所實作的功能,但他們對於使用者是不可見的。
針對不同的場景群組,OBS 會在合適的時機,將腳本設定以某種形式儲存至本機,或反轉此過程,這樣,腳本設定便具有了永久化效果。通過腳本視窗移除某個 Python 腳本檔案後,其對應的腳本設定也將從本機刪除。
互動控製項
想要詳細了解屬性集物件以及如何在 OBS 中使用控製項,你可以檢視如何使用控製項與 OBS 使用者互動?屬性集物件介紹一節。
在函式script_properties中,我們使用obs_properties_create建立了一個屬性集物件,並通過obs_properties_add_text為屬性集新增了一個對應微調方塊的屬性物件。
def script_properties():
# 建立一個屬性集物件
props = obs.obs_properties_create()
# 新增一個對應微調方塊的腳本屬性物件,用於表示小時
obs.obs_properties_add_int(props, 'hours', '小時:', 2, 5, 1)
return props在 OBS 載入或卸載 Python 腳本時執行工作
如果希望在 Python 腳本被 OBS 載入或卸載時執行某些工作,比如從檔案讀取或寫入資料,那麽你可以在腳本中定義函式script_load,script_unload。
script_load(settings)
- settings 參數
settings參數為 Python 腳本對應的腳本設定物件。
什麽情況下 OBS 會載入或卸載 Python 腳本?
當你在腳本視窗加入腳本,重新載入腳本,恢復預設設定檔時,或 OBS 啟動後,腳本就會被載入。當你在腳本視窗移除腳本,重新載入腳本時,恢復預設設定檔之前,或 OBS 關閉時,腳本就會被卸載。
在下面的程式碼中,我們通過函式script_load和script_unload,記錄並顯示了上一次腳本停止的時間。事實上,這種做法在直接關閉 OBS 時是無效的,原因會在稍後給予說明。
# 變數 data 表示腳本設定
data = None
def script_load(settings):
global data
data = settings
# 讀取腳本設定項 closed_time,他是腳本的停止時間
closed_time = obs.obs_data_get_string(data, 'closed_time')
if closed_time:
obs.script_log(obs.LOG_INFO, f'上次腳本停止的時間為 {closed_time}')
def script_unload():
# 將目前時間寫入腳本設定項 closed_time,作為腳本的停止時間
from datetime import datetime
obs.obs_data_set_string(data, 'closed_time', datetime.now().ctime())[exports.py] 上次腳本停止的時間為 Mon Mar 4 07:12:37 2024obspython 模組的 obs_data_get_string,obs_data_set_string 函式
obspython模組的obs_data_get_string和obs_data_set_string函式,用於對資料設定物件中字串型別的資料項進行讀取及寫入操作。
與之類似的函式還有obs_data_get_int,obs_data_set_int等。
在 OBS 使用者修改 Python 腳本屬性後執行工作
如果希望在使用者修改腳本屬性(通過腳本視窗)後執行某些工作,比如,根據修改後的腳本屬性調整實作的功能,那麽你可以在 Python 腳本中定義函式script_update,該函式的執行時間點在script_load函式與script_unload函式之間。
需要指出的是,任何腳本屬性的修改都會導致script_update函式的呼叫。如果僅監視某一個腳本屬性的變化,那麽可以使用obspython模組的obs_property_set_modified_callback函式。
script_update(settings)
- settings 參數
settings參數為 Python 腳本對應的腳本設定物件。
什麽情況下 OBS 會呼叫 script_update 函式?
除了在使用者修改腳本屬性後,script_update函式還會在加入腳本,重新載入腳本,恢復預設設定檔或 OBS 啟動時被呼叫。
腳本視窗中控製項顯示的值與腳本設定項對應的值可能不相同
在使用者對腳本視窗中的控製項進行有效編輯之前,控製項所顯示的值不會同步至對應的腳本設定項。這會產生困擾,因為使用者從該控製項看到的值,可能與你從腳本設定中讀取到的值並不相同。一旦腳本屬性對應的視窗控製項被有效編輯,那麽可從腳本設定安全的讀取對應控製項中的值。
要避免以上不相同的情況,可以定義script_defaults函式,並在該函式中為控製項對應的腳本設定項設定預設值。
之前,在函式script_properties中,我們新增了最小值為2的微調方塊,但讀取其對應的腳本設定項後,其結果可能顯示為0,如果該微調方塊未被有效編輯的話。
def script_update(settings):
# 讀取腳本設定項 hours 並顯示
hours = obs.obs_data_get_int(settings, 'hours')
obs.script_log(obs.LOG_INFO, f'目前小時為 {hours}')[exports.py] 目前小時為 0在 OBS 關閉時將資料寫入 Python 腳本設定
在 OBS 關閉時,Python 腳本中的script_save函式會被呼叫,此時可將一些重要的資料寫入腳本設定,以便下次啟動時讀取他們。
script_save(settings)
- settings 參數
settings參數為 Python 腳本對應的腳本設定物件。
在關閉 OBS 時通過 script_unload 函式寫入的腳本設定項不會被儲存至本機
雖然 OBS 在關閉時,會呼叫script_unload函式,但對腳本設定項的寫入操作可能會「無效」,因為他們不會被 OBS 儲存至本機,這不同於點選腳本視窗的重新載入腳本按鈕。
儲存重要資料的最佳方式是定義函式script_save而非script_unload,雖然之前的範例中示範了相反的做法。
我們使用script_save函式代替script_unload函式,這將使得 OBS 的關閉時間被視為腳本停止時間,而其他使用者操作(比如,點選重新載入腳本按鈕)的時間,不再被視為腳本的停止時間。
def script_save(settings):
# 將目前時間寫入腳本設定項 closed_time,作為腳本的停止時間
from datetime import datetime
obs.obs_data_set_string(settings, 'closed_time', datetime.now().ctime())為 Python 腳本屬性指定預設值
在 OBS 官方提供的obspython模組中,你無法直接通過參數為腳本屬性指定預設值,雖然一些新增腳本屬性的函式可以達到近似的效果。要為腳本視窗中的控製項設定預設值,應該在 Python 腳本中定義函式script_defaults,並在該函式中為腳本屬性對應的腳本設定項指定預設值。
script_defaults(settings)
- settings 參數
settings參數為 Python 腳本對應的腳本設定物件。
腳本設定項的預設值僅在其沒有具體值時被使用
需要指出,一旦腳本設定項擁有了具體的值,其預設值將不再被使用,這會發生在使用者編輯了某個控製項後,或你通過函式為腳本設定項指定了一個值後。
當你點選腳本視窗的預設設定檔按鈕後,腳本設定項的預設值將重新發揮作用,因為腳本設定項的具體值已經被清除。
在函式script_defaults中,我們將腳本設定項hours的預設值指定為3,這會使腳本視窗中的微調方塊預設顯示3。如果之前編輯過該微調方塊,那麽需要點選預設設定檔按鈕,才能看到該效果。
def script_defaults(settings):
# 將設定項 hours 的預設值設定為 3
obs.obs_data_set_default_int(settings, 'hours', 3)obspython 模組的 obs_data_set_default_int 函式
obspython模組的obs_data_set_default_int函式,用於為資料設定物件中整數型別的資料項設定預設值。
在 OBS 繪製每一影格時執行工作
如果你希望在 OBS 繪製每一影格時執行某些工作,那麽可以在 Python 腳本中定義函式script_tick。
script_tick(seconds)
- seconds 參數
seconds參數是上一影格到本影格所經歷的秒數。
使用 script_tick 函式可能為 OBS 帶來效能問題
OBS 對script_tick函式的呼叫頻率較高,因此,使用該函式完成複雜工作並不是一個好主意,如果你通過script_log函式不斷向指令稿記錄視窗輸出資訊,那麽 OBS 可能會無法回應使用者,在長時間執行之後。
obspython模組提供了另外一些關於計時器的函式,使用他們可以有效避免script_tick函式所產生的問題。
def script_tick(seconds):
obs.script_log(obs.LOG_INFO, f'{seconds} OBS 就要無法回應了!!!')取得 Python 腳本所在的目錄
對於新增的 Python 腳本,OBS 會為其定義一個名為script_path的函式(不屬於obspython模組),該函式可用於取得目前腳本所在的目錄。
script_path 函式與 Python 模組的 __file__ 特性之間的區別
__file__特性表示了 Python 模組對應的檔案路徑,而script_path函式傳回的是腳本檔案所在資料夾的路徑。
我們調整之前的程式碼,在script_load中呼叫script_path函式,並顯示傳回的目錄。
def script_load(settings):
# …
obs.script_log(obs.LOG_INFO, script_path())# Windows 中的輸出結果
[exports.py] …/scripts/為 OBS 新增或移除腳本計時器
通過obspython模組的timer_add和timer_remove函式,你可以為 OBS 新增或移除腳本計時器(Script Timers)。腳本計時器將在指定時間間隔下,反覆呼叫給出的回呼函式或方法,這不同於script_tick函式。
一旦使用了timer_add函式,被新增的腳本計時器就會開始工作。如果希望腳本計時器停止,可以通過timer_remove函式將其移除,或在回呼函式和方法中呼叫obspython模組的remove_current_callback函式。
timer_add(callback, milliseconds)
timer_remove(callback)
- callback 參數
callback參數是被腳本計時器回呼的函式或方法,在timer_remove函式中,callback對應的腳本計時器將被移除。- milliseconds 參數
milliseconds參數是計時器觸發的時間間隔,以毫秒為單位,接受整數型別的值。
timer_remove 函式無法移除執行個體方法對應的腳本計時器
如果將模組中的 Python 函式作為回呼目標,那麽timer_add和timer_remove可以正確執行,如果將 Python 類別的執行個體方法作為回呼目標,那麽timer_remove可能失效,其對應的腳本計時器不會被移除。
在下面的程式碼中,welcome是腳本計時器的回呼函式,我們通過remove_current_callback使該函式只被呼叫一次。
# 腳本計時器的回呼函式 welcome
def welcome():
obs.script_log(obs.LOG_INFO, '這是只被呼叫一次的回呼函式')
# 移除 welcome 對應的腳本計時器
obs.remove_current_callback()
# 加入腳本計時器,觸發時間間隔為 3 秒
obs.timer_add(welcome, 3000)[exports.py] 這是只被呼叫一次的回呼函式