feat: add comprehensive keyboard shortcuts for improved navigation and productivity
Build and Push Docker Image / build-and-push (push) Has been cancelled

This commit is contained in:
William Valentin
2025-08-05 10:05:32 -07:00
parent 9790f2730a
commit 86606d56b6
7 changed files with 414 additions and 8 deletions
+25
View File
@@ -15,6 +15,7 @@ make test
## 📚 Documentation
- **[Features Guide](docs/FEATURES.md)** - Complete feature documentation
- **[Keyboard Shortcuts](docs/KEYBOARD_SHORTCUTS.md)** - Keyboard shortcuts for efficient navigation
- **[Export System](docs/EXPORT_SYSTEM.md)** - Data export functionality and formats
- **[Development Guide](docs/DEVELOPMENT.md)** - Testing, development, and architecture
- **[Changelog](docs/CHANGELOG.md)** - Version history and feature evolution
@@ -483,6 +484,30 @@ thechart_data.csv # User data (created on first run)
- **`pyproject.toml`**: Project configuration and dependencies
- **`uv.lock`**: Dependency lock file
### Keyboard Shortcuts
```bash
# File Operations
Ctrl+S # Save/Add new entry
Ctrl+Q # Quit application
Ctrl+E # Export data
# Data Management
Ctrl+N # Clear entries
Ctrl+R / F5 # Refresh data
# Window Management
Ctrl+M # Manage medicines
Ctrl+P # Manage pathologies
# Table Operations
Delete # Delete selected entry
Escape # Clear selection
Double-click # Edit entry
# Help
F1 # Show keyboard shortcuts help
```
---
## Why uv?
+34
View File
@@ -5,6 +5,40 @@ All notable changes to TheChart project are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.7.0] - 2025-08-05
### ⌨️ Keyboard Shortcuts System
- **Added**: Comprehensive keyboard shortcuts for improved productivity
- **Added**: File operations shortcuts (Ctrl+S, Ctrl+Q, Ctrl+E)
- **Added**: Data management shortcuts (Ctrl+N, Ctrl+R, F5)
- **Added**: Window management shortcuts (Ctrl+M, Ctrl+P)
- **Added**: Table operation shortcuts (Delete, Escape)
- **Added**: Help system shortcut (F1)
- **Added**: Menu integration showing shortcuts next to menu items
- **Added**: Button labels updated to show primary shortcuts
- **Added**: In-app help dialog accessible via F1
- **Added**: Status bar feedback for all keyboard operations
- **Improved**: Button text shows shortcuts (e.g., "Add Entry (Ctrl+S)")
- **Improved**: Case-insensitive shortcuts (Ctrl+S and Ctrl+Shift+S both work)
#### Keyboard Shortcuts Added:
- **Ctrl+S**: Save/Add new entry
- **Ctrl+Q**: Quit application (with confirmation)
- **Ctrl+E**: Export data
- **Ctrl+N**: Clear entries
- **Ctrl+R / F5**: Refresh data
- **Ctrl+M**: Manage medicines
- **Ctrl+P**: Manage pathologies
- **Delete**: Delete selected entry (with confirmation)
- **Escape**: Clear selection
- **F1**: Show keyboard shortcuts help
### 📚 Documentation Updates
- **Updated**: FEATURES.md with keyboard shortcuts section
- **Added**: KEYBOARD_SHORTCUTS.md with comprehensive shortcut reference
- **Updated**: In-app help system with shortcut information
- **Updated**: About dialog with keyboard shortcut mention
## [1.6.1] - 2025-07-31
### 📚 Documentation Overhaul
+31
View File
@@ -159,6 +159,37 @@ Professional testing infrastructure with high code coverage.
- **Real-time Updates**: Immediate feedback and data updates
- **Error Handling**: Comprehensive error messages and recovery options
### ⌨️ Keyboard Shortcuts
Comprehensive keyboard shortcuts for efficient navigation and data entry.
#### File Operations:
- **Ctrl+S**: Save/Add new entry - Quickly save current entry data
- **Ctrl+Q**: Quit application - Exit with confirmation dialog
- **Ctrl+E**: Export data - Open export dialog window
#### Data Management:
- **Ctrl+N**: Clear entries - Clear all input fields for new entry
- **Ctrl+R / F5**: Refresh data - Reload data from CSV and update displays
#### Window Management:
- **Ctrl+M**: Manage medicines - Open medicine management window
- **Ctrl+P**: Manage pathologies - Open pathology management window
#### Table Operations:
- **Delete**: Delete selected entry - Remove selected table entry with confirmation
- **Escape**: Clear selection - Clear current table selection
- **Double-click**: Edit entry - Open edit dialog for selected entry
#### Help System:
- **F1**: Show keyboard shortcuts - Display help dialog with all shortcuts
#### Integration Features:
- **Menu Display**: All shortcuts shown in menu bar next to items
- **Button Labels**: Primary buttons show their keyboard shortcuts
- **Case Insensitive**: Both Ctrl+S and Ctrl+Shift+S work
- **Focus Management**: Shortcuts work when main window has focus
- **Status Feedback**: All operations provide status bar feedback
## Technical Architecture
### 🏗️ Modular Design
+71
View File
@@ -0,0 +1,71 @@
# Keyboard Shortcuts
TheChart application supports comprehensive keyboard shortcuts for improved productivity and efficient navigation.
## File Operations
- **Ctrl+S**: Save/Add new entry - Saves the current entry data to the database
- **Ctrl+Q**: Quit application - Exits the application (with confirmation dialog)
- **Ctrl+E**: Export data - Opens the export dialog window
## Data Management
- **Ctrl+N**: Clear entries - Clears all input fields to start a new entry
- **Ctrl+R** or **F5**: Refresh data - Reloads data from the CSV file and updates the display
## Window Management
- **Ctrl+M**: Manage medicines - Opens the medicine management window
- **Ctrl+P**: Manage pathologies - Opens the pathology management window
## Table Operations
- **Delete**: Delete selected entry - Deletes the currently selected entry in the table (with confirmation)
- **Escape**: Clear selection - Clears the current selection in the table
- **Double-click**: Edit entry - Opens the edit dialog for the selected entry
## Help
- **F1**: Show keyboard shortcuts help - Displays a dialog with all available keyboard shortcuts
## Implementation Details
### Menu Integration
All keyboard shortcuts are displayed in the menu bar next to their corresponding menu items for easy reference.
### Button Labels
Primary action buttons show their keyboard shortcuts in the button text (e.g., "Add Entry (Ctrl+S)").
### Case Sensitivity
- Shortcuts are case-insensitive
- Both `Ctrl+S` and `Ctrl+Shift+S` work
- Uppercase and lowercase variants are supported
### Focus Requirements
- Keyboard shortcuts work when the main window has focus
- Focus is automatically set to the main window on startup
- Shortcuts work across all tabs and interface elements
### Feedback System
- All operations provide feedback through the status bar
- Success and error messages are displayed
- Confirmation dialogs are shown for destructive operations (quit, delete)
## Usage Tips
### Quick Workflow
1. **Ctrl+N** - Clear fields for new entry
2. Enter data in the form
3. **Ctrl+S** - Save the entry
4. **F5** - Refresh to see updated data
### Navigation
- Use **Ctrl+M** and **Ctrl+P** to quickly access management windows
- Use **Delete** to remove unwanted entries from the table
- Use **Escape** to clear selections when needed
### Getting Help
- Press **F1** anytime to see the keyboard shortcuts help dialog
- All shortcuts are also visible in the menu bar
- Button tooltips show additional keyboard shortcut information
## Accessibility
- Keyboard shortcuts provide full application functionality without mouse use
- All critical operations have keyboard equivalents
- Shortcuts follow standard application conventions (Ctrl+S for save, Ctrl+Q for quit)
- Help system is easily accessible via F1
+5
View File
@@ -11,6 +11,11 @@ Welcome to TheChart documentation! This guide will help you navigate the availab
- Advanced Dose Tracking
- Graph Visualizations
- Data Management
- Keyboard Shortcuts
- **[Keyboard Shortcuts](KEYBOARD_SHORTCUTS.md)** - Comprehensive shortcut reference
- File operations shortcuts
- Data management shortcuts
- Navigation shortcuts
### For Developers
- **[Development Guide](DEVELOPMENT.md)** - Development setup and testing
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env python3
"""
Test script for keyboard shortcuts functionality.
This script tests that the keyboard shortcuts are properly bound.
"""
import os
import sys
import tkinter as tk
# Add the src directory to the path so we can import the main module
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
from main import MedTrackerApp
def test_keyboard_shortcuts():
"""Test that keyboard shortcuts are properly bound."""
print("Testing keyboard shortcuts...")
# Create a test window
root = tk.Tk()
root.withdraw() # Hide the window for testing
try:
# Create the app instance
app = MedTrackerApp(root)
# Test that the shortcuts are bound
expected_shortcuts = [
"<Control-s>",
"<Control-S>",
"<Control-q>",
"<Control-Q>",
"<Control-e>",
"<Control-E>",
"<Control-n>",
"<Control-N>",
"<Control-r>",
"<Control-R>",
"<F5>",
"<Control-m>",
"<Control-M>",
"<Control-p>",
"<Control-P>",
"<Delete>",
"<Escape>",
"<F1>",
]
# Check if shortcuts are bound
bound_shortcuts = []
for shortcut in expected_shortcuts:
if root.bind(shortcut):
bound_shortcuts.append(shortcut)
print(f"Successfully bound {len(bound_shortcuts)} keyboard shortcuts:")
for shortcut in bound_shortcuts:
print(f"{shortcut}")
# Test that methods exist
methods_to_test = [
"add_new_entry",
"handle_window_closing",
"_open_export_window",
"_clear_entries",
"refresh_data_display",
"_open_medicine_manager",
"_open_pathology_manager",
"_delete_selected_entry",
"_clear_selection",
"_show_keyboard_shortcuts",
]
for method_name in methods_to_test:
if hasattr(app, method_name):
print(f" ✓ Method {method_name} exists")
else:
print(f" ✗ Method {method_name} missing")
print("\n✅ Keyboard shortcuts test completed successfully!")
return True
except Exception as e:
print(f"❌ Error during testing: {e}")
return False
finally:
root.destroy()
if __name__ == "__main__":
success = test_keyboard_shortcuts()
sys.exit(0 if success else 1)
+155 -8
View File
@@ -72,6 +72,9 @@ class MedTrackerApp:
# Add menu bar
self._setup_menu()
# Setup keyboard shortcuts
self._setup_keyboard_shortcuts()
# Center the window on screen
self._center_window()
@@ -141,12 +144,12 @@ class MedTrackerApp:
self.input_frame,
[
{
"text": "Add Entry",
"text": "Add Entry (Ctrl+S)",
"command": self.add_new_entry,
"fill": "both",
"expand": True,
},
{"text": "Quit", "command": self.handle_window_closing},
{"text": "Quit (Ctrl+Q)", "command": self.handle_window_closing},
],
)
@@ -172,19 +175,126 @@ class MedTrackerApp:
# File menu
file_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="File", menu=file_menu)
file_menu.add_command(label="Export Data...", command=self._open_export_window)
file_menu.add_command(
label="Export Data...",
command=self._open_export_window,
accelerator="Ctrl+E",
)
file_menu.add_separator()
file_menu.add_command(label="Exit", command=self.handle_window_closing)
file_menu.add_command(
label="Exit", command=self.handle_window_closing, accelerator="Ctrl+Q"
)
# Tools menu
tools_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="Tools", menu=tools_menu)
tools_menu.add_command(
label="Manage Pathologies...", command=self._open_pathology_manager
label="Manage Pathologies...",
command=self._open_pathology_manager,
accelerator="Ctrl+P",
)
tools_menu.add_command(
label="Manage Medicines...", command=self._open_medicine_manager
label="Manage Medicines...",
command=self._open_medicine_manager,
accelerator="Ctrl+M",
)
tools_menu.add_separator()
tools_menu.add_command(
label="Clear Entries", command=self._clear_entries, accelerator="Ctrl+N"
)
tools_menu.add_command(
label="Refresh Data", command=self.refresh_data_display, accelerator="F5"
)
# Help menu
help_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="Help", menu=help_menu)
help_menu.add_command(
label="Keyboard Shortcuts",
command=self._show_keyboard_shortcuts,
accelerator="F1",
)
help_menu.add_command(label="About", command=self._show_about_dialog)
def _setup_keyboard_shortcuts(self) -> None:
"""Set up keyboard shortcuts for common actions."""
# Bind keyboard shortcuts to the main window
self.root.bind("<Control-s>", lambda e: self.add_new_entry())
self.root.bind("<Control-S>", lambda e: self.add_new_entry())
self.root.bind("<Control-q>", lambda e: self.handle_window_closing())
self.root.bind("<Control-Q>", lambda e: self.handle_window_closing())
self.root.bind("<Control-e>", lambda e: self._open_export_window())
self.root.bind("<Control-E>", lambda e: self._open_export_window())
self.root.bind("<Control-n>", lambda e: self._clear_entries())
self.root.bind("<Control-N>", lambda e: self._clear_entries())
self.root.bind("<Control-r>", lambda e: self.refresh_data_display())
self.root.bind("<Control-R>", lambda e: self.refresh_data_display())
self.root.bind("<F5>", lambda e: self.refresh_data_display())
self.root.bind("<Control-m>", lambda e: self._open_medicine_manager())
self.root.bind("<Control-M>", lambda e: self._open_medicine_manager())
self.root.bind("<Control-p>", lambda e: self._open_pathology_manager())
self.root.bind("<Control-P>", lambda e: self._open_pathology_manager())
self.root.bind("<Delete>", lambda e: self._delete_selected_entry())
self.root.bind("<Escape>", lambda e: self._clear_selection())
self.root.bind("<F1>", lambda e: self._show_keyboard_shortcuts())
# Make the window focusable so it can receive key events
self.root.focus_set()
logger.info("Keyboard shortcuts configured:")
logger.info(" Ctrl+S: Save/Add new entry")
logger.info(" Ctrl+Q: Quit application")
logger.info(" Ctrl+E: Export data")
logger.info(" Ctrl+N: Clear entries")
logger.info(" Ctrl+R/F5: Refresh data")
logger.info(" Ctrl+M: Manage medicines")
logger.info(" Ctrl+P: Manage pathologies")
logger.info(" Delete: Delete selected entry")
logger.info(" Escape: Clear selection")
logger.info(" F1: Show keyboard shortcuts help")
def _show_keyboard_shortcuts(self) -> None:
"""Show a dialog with keyboard shortcuts information."""
shortcuts_text = """Keyboard Shortcuts:
File Operations:
• Ctrl+S: Save/Add new entry
• Ctrl+Q: Quit application
• Ctrl+E: Export data
Data Management:
• Ctrl+N: Clear entries
• Ctrl+R / F5: Refresh data
Window Management:
• Ctrl+M: Manage medicines
• Ctrl+P: Manage pathologies
Table Operations:
• Delete: Delete selected entry
• Escape: Clear selection
• Double-click: Edit entry
Help:
• F1: Show this help dialog"""
messagebox.showinfo("Keyboard Shortcuts", shortcuts_text, parent=self.root)
def _show_about_dialog(self) -> None:
"""Show about dialog."""
about_text = """TheChart - Medication Tracker
A simple application for tracking medications and pathologies.
Features:
• Add daily medication and pathology entries
• Visual graphs and charts
• Data export capabilities
• Keyboard shortcuts for efficiency
Use Ctrl+S to save entries and Ctrl+Q to quit."""
messagebox.showinfo("About TheChart", about_text, parent=self.root)
def _open_export_window(self) -> None:
"""Open the export window."""
@@ -231,12 +341,12 @@ class MedTrackerApp:
self.input_frame,
[
{
"text": "Add Entry",
"text": "Add Entry (Ctrl+S)",
"command": self.add_new_entry,
"fill": "both",
"expand": True,
},
{"text": "Quit", "command": self.handle_window_closing},
{"text": "Quit (Ctrl+Q)", "command": self.handle_window_closing},
],
)
@@ -254,6 +364,43 @@ class MedTrackerApp:
# Update status to show completion
self.ui_manager.update_status("UI refreshed successfully", "success")
def _delete_selected_entry(self) -> None:
"""Delete the currently selected entry in the table."""
selection = self.tree.selection()
if not selection:
self.ui_manager.update_status("No entry selected for deletion", "warning")
return
item_id = selection[0]
item_values = self.tree.item(item_id, "values")
if messagebox.askyesno(
"Delete Entry",
f"Are you sure you want to delete the entry for {item_values[0]}?",
parent=self.root,
):
date: str = item_values[0]
logger.debug(f"Deleting entry with date={date}")
self.ui_manager.update_status("Deleting entry...", "info")
if self.data_manager.delete_entry(date):
self.ui_manager.update_status("Entry deleted successfully!", "success")
messagebox.showinfo(
"Success", "Entry deleted successfully!", parent=self.root
)
self.refresh_data_display()
else:
self.ui_manager.update_status("Failed to delete entry", "error")
messagebox.showerror(
"Error", "Failed to delete entry", parent=self.root
)
def _clear_selection(self) -> None:
"""Clear the current selection in the table."""
if self.tree.selection():
self.tree.selection_remove(self.tree.selection())
self.ui_manager.update_status("Selection cleared", "info")
def handle_double_click(self, event: tk.Event) -> None:
"""Handle double-click event to edit an entry."""
logger.debug("Double-click event triggered on treeview.")