feat: Optimize properties panel layout to prevent horizontal scrolling
- Add smart path truncation for long file paths (preserves filename and parent folder) - Set maximum width constraints on all UI components (QPushButton, QComboBox, QSpinBox, QDoubleSpinBox, QLineEdit) - Add tooltips showing full paths for truncated file path buttons - Disable horizontal scrollbar and optimize right panel width (320-380px) - Improve styling for all property widgets with consistent theme - Add better placeholder text for input fields Key improvements: - File paths like "C:/Very/Long/Path/.../filename.nef" → "...Long/Path/filename.nef" - All widgets limited to 250px max width to prevent panel expansion - Enhanced hover and focus states for better UX - Properties panel now fits within fixed width without horizontal scroll 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
77bd8324ab
commit
a1b6af0bde
@ -361,10 +361,10 @@ class IntegratedPipelineDashboard(QMainWindow):
|
||||
# Middle: Pipeline Editor (50% width) - without its own status bar
|
||||
middle_panel = self.create_pipeline_editor_panel()
|
||||
|
||||
# Right side: Configuration panels (25% width)
|
||||
# Right side: Configuration panels (25% width) - optimized for no horizontal scroll
|
||||
right_panel = self.create_configuration_panel()
|
||||
right_panel.setMinimumWidth(300)
|
||||
right_panel.setMaximumWidth(400)
|
||||
right_panel.setMinimumWidth(320)
|
||||
right_panel.setMaximumWidth(380)
|
||||
|
||||
# Add widgets to splitter
|
||||
main_splitter.addWidget(left_panel)
|
||||
@ -795,8 +795,15 @@ class IntegratedPipelineDashboard(QMainWindow):
|
||||
def create_node_properties_panel(self) -> QWidget:
|
||||
"""Create node properties editing panel."""
|
||||
widget = QScrollArea()
|
||||
|
||||
# Configure scroll area to prevent horizontal scrolling
|
||||
widget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
widget.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
||||
widget.setWidgetResizable(True)
|
||||
|
||||
content = QWidget()
|
||||
layout = QVBoxLayout(content)
|
||||
layout.setContentsMargins(10, 10, 10, 10) # Add some padding
|
||||
|
||||
# Header
|
||||
header = QLabel("Node Properties")
|
||||
@ -1271,6 +1278,41 @@ class IntegratedPipelineDashboard(QMainWindow):
|
||||
|
||||
return widget
|
||||
|
||||
def truncate_path_smart(self, path: str, max_length: int = 35) -> str:
|
||||
"""
|
||||
Smart path truncation that preserves important parts.
|
||||
Shows: ...drive/important_folder/filename.ext
|
||||
"""
|
||||
if not path or len(path) <= max_length:
|
||||
return path
|
||||
|
||||
import os
|
||||
|
||||
# Split path into components
|
||||
drive, path_without_drive = os.path.splitdrive(path)
|
||||
path_parts = path_without_drive.replace('\\', '/').split('/')
|
||||
|
||||
if len(path_parts) <= 2:
|
||||
# Very short path, just truncate from start
|
||||
return '...' + path[-(max_length-3):]
|
||||
|
||||
filename = path_parts[-1] if path_parts[-1] else path_parts[-2]
|
||||
|
||||
# Always keep filename and one parent directory if possible
|
||||
if len(filename) > max_length - 10:
|
||||
# Filename itself is too long
|
||||
return '...' + filename[-(max_length-3):]
|
||||
|
||||
# Try to keep parent folder + filename
|
||||
parent_dir = path_parts[-2] if len(path_parts) >= 2 else ''
|
||||
short_end = f"/{parent_dir}/{filename}" if parent_dir else f"/{filename}"
|
||||
|
||||
if len(short_end) <= max_length - 3:
|
||||
return '...' + short_end
|
||||
else:
|
||||
# Just keep filename
|
||||
return '.../' + filename
|
||||
|
||||
def create_property_widget_enhanced(self, node, prop_name: str, prop_value):
|
||||
"""Create enhanced property widget with better type detection."""
|
||||
# Create widget based on property name and value
|
||||
@ -1284,9 +1326,35 @@ class IntegratedPipelineDashboard(QMainWindow):
|
||||
# Check for file path properties first (from prop_options or name pattern)
|
||||
if (prop_options and isinstance(prop_options, dict) and prop_options.get('type') == 'file_path') or \
|
||||
prop_name in ['model_path', 'source_path', 'destination']:
|
||||
# File path property with filters from prop_options or defaults
|
||||
widget = QPushButton(str(prop_value) if prop_value else 'Select File...')
|
||||
widget.setStyleSheet("text-align: left; padding: 5px;")
|
||||
# File path property with smart truncation and width limits
|
||||
display_text = self.truncate_path_smart(str(prop_value)) if prop_value else 'Select File...'
|
||||
widget = QPushButton(display_text)
|
||||
|
||||
# Set fixed width and styling to prevent expansion
|
||||
widget.setMaximumWidth(250) # Limit button width
|
||||
widget.setMinimumWidth(200)
|
||||
widget.setStyleSheet("""
|
||||
QPushButton {
|
||||
text-align: left;
|
||||
padding: 5px 8px;
|
||||
background-color: #45475a;
|
||||
color: #cdd6f4;
|
||||
border: 1px solid #585b70;
|
||||
border-radius: 4px;
|
||||
font-size: 10px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #585b70;
|
||||
border-color: #74c7ec;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #313244;
|
||||
}
|
||||
""")
|
||||
|
||||
# Store full path for tooltip and internal use
|
||||
full_path = str(prop_value) if prop_value else ''
|
||||
widget.setToolTip(f"Full path: {full_path}\n\nClick to browse for {prop_name.replace('_', ' ')}")
|
||||
|
||||
def browse_file():
|
||||
# Use filter from prop_options if available, otherwise use defaults
|
||||
@ -1295,7 +1363,9 @@ class IntegratedPipelineDashboard(QMainWindow):
|
||||
else:
|
||||
# Fallback to original filters
|
||||
filters = {
|
||||
'model_path': 'Model files (*.onnx *.tflite *.pb)',
|
||||
'model_path': 'NEF Model files (*.nef)',
|
||||
'scpu_fw_path': 'SCPU Firmware files (*.bin)',
|
||||
'ncpu_fw_path': 'NCPU Firmware files (*.bin)',
|
||||
'source_path': 'Media files (*.mp4 *.avi *.mov *.mkv *.wav *.mp3)',
|
||||
'destination': 'Output files (*.json *.xml *.csv *.txt)'
|
||||
}
|
||||
@ -1303,7 +1373,12 @@ class IntegratedPipelineDashboard(QMainWindow):
|
||||
|
||||
file_path, _ = QFileDialog.getOpenFileName(self, f'Select {prop_name}', '', file_filter)
|
||||
if file_path:
|
||||
widget.setText(file_path)
|
||||
# Update button text with truncated path
|
||||
truncated_text = self.truncate_path_smart(file_path)
|
||||
widget.setText(truncated_text)
|
||||
# Update tooltip with full path
|
||||
widget.setToolTip(f"Full path: {file_path}\n\nClick to browse for {prop_name.replace('_', ' ')}")
|
||||
# Set property with full path
|
||||
if hasattr(node, 'set_property'):
|
||||
node.set_property(prop_name, file_path)
|
||||
|
||||
@ -1312,9 +1387,43 @@ class IntegratedPipelineDashboard(QMainWindow):
|
||||
# Check for dropdown properties (list options from prop_options or predefined)
|
||||
elif (prop_options and isinstance(prop_options, list)) or \
|
||||
prop_name in ['source_type', 'dongle_series', 'output_format', 'format', 'output_type', 'resolution']:
|
||||
# Dropdown property
|
||||
# Dropdown property with width limits
|
||||
widget = QComboBox()
|
||||
|
||||
# Set maximum width to prevent expansion
|
||||
widget.setMaximumWidth(250)
|
||||
widget.setMinimumWidth(150)
|
||||
widget.setStyleSheet("""
|
||||
QComboBox {
|
||||
padding: 4px 8px;
|
||||
background-color: #45475a;
|
||||
color: #cdd6f4;
|
||||
border: 1px solid #585b70;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
}
|
||||
QComboBox:hover {
|
||||
border-color: #74c7ec;
|
||||
}
|
||||
QComboBox::drop-down {
|
||||
border: none;
|
||||
width: 20px;
|
||||
}
|
||||
QComboBox::down-arrow {
|
||||
image: none;
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-top: 4px solid #cdd6f4;
|
||||
margin-right: 4px;
|
||||
}
|
||||
QComboBox QAbstractItemView {
|
||||
background-color: #313244;
|
||||
color: #cdd6f4;
|
||||
selection-background-color: #89b4fa;
|
||||
border: 1px solid #585b70;
|
||||
}
|
||||
""")
|
||||
|
||||
# Use options from prop_options if available, otherwise use defaults
|
||||
if prop_options and isinstance(prop_options, list):
|
||||
items = prop_options
|
||||
@ -1401,10 +1510,48 @@ class IntegratedPipelineDashboard(QMainWindow):
|
||||
widget.stateChanged.connect(on_change)
|
||||
|
||||
elif isinstance(prop_value, int):
|
||||
# Integer property
|
||||
# Integer property with width limits
|
||||
widget = QSpinBox()
|
||||
widget.setValue(prop_value)
|
||||
|
||||
# Set width limits to prevent expansion
|
||||
widget.setMaximumWidth(120)
|
||||
widget.setMinimumWidth(80)
|
||||
widget.setStyleSheet("""
|
||||
QSpinBox {
|
||||
padding: 4px 6px;
|
||||
background-color: #45475a;
|
||||
color: #cdd6f4;
|
||||
border: 1px solid #585b70;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
}
|
||||
QSpinBox:hover {
|
||||
border-color: #74c7ec;
|
||||
}
|
||||
QSpinBox:focus {
|
||||
border-color: #89b4fa;
|
||||
}
|
||||
QSpinBox::up-button, QSpinBox::down-button {
|
||||
width: 16px;
|
||||
background-color: #585b70;
|
||||
border: none;
|
||||
}
|
||||
QSpinBox::up-button:hover, QSpinBox::down-button:hover {
|
||||
background-color: #6c7086;
|
||||
}
|
||||
QSpinBox::up-arrow {
|
||||
border-left: 3px solid transparent;
|
||||
border-right: 3px solid transparent;
|
||||
border-bottom: 3px solid #cdd6f4;
|
||||
}
|
||||
QSpinBox::down-arrow {
|
||||
border-left: 3px solid transparent;
|
||||
border-right: 3px solid transparent;
|
||||
border-top: 3px solid #cdd6f4;
|
||||
}
|
||||
""")
|
||||
|
||||
# Set range from prop_options if available, otherwise use defaults
|
||||
if prop_options and isinstance(prop_options, dict) and 'min' in prop_options and 'max' in prop_options:
|
||||
widget.setRange(prop_options['min'], prop_options['max'])
|
||||
@ -1429,11 +1576,49 @@ class IntegratedPipelineDashboard(QMainWindow):
|
||||
widget.valueChanged.connect(on_change)
|
||||
|
||||
elif isinstance(prop_value, float):
|
||||
# Float property
|
||||
# Float property with width limits
|
||||
widget = QDoubleSpinBox()
|
||||
widget.setValue(prop_value)
|
||||
widget.setDecimals(2)
|
||||
|
||||
# Set width limits to prevent expansion
|
||||
widget.setMaximumWidth(120)
|
||||
widget.setMinimumWidth(80)
|
||||
widget.setStyleSheet("""
|
||||
QDoubleSpinBox {
|
||||
padding: 4px 6px;
|
||||
background-color: #45475a;
|
||||
color: #cdd6f4;
|
||||
border: 1px solid #585b70;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
}
|
||||
QDoubleSpinBox:hover {
|
||||
border-color: #74c7ec;
|
||||
}
|
||||
QDoubleSpinBox:focus {
|
||||
border-color: #89b4fa;
|
||||
}
|
||||
QDoubleSpinBox::up-button, QDoubleSpinBox::down-button {
|
||||
width: 16px;
|
||||
background-color: #585b70;
|
||||
border: none;
|
||||
}
|
||||
QDoubleSpinBox::up-button:hover, QDoubleSpinBox::down-button:hover {
|
||||
background-color: #6c7086;
|
||||
}
|
||||
QDoubleSpinBox::up-arrow {
|
||||
border-left: 3px solid transparent;
|
||||
border-right: 3px solid transparent;
|
||||
border-bottom: 3px solid #cdd6f4;
|
||||
}
|
||||
QDoubleSpinBox::down-arrow {
|
||||
border-left: 3px solid transparent;
|
||||
border-right: 3px solid transparent;
|
||||
border-top: 3px solid #cdd6f4;
|
||||
}
|
||||
""")
|
||||
|
||||
# Set range and step from prop_options if available, otherwise use defaults
|
||||
if prop_options and isinstance(prop_options, dict):
|
||||
if 'min' in prop_options and 'max' in prop_options:
|
||||
@ -1462,15 +1647,40 @@ class IntegratedPipelineDashboard(QMainWindow):
|
||||
widget.valueChanged.connect(on_change)
|
||||
|
||||
else:
|
||||
# String property (default)
|
||||
# String property (default) with width limits
|
||||
widget = QLineEdit()
|
||||
widget.setText(str(prop_value))
|
||||
|
||||
# Set width limits to prevent expansion
|
||||
widget.setMaximumWidth(250)
|
||||
widget.setMinimumWidth(150)
|
||||
widget.setStyleSheet("""
|
||||
QLineEdit {
|
||||
padding: 4px 8px;
|
||||
background-color: #45475a;
|
||||
color: #cdd6f4;
|
||||
border: 1px solid #585b70;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
}
|
||||
QLineEdit:hover {
|
||||
border-color: #74c7ec;
|
||||
}
|
||||
QLineEdit:focus {
|
||||
border-color: #89b4fa;
|
||||
}
|
||||
QLineEdit::placeholder {
|
||||
color: #6c7086;
|
||||
}
|
||||
""")
|
||||
|
||||
# Set placeholders for specific properties
|
||||
placeholders = {
|
||||
'model_path': 'Path to model file (.nef, .onnx, etc.)',
|
||||
'model_path': 'Path to model file',
|
||||
'destination': 'Output file path',
|
||||
'resolution': 'e.g., 1920x1080'
|
||||
'resolution': 'e.g., 1920x1080',
|
||||
'port_id': 'e.g., 6,7,8 or auto',
|
||||
'operations': 'e.g., resize,normalize'
|
||||
}
|
||||
|
||||
if prop_name in placeholders:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user