corrscope/corrscope/gui/view_mainwindow.py

480 wiersze
18 KiB
Python

# -*- coding: utf-8 -*-
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from corrscope.gui.view_stack import (
LayoutStack,
set_layout,
central_widget,
append_widget,
add_row,
add_tab,
set_attr_objectName,
append_stretch,
add_grid_col,
Both,
set_menu_bar,
append_menu,
add_toolbar,
)
NBSP = "\xa0"
class HLine(QFrame):
def __init__(self, parent):
super(HLine, self).__init__(parent)
self.setFrameShape(QFrame.HLine)
self.setFrameShadow(QFrame.Sunken)
class VLine(QFrame):
def __init__(self, parent):
super(VLine, self).__init__(parent)
self.setFrameShape(QFrame.VLine)
self.setFrameShadow(QFrame.Sunken)
def fixed_size_policy():
return QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
# noinspection PyAttributeOutsideInit
class MainWindow(QWidget):
@staticmethod
def tr(*args, **kwargs) -> str:
"""Only at runtime, not at pylupdate5 time."""
# noinspection PyCallByClass,PyTypeChecker
return QCoreApplication.translate("MainWindow", *args, **kwargs)
left_tabs: QTabWidget
def setupUi(self, MainWindow: QMainWindow):
MainWindow.resize(1160, 0)
s = LayoutStack(MainWindow)
# Window contents
with central_widget(s, QWidget) as self.centralWidget:
horizontalLayout = set_layout(s, QHBoxLayout)
# Left-hand config tabs
with append_widget(s, QTabWidget) as self.left_tabs:
self.tabGeneral = self.add_general_tab(s)
self.tabStereo = self.add_stereo_tab(s)
self.tabPerf = self.add_performance_tab(s)
# Right-hand channel list
with append_widget(s, QVBoxLayout) as self.audioColumn:
# Top bar (master audio, trigger)
self.add_top_bar(s)
# Channel list (group box)
self.channelsGroup = self.add_channels_list(s)
# Right-hand channel list expands to fill space.
horizontalLayout.setStretch(1, 1)
self.add_actions(s, MainWindow)
# Creates references to labels
set_attr_objectName(self, s)
# Initializes labels by reference
self.retranslateUi(MainWindow)
# Depends on objectName
QMetaObject.connectSlotsByName(MainWindow)
def add_general_tab(self, s: LayoutStack) -> QWidget:
tr = self.tr
with self._add_tab(s, tr("&General")) as tab:
set_layout(s, QVBoxLayout)
# Global group
with append_widget(s, QGroupBox) as self.optionGlobal:
set_layout(s, QFormLayout)
with add_row(s, "", BoundSpinBox) as self.fps:
self.fps.setMinimum(1)
self.fps.setMaximum(999)
self.fps.setSingleStep(10)
with add_row(s, "", BoundSpinBox) as self.trigger_ms:
self.trigger_ms.setMinimum(5)
self.trigger_ms.setSingleStep(5)
with add_row(s, "", BoundSpinBox) as self.render_ms:
self.render_ms.setMinimum(5)
self.render_ms.setSingleStep(5)
with add_row(s, "", BoundDoubleSpinBox) as self.amplification:
self.amplification.setSingleStep(0.1)
with add_row(s, "", BoundDoubleSpinBox) as self.begin_time:
self.begin_time.setMaximum(9999.0)
with append_widget(s, QGroupBox) as self.optionAppearance:
set_layout(s, QFormLayout)
with add_row(s, "", BoundLineEdit) as self.render_resolution:
pass
with add_row(s, "", BoundColorWidget) as self.render__bg_color:
pass
with add_row(s, "", BoundColorWidget) as self.render__init_line_color:
pass
with add_row(s, "", BoundDoubleSpinBox) as self.render__line_width:
self.render__line_width.setMinimum(0.5)
self.render__line_width.setSingleStep(0.5)
with add_row(s, "", OptionalColorWidget) as self.render__grid_color:
pass
with add_row(s, "", OptionalColorWidget) as self.render__midline_color:
pass
with add_row(s, BoundCheckBox, BoundCheckBox) as (
self.render__v_midline,
self.render__h_midline,
):
pass
with append_widget(s, QGroupBox) as self.optionLayout:
set_layout(s, QFormLayout)
with add_row(s, "", BoundComboBox) as self.layout__orientation:
pass
with add_row(s, tr("Columns"), QHBoxLayout) as self.layoutDims:
with append_widget(s, BoundSpinBox) as self.layout__ncols:
self.layout__ncols.setSpecialValueText(NBSP)
with append_widget(s, QLabel) as self.layout__nrowsL:
pass
with append_widget(s, BoundSpinBox) as self.layout__nrows:
self.layout__nrows.setSpecialValueText(NBSP)
append_stretch(s)
return tab
def add_stereo_tab(self, s: LayoutStack) -> QWidget:
tr = self.tr
with self._add_tab(s, tr("&Stereo")) as tab:
set_layout(s, QVBoxLayout)
with append_widget(s, QGroupBox) as self.optionStereo:
set_layout(s, QFormLayout)
with add_row(s, "", BoundComboBox) as self.trigger_stereo:
pass
with add_row(s, "", BoundComboBox) as self.render_stereo:
pass
with append_widget(s, QGroupBox) as self.dockStereo_2:
set_layout(s, QFormLayout)
with add_row(s, "", BoundComboBox) as self.layout__stereo_orientation:
pass
with add_row(s, "", BoundDoubleSpinBox) as (
self.render__stereo_grid_opacity
):
self.render__stereo_grid_opacity.setMaximum(1.0)
self.render__stereo_grid_opacity.setSingleStep(0.25)
append_stretch(s)
return tab
def add_performance_tab(self, s: LayoutStack) -> QWidget:
tr = self.tr
with self._add_tab(s, tr("&Performance")) as tab:
set_layout(s, QVBoxLayout)
with append_widget(s, QGroupBox) as self.perfPreview:
set_layout(s, QFormLayout)
with add_row(s, "", BoundSpinBox) as self.trigger_subsampling:
self.trigger_subsampling.setMinimum(1)
with add_row(s, "", BoundSpinBox) as self.render_subsampling:
self.render_subsampling.setMinimum(1)
with add_row(s, "", BoundSpinBox) as self.render_subfps:
self.render_subfps.setMinimum(1)
with add_row(s, "", BoundDoubleSpinBox) as self.render__res_divisor:
self.render__res_divisor.setMinimum(1.0)
self.render__res_divisor.setSingleStep(0.5)
append_stretch(s)
return tab
@staticmethod
def _add_tab(s: LayoutStack, label: str = ""):
return add_tab(s, QWidget, label)
def add_top_bar(self, s):
tr = self.tr
with append_widget(s, QHBoxLayout):
with append_widget(s, QVBoxLayout):
with append_widget(s, QGroupBox):
s.widget.setTitle(tr("FFmpeg Options"))
set_layout(s, QFormLayout)
# Master audio
with add_row(s, tr("Master Audio"), QHBoxLayout):
with append_widget(s, BoundLineEdit) as self.master_audio:
pass
with append_widget(s, QPushButton) as self.master_audio_browse:
pass
with add_row(
s, tr("Video Template"), BoundLineEdit
) as self.ffmpeg_cli__video_template:
pass
with add_row(
s, tr("Audio Template"), BoundLineEdit
) as self.ffmpeg_cli__audio_template:
pass
# Trigger config
with append_widget(s, QVBoxLayout):
with append_widget(s, QHBoxLayout):
with append_widget(
s, QGroupBox, title=tr("Wave Alignment"), layout=QGridLayout
):
with add_grid_col(
s,
tr("Buffer Strength"),
BoundDoubleSpinBox,
name="trigger__buffer_strength",
):
pass
with add_grid_col(
s,
tr("Buffer Responsiveness"),
BoundDoubleSpinBox,
name="trigger__responsiveness",
maximum=1.0,
singleStep=0.1,
):
pass
with add_grid_col(
s,
tr("Mean Responsiveness"),
BoundDoubleSpinBox,
name="trigger__mean_responsiveness",
) as w: # type: BoundDoubleSpinBox
w.setMaximum(1.0)
w.setSingleStep(0.1)
with add_grid_col(s, BoundCheckBox, Both) as (
self.trigger__pitch_tracking
):
assert isinstance(self.trigger__pitch_tracking, QWidget)
with append_widget(
s, QGroupBox, title=tr("Edge Search"), layout=QGridLayout
):
with add_grid_col(s, "", BoundComboBox) as (
self.trigger__edge_direction
):
pass
with add_grid_col(s, "", BoundDoubleSpinBox) as (
self.trigger__edge_strength
):
self.trigger__edge_strength.setMinimum(0.0)
with append_widget(s, QHBoxLayout):
with append_widget(
s, QGroupBox, title=tr("Slope Search"), layout=QGridLayout
):
with add_grid_col(
s,
tr("Slope Strength"),
BoundDoubleSpinBox,
name="trigger__slope_strength",
):
s.widget.setSingleStep(10)
s.widget.setMaximum(200)
with add_grid_col(
s,
tr("Slope Width"),
BoundDoubleSpinBox,
name="trigger__slope_width",
):
s.widget.setMinimum(0)
s.widget.setMaximum(0.5)
s.widget.setSingleStep(0.02)
with append_widget(
s, QGroupBox, title=tr("Post Triggering"), layout=QGridLayout
):
with add_grid_col(
s, tr("Post Trigger"), TypeComboBox
) as self.trigger__post_trigger:
pass
with add_grid_col(
s, tr("Post Trigger Radius"), BoundSpinBox
) as self.trigger__post_radius:
pass
# self.trigger__post_radius: BoundSpinBox
# self.trigger__post_radius.setMinimum(0)
def add_channels_list(self, s):
tr = self.tr
with append_widget(s, QGroupBox) as group:
s.widget.setTitle(tr("Oscilloscope Channels"))
set_layout(s, QVBoxLayout)
# Button toolbar
with append_widget(s, QHBoxLayout) as self.channelBar:
append_stretch(s)
with append_widget(s, ShortcutButton) as self.channelAdd:
pass
with append_widget(s, ShortcutButton) as self.channelDelete:
pass
with append_widget(s, ShortcutButton) as self.channelUp:
pass
with append_widget(s, ShortcutButton) as self.channelDown:
pass
# Spreadsheet grid
with append_widget(s, ChannelTableView) as self.channel_view:
pass
return group
def add_actions(self, s: LayoutStack, MainWindow):
tr = self.tr
# Setup actions
self.actionOpen = QAction(MainWindow)
self.actionSave = QAction(MainWindow)
self.actionNew = QAction(MainWindow)
self.actionSaveAs = QAction(MainWindow)
self.actionExit = QAction(MainWindow)
self.actionPreview = QAction(MainWindow)
self.actionRender = QAction(MainWindow)
self.action_separate_render_dir = QAction(MainWindow)
self.action_separate_render_dir.setCheckable(True)
# Setup menu_bar
assert s.widget is MainWindow
with set_menu_bar(s) as self.menuBar:
with append_menu(s) as self.menuFile:
self.menuFile.addAction(self.actionNew)
self.menuFile.addAction(self.actionOpen)
self.menuFile.addAction(self.actionSave)
self.menuFile.addAction(self.actionSaveAs)
self.menuFile.addSeparator()
self.menuFile.addAction(self.actionPreview)
self.menuFile.addAction(self.actionRender)
self.menuFile.addSeparator()
self.menuFile.addAction(self.actionExit)
with append_menu(s) as self.menuTools:
self.menuTools.addAction(self.action_separate_render_dir)
# Setup toolbar
with add_toolbar(s, Qt.TopToolBarArea) as self.toolBar:
self.toolBar.addAction(self.actionNew)
self.toolBar.addAction(self.actionOpen)
self.toolBar.addAction(self.actionSave)
self.toolBar.addAction(self.actionSaveAs)
self.toolBar.addSeparator()
self.toolBar.addAction(self.actionPreview)
self.toolBar.addAction(self.actionRender)
# noinspection PyUnresolvedReferences
def retranslateUi(self, MainWindow):
tr = self.tr
MainWindow.setWindowTitle(tr("MainWindow"))
self.optionGlobal.setTitle(tr("Global"))
self.fpsL.setText(tr("FPS"))
self.trigger_msL.setText(tr("Trigger Width"))
self.render_msL.setText(tr("Render Width"))
self.amplificationL.setText(tr("Amplification"))
self.begin_timeL.setText(tr("Begin Time"))
self.optionAppearance.setTitle(tr("Appearance"))
self.render_resolutionL.setText(tr("Resolution"))
self.render_resolution.setText(tr("vs"))
self.render__bg_colorL.setText(tr("Background"))
self.render__init_line_colorL.setText(tr("Line Color"))
self.render__line_widthL.setText(tr("Line Width"))
self.render__grid_colorL.setText(tr("Grid Color"))
self.render__midline_colorL.setText(tr("Midline Color"))
self.render__v_midline.setText(tr("Vertical"))
self.render__h_midline.setText(tr("Horizontal Midline"))
self.optionLayout.setTitle(tr("Layout"))
self.layout__orientationL.setText(tr("Orientation"))
self.layout__nrowsL.setText(tr("Rows"))
self.optionStereo.setTitle(tr("Stereo Enable"))
self.trigger_stereoL.setText(tr("Trigger Stereo"))
self.render_stereoL.setText(tr("Render Stereo"))
self.dockStereo_2.setTitle(tr("Stereo Appearance"))
self.layout__stereo_orientationL.setText(tr("Stereo Orientation"))
self.render__stereo_grid_opacityL.setText(tr("Grid Opacity"))
self.perfPreview.setTitle(tr("Preview Only"))
self.trigger_subsamplingL.setText(tr("Trigger Subsampling"))
self.render_subsamplingL.setText(tr("Render Subsampling"))
self.render_subfpsL.setText(tr("Render FPS Divisor"))
self.render__res_divisorL.setText(tr("Resolution Divisor"))
self.master_audio.setText(tr("/"))
self.master_audio_browse.setText(tr("&Browse..."))
self.trigger__edge_strengthL.setText(tr("Edge Strength"))
self.trigger__pitch_tracking.setText(tr("Pitch Tracking"))
self.trigger__edge_directionL.setText(tr("Edge Direction"))
self.channelAdd.setText(tr("&Add..."))
self.channelDelete.setText(tr("&Delete"))
self.channelUp.setText(tr("Up"))
self.channelDown.setText(tr("Down"))
self.menuFile.setTitle(tr("&File"))
self.menuTools.setTitle(tr("&Tools"))
self.toolBar.setWindowTitle(tr("toolBar"))
self.actionOpen.setText(tr("&Open"))
self.actionOpen.setShortcut(tr("Ctrl+O"))
self.actionSave.setText(tr("&Save"))
self.actionSave.setShortcut(tr("Ctrl+S"))
self.actionNew.setText(tr("&New"))
self.actionNew.setShortcut(tr("Ctrl+N"))
self.actionSaveAs.setText(tr("Save &As"))
self.actionSaveAs.setShortcut(tr("Ctrl+Shift+S"))
self.actionExit.setText(tr("E&xit"))
self.actionExit.setShortcut(tr("Ctrl+Q"))
self.actionPreview.setText(tr("&Preview"))
self.actionPreview.setShortcut(tr("Ctrl+P"))
self.actionRender.setText(tr("&Render to Video"))
self.actionRender.setShortcut(tr("Ctrl+R"))
self.action_separate_render_dir.setText(tr("&Separate Render Folder"))
from corrscope.gui.__init__ import ChannelTableView, ShortcutButton
from corrscope.gui.model_bind import (
BoundLineEdit,
BoundSpinBox,
BoundDoubleSpinBox,
BoundCheckBox,
BoundComboBox,
TypeComboBox,
BoundColorWidget,
OptionalColorWidget,
)
# Delete unbound widgets, so they cannot accidentally be used.
del QLineEdit, QSpinBox, QDoubleSpinBox, QCheckBox, QComboBox