kopia lustrzana https://github.com/corrscope/corrscope
Remember most recently selected filetype in Render dialog
Some users prefer to always render their files as .mkv or another filetype. This commit changes corrscope's render dialog to remember the most recently selected filetype, and default to it on next render.pull/493/head
rodzic
6243148685
commit
62b8053b96
|
@ -4,6 +4,7 @@
|
|||
|
||||
- Implement split stereo bar colors (#491)
|
||||
- Add "Reload Font List" menu item to fix missing fonts (#492)
|
||||
- Remember most recently selected filetype in Render dialog (#493)
|
||||
|
||||
### Major Changes
|
||||
|
||||
|
|
|
@ -36,9 +36,6 @@ YAML_EXTS = [".yaml"]
|
|||
# Default extension when writing Config.
|
||||
YAML_NAME = YAML_EXTS[0]
|
||||
|
||||
# Default output extension, only used in GUI and unit tests
|
||||
VIDEO_NAME = ".mp4"
|
||||
|
||||
|
||||
DEFAULT_NAME = corrscope.app_name
|
||||
|
||||
|
|
|
@ -566,17 +566,20 @@ class MainWindow(qw.QMainWindow, Ui_MainWindow):
|
|||
cfg_filename = self.get_save_filename(cli.YAML_NAME)
|
||||
|
||||
# Folder is obtained from self.pref.file_dir_ref.
|
||||
filters = ["YAML files (*.yaml)", "All files (*)"]
|
||||
path = get_save_file_path(
|
||||
extensions = {
|
||||
".yaml": "YAML files (*.yaml)",
|
||||
None: "All files (*)",
|
||||
}
|
||||
save_name = get_save_file_path(
|
||||
self,
|
||||
"Save As",
|
||||
self.pref.file_dir_ref,
|
||||
cfg_filename,
|
||||
filters,
|
||||
extensions,
|
||||
cli.YAML_NAME,
|
||||
)
|
||||
if path:
|
||||
self._cfg_path = path
|
||||
if save_name:
|
||||
self._cfg_path = Path(save_name.name)
|
||||
self.load_title()
|
||||
self.on_action_save()
|
||||
return True
|
||||
|
@ -618,30 +621,34 @@ class MainWindow(qw.QMainWindow, Ui_MainWindow):
|
|||
return
|
||||
|
||||
# Name and extension (no folder).
|
||||
video_filename = self.get_save_filename(cli.VIDEO_NAME)
|
||||
filters = [
|
||||
"MP4 files (*.mp4)",
|
||||
"Matroska files (*.mkv)",
|
||||
"WebM files (*.webm)",
|
||||
"All files (*)",
|
||||
]
|
||||
|
||||
video_stem = cli.get_file_stem(self._cfg_path, self.cfg, default="")
|
||||
|
||||
# Points to either `file_dir` or `render_dir`.
|
||||
# Folder is obtained from `dir_ref`.
|
||||
dir_ref = self.pref.render_dir_ref
|
||||
|
||||
path = get_save_file_path(
|
||||
self, "Render to Video", dir_ref, video_filename, filters, cli.VIDEO_NAME
|
||||
suffix = self.pref.render_ext
|
||||
extensions = {
|
||||
".mp4": "MP4 files (*.mp4)",
|
||||
".mkv": "Matroska files (*.mkv)",
|
||||
".webm": "WebM files (*.webm)",
|
||||
None: "All files (*)",
|
||||
}
|
||||
assert gp.VIDEO_NAME in extensions
|
||||
save_name = get_save_file_path(
|
||||
self, "Render to Video", dir_ref, video_stem, extensions, suffix
|
||||
)
|
||||
if path:
|
||||
name = str(path)
|
||||
if save_name:
|
||||
self.pref.render_ext = save_name.suffix
|
||||
|
||||
dlg = CorrProgressDialog(self, "Rendering video")
|
||||
|
||||
# FFmpegOutputConfig contains only hashable/immutable strs,
|
||||
# so get_ffmpeg_cfg() can be shared across threads without copying.
|
||||
# Optionally copy_config() first.
|
||||
|
||||
outputs = [self.cfg.get_ffmpeg_cfg(name)]
|
||||
outputs = [self.cfg.get_ffmpeg_cfg(save_name.name)]
|
||||
self.play_thread(outputs, PreviewOrRender.render, dlg)
|
||||
|
||||
def play_thread(
|
||||
|
|
|
@ -11,21 +11,17 @@ Name = str
|
|||
Filter = str
|
||||
|
||||
|
||||
@attr.dataclass
|
||||
class FileName:
|
||||
file_name: Optional[str]
|
||||
file_list: Optional[List[str]]
|
||||
sel_filter: str
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def _get_hist_name(
|
||||
func: Callable[..., Tuple[str, str]],
|
||||
func: Callable[..., Tuple[T, str]],
|
||||
parent: qw.QWidget,
|
||||
title: str,
|
||||
history_dir: _gp.Ref[_gp.GlobalPrefs],
|
||||
default_name: Optional[str],
|
||||
filters: List[str],
|
||||
) -> Optional[FileName]:
|
||||
) -> Optional[T]:
|
||||
"""
|
||||
Get file name.
|
||||
Default folder is history folder, and `default_name`.folder is discarded.
|
||||
|
@ -48,14 +44,12 @@ def _get_hist_name(
|
|||
if isinstance(name, list):
|
||||
assert func == qw.QFileDialog.getOpenFileNames
|
||||
dir = os.path.dirname(name[0])
|
||||
history_dir.set(dir)
|
||||
return FileName(None, name, sel_filter)
|
||||
|
||||
else:
|
||||
assert isinstance(name, str)
|
||||
dir = os.path.dirname(name)
|
||||
history_dir.set(dir)
|
||||
return FileName(name, None, sel_filter)
|
||||
|
||||
history_dir.set(dir)
|
||||
return name
|
||||
|
||||
|
||||
def get_open_file_name(
|
||||
|
@ -68,8 +62,8 @@ def get_open_file_name(
|
|||
qw.QFileDialog.getOpenFileName, parent, title, history_dir, None, filters
|
||||
)
|
||||
if name:
|
||||
assert name.file_name is not None
|
||||
return name.file_name
|
||||
assert isinstance(name, str)
|
||||
return name
|
||||
return None
|
||||
|
||||
|
||||
|
@ -83,35 +77,91 @@ def get_open_file_list(
|
|||
qw.QFileDialog.getOpenFileNames, parent, title, history_dir, None, filters
|
||||
)
|
||||
if name:
|
||||
assert name.file_list is not None
|
||||
return name.file_list
|
||||
assert isinstance(name, list)
|
||||
return name
|
||||
return None
|
||||
|
||||
|
||||
# Returns Path for legacy reasons. Others return str.
|
||||
@attr.dataclass
|
||||
class KeyFilter:
|
||||
key: Optional[str]
|
||||
filter: str
|
||||
|
||||
|
||||
@attr.dataclass
|
||||
class SaveName:
|
||||
name: str
|
||||
|
||||
# Used to key into get_save_file_path(filters).
|
||||
suffix: Optional[str]
|
||||
|
||||
|
||||
def get_save_file_path(
|
||||
parent: qw.QWidget,
|
||||
title: str,
|
||||
history_dir: _gp.Ref[_gp.GlobalPrefs],
|
||||
default_name: str,
|
||||
filters: List[str],
|
||||
default_suffix: str,
|
||||
) -> Optional[Path]:
|
||||
name = _get_hist_name(
|
||||
qw.QFileDialog.getSaveFileName,
|
||||
parent,
|
||||
title,
|
||||
history_dir,
|
||||
default_name,
|
||||
filters,
|
||||
initial_stem: str,
|
||||
filters: Dict[Optional[str], str],
|
||||
suffix: Optional[str],
|
||||
) -> Optional[SaveName]:
|
||||
"""
|
||||
Given a working directory and initial filename, request and return a filename to
|
||||
save a file.
|
||||
|
||||
This function takes multiple filetypes, an initial filetype (by string
|
||||
extension), and additionally returns the filetype the user selected.
|
||||
"""
|
||||
|
||||
init_filter = filters.get(suffix, None)
|
||||
# If unsupported save extension (newer version?), use default extension.
|
||||
if not init_filter:
|
||||
suffix, init_filter = next(iter(filters.items()))
|
||||
|
||||
# Get initial directory for the dialog.
|
||||
init_path: str = history_dir.get()
|
||||
|
||||
# Get initial filename if present.
|
||||
if initial_stem:
|
||||
init_path = os.path.join(init_path, os.path.basename(initial_stem))
|
||||
# Append file extension.
|
||||
if suffix:
|
||||
init_path += suffix
|
||||
|
||||
# Get filename from dialog.
|
||||
filter_str = ";;".join(filters.values())
|
||||
out_name, sel_filter = qw.QFileDialog.getSaveFileName(
|
||||
parent, title, init_path, filter_str, init_filter
|
||||
)
|
||||
if name:
|
||||
assert name.file_name is not None
|
||||
path = Path(name.file_name)
|
||||
|
||||
if name.sel_filter == filters[0] and path.suffix == "":
|
||||
path = path.with_suffix(default_suffix)
|
||||
|
||||
return path
|
||||
else:
|
||||
if not out_name:
|
||||
return None
|
||||
|
||||
for suffix, filter in filters.items():
|
||||
if filter == sel_filter:
|
||||
out_suffix = suffix
|
||||
break
|
||||
else:
|
||||
# getSaveFileName() will always return a filter from the list (with whitespace collapsed,
|
||||
# https://github.com/qt/qtbase/blob/ec011141b8d17a2edc58e0d5b6ebb0f1632fff90/src/widgets/dialogs/qfiledialog.cpp#L1415).
|
||||
# This block is only reachable if the returned filter is not in `filter_str` passed in.
|
||||
# This indicates a bug in the caller (incorrect whitespace in filters.values()).
|
||||
raise RuntimeError(
|
||||
f"unrecognized file filter {repr(sel_filter)} in {list(filters.values())}"
|
||||
)
|
||||
|
||||
user_suffix = Path(out_name).suffix
|
||||
|
||||
# If user explicitly types a different extension, use it next time (even if they
|
||||
# don't change the dropdown). This works on Windows and FIXME Linux? Mac?
|
||||
# FIXME it seems confusing to pick a different default extension if you type but don't dropdown? IDK.
|
||||
if user_suffix and user_suffix in filters:
|
||||
out_suffix = user_suffix
|
||||
|
||||
# Append suffix if missing in user-specified name but present in dialog.
|
||||
# getSaveFileName() does not automatically append a suffix on Linux KDE.
|
||||
if user_suffix == "" and out_suffix:
|
||||
out_name += out_suffix
|
||||
|
||||
# Update recently used dir.
|
||||
dir = os.path.dirname(out_name)
|
||||
history_dir.set(dir)
|
||||
return SaveName(out_name, out_suffix)
|
||||
|
|
|
@ -6,6 +6,9 @@ from atomicwrites import atomic_write
|
|||
from corrscope.config import DumpableAttrs, yaml
|
||||
from corrscope.settings import paths
|
||||
|
||||
# Default output extension.
|
||||
VIDEO_NAME = ".mp4"
|
||||
|
||||
|
||||
Attrs = TypeVar("Attrs", bound=DumpableAttrs)
|
||||
|
||||
|
@ -40,6 +43,9 @@ class GlobalPrefs(DumpableAttrs, always_dump="*"):
|
|||
separate_render_dir: bool = False
|
||||
render_dir: str = "" # Set to "" whenever separate_render_dir=False.
|
||||
|
||||
# Most recent video filetype, including period.
|
||||
render_ext: Optional[str] = VIDEO_NAME
|
||||
|
||||
@property
|
||||
def render_dir_ref(self) -> "Ref[GlobalPrefs]":
|
||||
if self.separate_render_dir:
|
||||
|
|
Ładowanie…
Reference in New Issue