add/edit tag to ui
This commit is contained in:
142
bin/ffx/tag_delete_screen.py
Normal file
142
bin/ffx/tag_delete_screen.py
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
import click
|
||||||
|
|
||||||
|
from textual import events
|
||||||
|
from textual.app import App, ComposeResult
|
||||||
|
from textual.screen import Screen
|
||||||
|
from textual.widgets import Header, Footer, Placeholder, Label, ListView, ListItem, Static, DataTable, Button, Input
|
||||||
|
from textual.containers import Grid, Horizontal
|
||||||
|
|
||||||
|
from ffx.model.pattern import Pattern
|
||||||
|
from ffx.track_descriptor import TrackDescriptor
|
||||||
|
|
||||||
|
# from .show_controller import ShowController
|
||||||
|
# from .pattern_controller import PatternController
|
||||||
|
from .track_controller import TrackController
|
||||||
|
|
||||||
|
from .track_type import TrackType
|
||||||
|
|
||||||
|
# Screen[dict[int, str, int]]
|
||||||
|
class TrackDeleteScreen(Screen):
|
||||||
|
|
||||||
|
CSS = """
|
||||||
|
|
||||||
|
Grid {
|
||||||
|
grid-size: 4 9;
|
||||||
|
grid-rows: 2 2 2 2 2 2 2 2 2;
|
||||||
|
grid-columns: 30 30 30 30;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
padding: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Input {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
#toplabel {
|
||||||
|
height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.two {
|
||||||
|
column-span: 2;
|
||||||
|
}
|
||||||
|
.three {
|
||||||
|
column-span: 3;
|
||||||
|
}
|
||||||
|
.four {
|
||||||
|
column-span: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box {
|
||||||
|
height: 100%;
|
||||||
|
border: solid green;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, trackDescriptor : TrackDescriptor):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.context = self.app.getContext()
|
||||||
|
self.Session = self.context['database']['session'] # convenience
|
||||||
|
|
||||||
|
if type(trackDescriptor) is not TrackDescriptor:
|
||||||
|
raise click.ClickException('TrackDeleteScreen.init(): trackDescriptor is required to be of type TrackDescriptor')
|
||||||
|
|
||||||
|
self.__tc = TrackController(context = self.context)
|
||||||
|
|
||||||
|
self.__trackDescriptor = trackDescriptor
|
||||||
|
|
||||||
|
|
||||||
|
def on_mount(self):
|
||||||
|
|
||||||
|
self.query_one("#subindexlabel", Static).update(str(self.__trackDescriptor.getSubIndex()))
|
||||||
|
self.query_one("#patternlabel", Static).update(str(self.__trackDescriptor.getPatternId()))
|
||||||
|
self.query_one("#languagelabel", Static).update(str(self.__trackDescriptor.getLanguage().label()))
|
||||||
|
self.query_one("#titlelabel", Static).update(str(str(self.__trackDescriptor.getTitle())))
|
||||||
|
|
||||||
|
|
||||||
|
def compose(self):
|
||||||
|
|
||||||
|
yield Header()
|
||||||
|
|
||||||
|
with Grid():
|
||||||
|
|
||||||
|
#1
|
||||||
|
yield Static(f"Are you sure to delete the following {self.__trackDescriptor.getType().label()} track?", id="toplabel", classes="four")
|
||||||
|
|
||||||
|
#2
|
||||||
|
yield Static("sub index")
|
||||||
|
yield Static(" ", id="subindexlabel", classes="three")
|
||||||
|
|
||||||
|
#3
|
||||||
|
yield Static("from pattern")
|
||||||
|
yield Static(" ", id="patternlabel", classes="three")
|
||||||
|
|
||||||
|
#4
|
||||||
|
yield Static(" ", classes="four")
|
||||||
|
|
||||||
|
#5
|
||||||
|
yield Static("Language")
|
||||||
|
yield Static(" ", id="languagelabel", classes="three")
|
||||||
|
|
||||||
|
#6
|
||||||
|
yield Static("Title")
|
||||||
|
yield Static(" ", id="titlelabel", classes="three")
|
||||||
|
|
||||||
|
#7
|
||||||
|
yield Static(" ", classes="four")
|
||||||
|
|
||||||
|
#8
|
||||||
|
yield Static(" ", classes="four")
|
||||||
|
|
||||||
|
#9
|
||||||
|
yield Button("Delete", id="delete_button")
|
||||||
|
yield Button("Cancel", id="cancel_button")
|
||||||
|
|
||||||
|
yield Footer()
|
||||||
|
|
||||||
|
|
||||||
|
# Event handler for button press
|
||||||
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||||
|
|
||||||
|
if event.button.id == "delete_button":
|
||||||
|
|
||||||
|
track = self.__tc.findTrack(self.__trackDescriptor.getPatternId(), self.__trackDescriptor.getType(), self.__trackDescriptor.getSubIndex())
|
||||||
|
|
||||||
|
if track is None:
|
||||||
|
raise click.ClickException(f"Track is none: patternId={self.__trackDescriptor.getPatternId()} type={self.__trackDescriptor.getType()} subIndex={self.__trackDescriptor.getSubIndex()}")
|
||||||
|
|
||||||
|
if track is not None:
|
||||||
|
|
||||||
|
if self.__tc.deleteTrack(track.getId()):
|
||||||
|
self.dismiss(self.__trackDescriptor)
|
||||||
|
|
||||||
|
else:
|
||||||
|
#TODO: Meldung
|
||||||
|
self.app.pop_screen()
|
||||||
|
|
||||||
|
if event.button.id == "cancel_button":
|
||||||
|
self.app.pop_screen()
|
||||||
|
|
||||||
125
bin/ffx/tag_details_screen.py
Normal file
125
bin/ffx/tag_details_screen.py
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
import click, time
|
||||||
|
|
||||||
|
from textual import events
|
||||||
|
from textual.app import App, ComposeResult
|
||||||
|
from textual.screen import Screen
|
||||||
|
from textual.widgets import Header, Footer, Placeholder, Label, ListView, ListItem, Static, DataTable, Button, Input, Checkbox, SelectionList, Select
|
||||||
|
from textual.containers import Grid, Horizontal
|
||||||
|
|
||||||
|
# Screen[dict[int, str, int]]
|
||||||
|
class TagDetailsScreen(Screen):
|
||||||
|
|
||||||
|
CSS = """
|
||||||
|
|
||||||
|
Grid {
|
||||||
|
grid-size: 5 20;
|
||||||
|
grid-rows: 2 2 2 2 2 3 2 2 2 2 2 6 2 2 6 2 2 2 2 6;
|
||||||
|
grid-columns: 25 25 25 25 225;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
padding: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Input {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
SelectionList {
|
||||||
|
border: none;
|
||||||
|
min-height: 6;
|
||||||
|
}
|
||||||
|
Select {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
DataTable {
|
||||||
|
min-height: 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toplabel {
|
||||||
|
height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.two {
|
||||||
|
column-span: 2;
|
||||||
|
}
|
||||||
|
.three {
|
||||||
|
column-span: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.four {
|
||||||
|
column-span: 4;
|
||||||
|
}
|
||||||
|
.five {
|
||||||
|
column-span: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box {
|
||||||
|
height: 100%;
|
||||||
|
border: solid green;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, key=None, value=None):
|
||||||
|
super().__init__()
|
||||||
|
self.__key = key
|
||||||
|
self.__value = value
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def on_mount(self):
|
||||||
|
|
||||||
|
if self.__key is not None:
|
||||||
|
self.query_one("#key_input", Input).value = str(self.__key)
|
||||||
|
|
||||||
|
if self.__value is not None:
|
||||||
|
self.query_one("#value_input", Input).value = str(self.__value)
|
||||||
|
|
||||||
|
|
||||||
|
def compose(self):
|
||||||
|
|
||||||
|
yield Header()
|
||||||
|
|
||||||
|
with Grid():
|
||||||
|
|
||||||
|
# 8
|
||||||
|
yield Static("Key")
|
||||||
|
yield Input(id="key_input", classes="four")
|
||||||
|
|
||||||
|
yield Static("Value")
|
||||||
|
yield Input(id="value_input", classes="four")
|
||||||
|
|
||||||
|
# 17
|
||||||
|
yield Static(" ", classes="five")
|
||||||
|
|
||||||
|
# 18
|
||||||
|
yield Button("Save", id="save_button")
|
||||||
|
yield Button("Cancel", id="cancel_button")
|
||||||
|
|
||||||
|
# 19
|
||||||
|
yield Static(" ", classes="five")
|
||||||
|
|
||||||
|
# 20
|
||||||
|
yield Static(" ", classes="five", id="messagestatic")
|
||||||
|
|
||||||
|
yield Footer(id="footer")
|
||||||
|
|
||||||
|
|
||||||
|
def getTagFromInput(self):
|
||||||
|
|
||||||
|
tagKey = self.query_one("#key_input", Input).value
|
||||||
|
tagValue = self.query_one("#value_input", Input).value
|
||||||
|
|
||||||
|
return (tagKey, tagValue)
|
||||||
|
|
||||||
|
|
||||||
|
# Event handler for button press
|
||||||
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||||
|
|
||||||
|
# Check if the button pressed is the one we are interested in
|
||||||
|
if event.button.id == "save_button":
|
||||||
|
self.dismiss(self.getTagFromInput())
|
||||||
|
|
||||||
|
if event.button.id == "cancel_button":
|
||||||
|
self.app.pop_screen()
|
||||||
@@ -5,7 +5,7 @@ from textual.app import App, ComposeResult
|
|||||||
from textual.screen import Screen
|
from textual.screen import Screen
|
||||||
from textual.widgets import Header, Footer, Placeholder, Label, ListView, ListItem, Static, DataTable, Button, Input, Checkbox, SelectionList, Select
|
from textual.widgets import Header, Footer, Placeholder, Label, ListView, ListItem, Static, DataTable, Button, Input, Checkbox, SelectionList, Select
|
||||||
from textual.containers import Grid, Horizontal
|
from textual.containers import Grid, Horizontal
|
||||||
|
from textual.coordinate import Coordinate
|
||||||
|
|
||||||
from ffx.model.show import Show
|
from ffx.model.show import Show
|
||||||
from ffx.model.pattern import Pattern
|
from ffx.model.pattern import Pattern
|
||||||
@@ -22,6 +22,76 @@ from .audio_layout import AudioLayout
|
|||||||
|
|
||||||
from .track_descriptor import TrackDescriptor
|
from .track_descriptor import TrackDescriptor
|
||||||
|
|
||||||
|
from .tag_details_screen import TagDetailsScreen
|
||||||
|
|
||||||
|
from textual.widgets._data_table import CellDoesNotExist
|
||||||
|
|
||||||
|
|
||||||
|
class EditableDataTable(DataTable):
|
||||||
|
"""Custom DataTable that allows editing cells upon mouse click."""
|
||||||
|
|
||||||
|
def on_click(self, event: events.Click) -> None:
|
||||||
|
"""Handle mouse clicks on the table."""
|
||||||
|
|
||||||
|
# raise click.ClickException(f"event={event}")
|
||||||
|
|
||||||
|
#event.x und event.y sind koordinaten im Datatable
|
||||||
|
|
||||||
|
#clicked_cell = self.coordinate_to_cell_key(self.cursor_coordinate)
|
||||||
|
#raise click.ClickException(f"x={event.x} y={event.y} clicked_cell={clicked_cell}")
|
||||||
|
|
||||||
|
|
||||||
|
#raise click.ClickException(f"cuco={self.cursor_coordinate}")
|
||||||
|
|
||||||
|
#clickCoordinates = Coordinate(row = event.y, column=event.x)
|
||||||
|
|
||||||
|
#clicked_cell = self.get_cell_at(event.x, event.y)
|
||||||
|
#row_key, col_key = self.coordinate_to_cell_key(self.cursor_coordinate)
|
||||||
|
|
||||||
|
#raise click.ClickException(f"x={event.x} y={event.y} row={row_key} col={col_key}")
|
||||||
|
|
||||||
|
|
||||||
|
#if clicked_cell:
|
||||||
|
# if row_key is not None and col_key is not None:
|
||||||
|
#row, column = clicked_cell
|
||||||
|
# Get the current cell value and activate editing.
|
||||||
|
#current_value = self.get_cell(row_key, col_key)
|
||||||
|
current_value = self.get_cell_at(self.cursor_coordinate)
|
||||||
|
|
||||||
|
region = self._get_cell_region(self.cursor_coordinate)
|
||||||
|
|
||||||
|
raise click.ClickException(f"region={region}")
|
||||||
|
|
||||||
|
#current_value = self.get_cell(row_key, col_key)
|
||||||
|
#self.activate_cell_edit(row_key, col_key, current_value)
|
||||||
|
|
||||||
|
raise click.ClickException(f"val={current_value}")
|
||||||
|
|
||||||
|
def activate_cell_edit(self, row: int, column: int, value: str) -> None:
|
||||||
|
# """Method to activate cell editing."""
|
||||||
|
# # Create a new input box with the value of the cell to be edited.
|
||||||
|
input_box = Input(value=value, classes="cell-editor")
|
||||||
|
input_box.styles.width = len(value) + 2 # Adjust the width of the input box
|
||||||
|
input_box.styles.padding = (0, 1)
|
||||||
|
#
|
||||||
|
# # Position the input box over the cell to be edited.
|
||||||
|
input_box.styles.position = "absolute"
|
||||||
|
input_box.styles.left = self.get_cell_position(row, column)[0]
|
||||||
|
input_box.styles.top = self.get_cell_position(row, column)[1]
|
||||||
|
|
||||||
|
# # Add the input box to the parent widget for editing.
|
||||||
|
# self.app.mount(input_box)
|
||||||
|
# input_box.focus()
|
||||||
|
#
|
||||||
|
# # Listen to when the user completes editing.
|
||||||
|
# input_box.on_blur = lambda event: self.save_cell_value(row, column, input_box.value)
|
||||||
|
#
|
||||||
|
# def save_cell_value(self, row: int, column: int, value: str) -> None:
|
||||||
|
# """Save the new value to the table and remove the input box."""
|
||||||
|
# self.update_cell(row, column, value)
|
||||||
|
# self.app.query_one(".cell-editor").remove() # Remove the input box after saving.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Screen[dict[int, str, int]]
|
# Screen[dict[int, str, int]]
|
||||||
class TrackDetailsScreen(Screen):
|
class TrackDetailsScreen(Screen):
|
||||||
@@ -87,23 +157,6 @@ class TrackDetailsScreen(Screen):
|
|||||||
self.__tc = TrackController(context = self.context)
|
self.__tc = TrackController(context = self.context)
|
||||||
self.__pc = PatternController(context = self.context)
|
self.__pc = PatternController(context = self.context)
|
||||||
|
|
||||||
|
|
||||||
INDEX_KEY = 'index'
|
|
||||||
SUB_INDEX_KEY = 'sub_index'
|
|
||||||
PATTERN_ID_KEY = 'pattern_id'
|
|
||||||
|
|
||||||
TRACK_TYPE_KEY = 'track_type'
|
|
||||||
DISPOSITION_SET_KEY = 'disposition_set'
|
|
||||||
TAGS_KEY = 'tags'
|
|
||||||
AUDIO_LAYOUT_KEY = 'audio_layout'
|
|
||||||
|
|
||||||
# if trackDescriptor is None:
|
|
||||||
# self.__trackDescriptor = TrackDescriptor(index=,
|
|
||||||
# sub_index=
|
|
||||||
# pattern_id=patternId,
|
|
||||||
# track_type=trackType)
|
|
||||||
# else:
|
|
||||||
|
|
||||||
self.__isNew = trackDescriptor is None
|
self.__isNew = trackDescriptor is None
|
||||||
if self.__isNew:
|
if self.__isNew:
|
||||||
self.__trackType = trackType
|
self.__trackType = trackType
|
||||||
@@ -144,7 +197,7 @@ class TrackDetailsScreen(Screen):
|
|||||||
|
|
||||||
def compose(self):
|
def compose(self):
|
||||||
|
|
||||||
self.trackTagsTable = DataTable(classes="five")
|
self.trackTagsTable = EditableDataTable(classes="five")
|
||||||
|
|
||||||
# Define the columns with headers
|
# Define the columns with headers
|
||||||
self.column_key_track_tag_key = self.trackTagsTable.add_column("Key", width=10)
|
self.column_key_track_tag_key = self.trackTagsTable.add_column("Key", width=10)
|
||||||
@@ -193,8 +246,9 @@ class TrackDetailsScreen(Screen):
|
|||||||
|
|
||||||
# 11
|
# 11
|
||||||
yield Static("Stream tags")
|
yield Static("Stream tags")
|
||||||
yield Static(" ", classes="two")
|
yield Static(" ")
|
||||||
yield Button("Add", id="button_add_stream_tag")
|
yield Button("Add", id="button_add_stream_tag")
|
||||||
|
yield Button("Edit", id="button_edit_stream_tag")
|
||||||
yield Button("Delete", id="button_delete_stream_tag")
|
yield Button("Delete", id="button_delete_stream_tag")
|
||||||
# 12
|
# 12
|
||||||
yield self.trackTagsTable
|
yield self.trackTagsTable
|
||||||
@@ -259,6 +313,31 @@ class TrackDetailsScreen(Screen):
|
|||||||
return TrackDescriptor(**kwargs)
|
return TrackDescriptor(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def getSelectedTag(self):
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
# Fetch the currently selected row when 'Enter' is pressed
|
||||||
|
#selected_row_index = self.table.cursor_row
|
||||||
|
row_key, col_key = self.trackTagsTable.coordinate_to_cell_key(self.trackTagsTable.cursor_coordinate)
|
||||||
|
|
||||||
|
if row_key is not None:
|
||||||
|
selected_tag_data = self.trackTagsTable.get_row(row_key)
|
||||||
|
|
||||||
|
tagKey = str(selected_tag_data[0])
|
||||||
|
tagValue = str(selected_tag_data[1])
|
||||||
|
|
||||||
|
return tagKey, tagValue
|
||||||
|
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
except CellDoesNotExist:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Event handler for button press
|
# Event handler for button press
|
||||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||||
|
|
||||||
@@ -308,3 +387,64 @@ class TrackDetailsScreen(Screen):
|
|||||||
|
|
||||||
if event.button.id == "cancel_button":
|
if event.button.id == "cancel_button":
|
||||||
self.app.pop_screen()
|
self.app.pop_screen()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if event.button.id == "button_add_stream_tag":
|
||||||
|
self.app.push_screen(TagDetailsScreen(), self.handle_add_tag)
|
||||||
|
|
||||||
|
#row = ('<key>','<value>')
|
||||||
|
#self.trackTagsTable.add_row(*map(str, row))
|
||||||
|
|
||||||
|
if event.button.id == "button_edit_stream_tag":
|
||||||
|
tagKey, tagValue = self.getSelectedTag()
|
||||||
|
self.app.push_screen(TagDetailsScreen(key=tagKey, value=tagValue), self.handle_add_tag)
|
||||||
|
|
||||||
|
if event.button.id == "button_delete_stream_tag":
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def handle_add_tag(self, tag):
|
||||||
|
|
||||||
|
row = (tag[0], tag[1])
|
||||||
|
self.trackTagsTable.add_row(*map(str, row))
|
||||||
|
|
||||||
|
|
||||||
|
def handle_edit_tag(self, trackDescriptor : TrackDescriptor):
|
||||||
|
pass
|
||||||
|
# try:
|
||||||
|
# if trackDescriptor.getType() == TrackType.AUDIO:
|
||||||
|
#
|
||||||
|
# row_key, col_key = self.audioStreamsTable.coordinate_to_cell_key(self.audioStreamsTable.cursor_coordinate)
|
||||||
|
#
|
||||||
|
# self.audioStreamsTable.update_cell(row_key, self.column_key_audio_language, trackDescriptor.getLanguage().label())
|
||||||
|
# self.audioStreamsTable.update_cell(row_key, self.column_key_audio_title, trackDescriptor.getTitle())
|
||||||
|
# self.audioStreamsTable.update_cell(row_key, self.column_key_audio_default, 'Yes' if TrackDisposition.DEFAULT in trackDescriptor.getDispositionSet() else 'No')
|
||||||
|
# self.audioStreamsTable.update_cell(row_key, self.column_key_audio_forced, 'Yes' if TrackDisposition.FORCED in trackDescriptor.getDispositionSet() else 'No')
|
||||||
|
#
|
||||||
|
# if trackDescriptor.getType() == TrackType.SUBTITLE:
|
||||||
|
#
|
||||||
|
# row_key, col_key = self.subtitleStreamsTable.coordinate_to_cell_key(self.subtitleStreamsTable.cursor_coordinate)
|
||||||
|
#
|
||||||
|
# self.subtitleStreamsTable.update_cell(row_key, self.column_key_subtitle_language, trackDescriptor.getLanguage().label())
|
||||||
|
# self.subtitleStreamsTable.update_cell(row_key, self.column_key_subtitle_title, trackDescriptor.getTitle())
|
||||||
|
# self.subtitleStreamsTable.update_cell(row_key, self.column_key_subtitle_default, 'Yes' if TrackDisposition.DEFAULT in trackDescriptor.getDispositionSet() else 'No')
|
||||||
|
# self.subtitleStreamsTable.update_cell(row_key, self.column_key_subtitle_forced, 'Yes' if TrackDisposition.FORCED in trackDescriptor.getDispositionSet() else 'No')
|
||||||
|
#
|
||||||
|
# except CellDoesNotExist:
|
||||||
|
# pass
|
||||||
|
|
||||||
|
|
||||||
|
def handle_delete_tag(self, trackDescriptor : TrackDescriptor):
|
||||||
|
pass
|
||||||
|
# try:
|
||||||
|
# if trackDescriptor.getType() == TrackType.AUDIO:
|
||||||
|
# self.updateAudioTracks()
|
||||||
|
#
|
||||||
|
# if trackDescriptor.getType() == TrackType.SUBTITLE:
|
||||||
|
# self.updateSubtitleTracks()
|
||||||
|
#
|
||||||
|
# except CellDoesNotExist:
|
||||||
|
# pass
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user