Skip to content

File pickers & long tasks ​

System file dialogs (main-thread safe) and cancellable progress bars.


File / folder pickers ​

python
host.pick_file(title="选择文件", name_filter="所有文件 (*.*)", default_dir="") -> str
host.pick_files(title="选择文件", name_filter="所有文件 (*.*)", default_dir="") -> list[str]
host.pick_folder(title="选择文件夹", default_dir="") -> str
host.pick_save_file(title="保存文件", name_filter="所有文件 (*.*)", default_path="") -> str
  • Internally hops to the Qt GUI thread and opens the system dialog; callable from any thread, blocks until the user closes it.
  • Cancel returns "" / [] and never raises.
  • name_filter looks like "文本 (*.txt *.json);;所有文件 (*.*)".
  • Need a file path in the plugin's private config? Write the returned path back into the plugin config / KV.

To let users pick a file by clicking a button on the settings page, see Custom settings view (config_schema's type: "file" currently has no file-picker widget).


Long-task progress (cancellable) ​

python
tid = host.begin_progress(plugin_id, title="批量汉化", total=120.0) -> str
host.update_progress(tid, current=12, message="item.lua") -> bool
host.is_progress_cancelled(tid) -> bool     # check it inside the loop; exit as soon as it returns True
host.end_progress(tid, success=True, message="完成") -> bool
host.cancel_progress(tid) -> bool           # usually triggered by the UI / a hotkey
host.get_progress(tid) -> dict
  • Front-end signal: pluginProgress(task_id, plugin_id, ratio, message, done, cancelled).
  • total <= 0 means indeterminate progress; ratio ranges over 0~1.
  • After a task ends it is kept for about 60 seconds so it can be queried, then cleaned up automatically.
  • begin_progress returns a task_id; not getting one means the host refused (e.g. the same plugin already has a task running).

Standard pattern ​

python
def action_batch(self, **kwargs):
    tid = self.host.begin_progress(self.id, title="批量处理", total=float(len(items)))
    if not tid:
        return {"ok": False, "error": "已有任务在跑"}
    ok = True
    try:
        for i, it in enumerate(items, 1):
            if self.host.is_progress_cancelled(tid):
                ok = False
                break
            self._process(it)
            self.host.update_progress(tid, current=i, message=it.name)
    finally:
        self.host.end_progress(tid, success=ok,
                               message="完成" if ok else "已取消")
    return {"ok": ok}

Key points:

  • is_progress_cancelled() returns True only on a genuine cancellation, and you must check it inside the loop — this is the only interruption point.
  • Keep the loop body fast; if a single item is very slow, make the cancellation granularity coarser instead of chopping the task up just to stay responsive.
  • When the action itself is triggered by a hotkey / QML, wrap the whole thing in self.run_in_background(...) — don't block the UI.

TransBox — empowering borderless cross-language communication for everyone