kopia lustrzana https://github.com/corrscope/corrscope
Improve GUI dialog path defaults (#277)
- Use Channel 0 for render/save file name, if available. - Otherwise use empty string instead of suggesting corrscope.yaml/mp4. - Set save dir when loading .wav from CLI.pull/357/head
rodzic
33c3fdba1d
commit
fa105f1bf5
|
@ -2,16 +2,16 @@ import datetime
|
|||
import sys
|
||||
from itertools import count
|
||||
from pathlib import Path
|
||||
from typing import Optional, List, Tuple, Union, Iterator, cast
|
||||
from typing import Optional, List, Tuple, Union, Iterator, cast, TypeVar
|
||||
|
||||
import click
|
||||
|
||||
import corrscope
|
||||
from corrscope.channel import ChannelConfig
|
||||
from corrscope.config import yaml
|
||||
from corrscope.settings.paths import MissingFFmpegError
|
||||
from corrscope.outputs import IOutputConfig, FFplayOutputConfig, FFmpegOutputConfig
|
||||
from corrscope.corrscope import default_config, CorrScope, Config, Arguments
|
||||
from corrscope.outputs import IOutputConfig, FFplayOutputConfig
|
||||
from corrscope.settings.paths import MissingFFmpegError
|
||||
|
||||
|
||||
Folder = click.Path(exists=True, file_okay=False)
|
||||
|
@ -43,20 +43,39 @@ VIDEO_NAME = ".mp4"
|
|||
DEFAULT_NAME = corrscope.app_name
|
||||
|
||||
|
||||
def get_name(audio_file: Union[None, str, Path]) -> str:
|
||||
# Write file to current working dir, not audio dir.
|
||||
if audio_file:
|
||||
name = Path(audio_file).stem
|
||||
def _get_file_name(cfg_path: Optional[Path], cfg: Config, ext: str) -> str:
|
||||
"""
|
||||
Returns a file path with extension (but no dir).
|
||||
Defaults to "corrscope.ext".
|
||||
"""
|
||||
name = get_file_stem(cfg_path, cfg, default=DEFAULT_NAME)
|
||||
return name + ext
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def get_file_stem(cfg_path: Optional[Path], cfg: Config, default: T) -> Union[str, T]:
|
||||
"""
|
||||
Returns a "name" (no dir or extension) for saving file or video.
|
||||
Defaults to `default`.
|
||||
|
||||
Used by GUI as well.
|
||||
"""
|
||||
if cfg_path:
|
||||
# Current file was saved.
|
||||
file_path_or_name = Path(cfg_path)
|
||||
|
||||
# Current file was never saved.
|
||||
# Master audio and all channels may/not be absolute paths.
|
||||
elif cfg.master_audio:
|
||||
file_path_or_name = Path(cfg.master_audio)
|
||||
elif len(cfg.channels) > 0:
|
||||
file_path_or_name = Path(cfg.channels[0].wav_path)
|
||||
else:
|
||||
name = DEFAULT_NAME
|
||||
return name
|
||||
return default
|
||||
|
||||
|
||||
def get_path(audio_file: Union[None, str, Path], ext: str) -> Path:
|
||||
name = get_name(audio_file)
|
||||
|
||||
# Add extension
|
||||
return Path(name).with_suffix(ext)
|
||||
return file_path_or_name.stem
|
||||
|
||||
|
||||
PROFILE_DUMP_NAME = "cprofile"
|
||||
|
@ -202,8 +221,9 @@ def main(
|
|||
assert False, cfg_or_path
|
||||
|
||||
if write:
|
||||
write_path = get_path(audio, YAML_NAME)
|
||||
yaml.dump(cfg, write_path)
|
||||
# `corrscope file.yaml -w` should write back to same path.
|
||||
write_path = _get_file_name(cfg_path, cfg, ext=YAML_NAME)
|
||||
yaml.dump(cfg, Path(write_path))
|
||||
|
||||
outputs = [] # type: List[IOutputConfig]
|
||||
|
||||
|
@ -211,8 +231,8 @@ def main(
|
|||
outputs.append(FFplayOutputConfig())
|
||||
|
||||
if render:
|
||||
video_path = get_path(cfg_path or audio, VIDEO_NAME)
|
||||
outputs.append(cfg.get_ffmpeg_cfg(str(video_path)))
|
||||
video_path = _get_file_name(cfg_path, cfg, ext=VIDEO_NAME)
|
||||
outputs.append(cfg.get_ffmpeg_cfg(video_path))
|
||||
|
||||
if outputs:
|
||||
arg = Arguments(cfg_dir=cfg_dir, outputs=outputs)
|
||||
|
|
|
@ -102,6 +102,31 @@ class MainWindow(qw.QMainWindow, Ui_MainWindow):
|
|||
|
||||
Opening a document:
|
||||
- load_cfg_from_path
|
||||
|
||||
## Dialog Directory/Filename Generation
|
||||
|
||||
Save-dialog dir is persistent state, saved across program runs.
|
||||
Most recent of:
|
||||
- Any open/save dialog (unless separate_render_dir is True).
|
||||
- self.pref.file_dir_ref, .set()
|
||||
- Load YAML from CLI.
|
||||
- load_cfg_from_path(cfg_path) sets `self.pref.file_dir`.
|
||||
- Load .wav files from CLI.
|
||||
- if isinstance(cfg_or_path, Config):
|
||||
- save_dir = self.compute_save_dir(self.cfg)
|
||||
- self.pref.file_dir = save_dir (if not empty)
|
||||
|
||||
Render-dialog dir is persistent state, = most recent render-save dialog.
|
||||
- self.pref.render_dir, .set()
|
||||
|
||||
Save/render-dialog filename (no dir) is computed on demand, NOT persistent state.
|
||||
- (Currently loaded config path, or master audio, or channel 0) + ext.
|
||||
- Otherwise empty string.
|
||||
- self.get_save_filename() calls cli.get_file_stem().
|
||||
|
||||
CLI filename is the same,
|
||||
but defaults to "corrscope.{yaml, mp4}" instead of empty string.
|
||||
- cli._get_file_name() calls cli.get_file_stem().
|
||||
"""
|
||||
|
||||
def __init__(self, cfg_or_path: Union[Config, Path]):
|
||||
|
@ -145,6 +170,9 @@ class MainWindow(qw.QMainWindow, Ui_MainWindow):
|
|||
# Bind config to UI.
|
||||
if isinstance(cfg_or_path, Config):
|
||||
self.load_cfg(cfg_or_path, None)
|
||||
save_dir = self.compute_save_dir(self.cfg)
|
||||
if save_dir:
|
||||
self.pref.file_dir = save_dir
|
||||
elif isinstance(cfg_or_path, Path):
|
||||
self.load_cfg_from_path(cfg_or_path)
|
||||
else:
|
||||
|
@ -217,7 +245,7 @@ class MainWindow(qw.QMainWindow, Ui_MainWindow):
|
|||
if not self.prompt_save():
|
||||
return
|
||||
name = get_open_file_name(
|
||||
self.pref.file_dir_ref, self, "Open config", ["YAML files (*.yaml)"]
|
||||
self, "Open config", self.pref.file_dir_ref, ["YAML files (*.yaml)"]
|
||||
)
|
||||
if name:
|
||||
cfg_path = Path(name)
|
||||
|
@ -301,7 +329,7 @@ class MainWindow(qw.QMainWindow, Ui_MainWindow):
|
|||
|
||||
def on_master_audio_browse(self):
|
||||
name = get_open_file_name(
|
||||
self.pref.file_dir_ref, self, "Open master audio file", FILTER_WAV_FILES
|
||||
self, "Open master audio file", self.pref.file_dir_ref, FILTER_WAV_FILES
|
||||
)
|
||||
if name:
|
||||
master_audio = "master_audio"
|
||||
|
@ -317,7 +345,7 @@ class MainWindow(qw.QMainWindow, Ui_MainWindow):
|
|||
|
||||
def on_channel_add(self):
|
||||
wavs = get_open_file_list(
|
||||
self.pref.file_dir_ref, self, "Add audio channels", FILTER_WAV_FILES
|
||||
self, "Add audio channels", self.pref.file_dir_ref, FILTER_WAV_FILES
|
||||
)
|
||||
if wavs:
|
||||
self.channel_view.append_channels(wavs)
|
||||
|
@ -341,14 +369,17 @@ class MainWindow(qw.QMainWindow, Ui_MainWindow):
|
|||
"""
|
||||
:return: False if user cancels save action.
|
||||
"""
|
||||
cfg_path_default = self.file_stem + cli.YAML_NAME
|
||||
|
||||
# Name and extension (no folder).
|
||||
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(
|
||||
self.pref.file_dir_ref,
|
||||
self,
|
||||
"Save As",
|
||||
cfg_path_default,
|
||||
self.pref.file_dir_ref,
|
||||
cfg_filename,
|
||||
filters,
|
||||
cli.YAML_NAME,
|
||||
)
|
||||
|
@ -377,14 +408,16 @@ class MainWindow(qw.QMainWindow, Ui_MainWindow):
|
|||
qw.QMessageBox.critical(self, "Error", error_msg)
|
||||
return
|
||||
|
||||
video_path = self.file_stem + cli.VIDEO_NAME
|
||||
# Name and extension (no folder).
|
||||
video_filename = self.get_save_filename(cli.VIDEO_NAME)
|
||||
filters = ["MP4 files (*.mp4)", "All files (*)"]
|
||||
|
||||
# Points to either `file_dir` or `render_dir`.
|
||||
ref = self.pref.render_dir_ref
|
||||
# Folder is obtained from `dir_ref`.
|
||||
dir_ref = self.pref.render_dir_ref
|
||||
|
||||
path = get_save_file_path(
|
||||
ref, self, "Render to Video", video_path, filters, cli.VIDEO_NAME
|
||||
self, "Render to Video", dir_ref, video_filename, filters, cli.VIDEO_NAME
|
||||
)
|
||||
if path:
|
||||
name = str(path)
|
||||
|
@ -460,10 +493,34 @@ class MainWindow(qw.QMainWindow, Ui_MainWindow):
|
|||
return self._cfg_path.name
|
||||
return self.UNTITLED
|
||||
|
||||
@property
|
||||
def file_stem(self) -> str:
|
||||
""" Returns a "config name" with no dots or slashes. """
|
||||
return cli.get_name(self._cfg_path or self.cfg.master_audio)
|
||||
def get_save_filename(self, suffix: str) -> str:
|
||||
"""
|
||||
If file name can be guessed, return "filename.suffix" (no dir).
|
||||
Otherwise return "".
|
||||
|
||||
Used for saving file or video.
|
||||
"""
|
||||
stem = cli.get_file_stem(self._cfg_path, self.cfg, default="")
|
||||
if stem:
|
||||
return stem + suffix
|
||||
else:
|
||||
return ""
|
||||
|
||||
@staticmethod
|
||||
def compute_save_dir(cfg: Config) -> Optional[str]:
|
||||
"""Computes a "save directory" when constructing a config from CLI wav files."""
|
||||
if cfg.master_audio:
|
||||
file_path = cfg.master_audio
|
||||
elif len(cfg.channels) > 0:
|
||||
file_path = cfg.channels[0].wav_path
|
||||
else:
|
||||
return None
|
||||
|
||||
# If file_path is "file.wav", we want to return "." .
|
||||
# os.path.dirname("file.wav") == ""
|
||||
# Path("file.wav").parent..str == "."
|
||||
dir = Path(file_path).parent
|
||||
return str(dir)
|
||||
|
||||
@property
|
||||
def cfg(self):
|
||||
|
|
|
@ -20,14 +20,17 @@ class FileName:
|
|||
|
||||
def _get_hist_name(
|
||||
func: Callable[..., Tuple[str, str]],
|
||||
history_dir: _gp.Ref[_gp.GlobalPrefs],
|
||||
parent: qw.QWidget,
|
||||
title: str,
|
||||
history_dir: _gp.Ref[_gp.GlobalPrefs],
|
||||
default_name: Optional[str],
|
||||
filters: List[str],
|
||||
) -> Optional[FileName]:
|
||||
"""Get file name, defaulting to history folder. If user accepts, update history."""
|
||||
|
||||
"""
|
||||
Get file name.
|
||||
Default folder is history folder, and `default_name`.folder is discarded.
|
||||
If user accepts, update history.
|
||||
"""
|
||||
# Get recently used dir.
|
||||
dir_or_file: str = history_dir.get()
|
||||
if default_name:
|
||||
|
@ -56,13 +59,13 @@ def _get_hist_name(
|
|||
|
||||
|
||||
def get_open_file_name(
|
||||
history_dir: _gp.Ref[_gp.GlobalPrefs],
|
||||
parent: qw.QWidget,
|
||||
title: str,
|
||||
history_dir: _gp.Ref[_gp.GlobalPrefs],
|
||||
filters: List[str],
|
||||
) -> Optional[str]:
|
||||
name = _get_hist_name(
|
||||
qw.QFileDialog.getOpenFileName, history_dir, parent, title, None, filters
|
||||
qw.QFileDialog.getOpenFileName, parent, title, history_dir, None, filters
|
||||
)
|
||||
if name:
|
||||
assert name.file_name is not None
|
||||
|
@ -71,13 +74,13 @@ def get_open_file_name(
|
|||
|
||||
|
||||
def get_open_file_list(
|
||||
history_dir: _gp.Ref[_gp.GlobalPrefs],
|
||||
parent: qw.QWidget,
|
||||
title: str,
|
||||
history_dir: _gp.Ref[_gp.GlobalPrefs],
|
||||
filters: List[str],
|
||||
) -> Optional[List[str]]:
|
||||
name = _get_hist_name(
|
||||
qw.QFileDialog.getOpenFileNames, history_dir, parent, title, None, filters
|
||||
qw.QFileDialog.getOpenFileNames, parent, title, history_dir, None, filters
|
||||
)
|
||||
if name:
|
||||
assert name.file_list is not None
|
||||
|
@ -87,18 +90,18 @@ def get_open_file_list(
|
|||
|
||||
# Returns Path for legacy reasons. Others return str.
|
||||
def get_save_file_path(
|
||||
history_dir: _gp.Ref[_gp.GlobalPrefs],
|
||||
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,
|
||||
history_dir,
|
||||
parent,
|
||||
title,
|
||||
history_dir,
|
||||
default_name,
|
||||
filters,
|
||||
)
|
||||
|
|
|
@ -142,7 +142,7 @@ def test_load_yaml_another_dir(mocker, Popen):
|
|||
|
||||
# Issue: this test does not use cli.main() to compute output path.
|
||||
# Possible solution: Call cli.main() via Click runner.
|
||||
output = FFmpegOutputConfig(cli.get_path(cfg.master_audio, cli.VIDEO_NAME))
|
||||
output = FFmpegOutputConfig(cli._get_file_name(None, cfg, cli.VIDEO_NAME))
|
||||
corr = CorrScope(cfg, Arguments(subdir, [output]))
|
||||
corr.play()
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue