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
nyanpasu64 2019-04-18 12:20:42 -07:00 zatwierdzone przez GitHub
rodzic 33c3fdba1d
commit fa105f1bf5
4 zmienionych plików z 122 dodań i 42 usunięć

Wyświetl plik

@ -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)

Wyświetl plik

@ -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):

Wyświetl plik

@ -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,
)

Wyświetl plik

@ -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()