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