Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c3c88c63d2 | |||
| 86606d56b6 |
@@ -1,7 +1,7 @@
|
|||||||
# TheChart
|
# TheChart
|
||||||
Advanced medication tracking application for monitoring treatment progress and symptom evolution.
|
Modern medication tracking application with advanced UI/UX for monitoring treatment progress and symptom evolution.
|
||||||
|
|
||||||
## Quick Start
|
## 🚀 Quick Start
|
||||||
```bash
|
```bash
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
make install
|
make install
|
||||||
@@ -14,11 +14,22 @@ make test
|
|||||||
```
|
```
|
||||||
|
|
||||||
## 📚 Documentation
|
## 📚 Documentation
|
||||||
- **[Features Guide](docs/FEATURES.md)** - Complete feature documentation
|
- **[Features Guide](docs/FEATURES.md)** - Complete feature documentation with UI/UX improvements
|
||||||
- **[Export System](docs/EXPORT_SYSTEM.md)** - Data export functionality and formats
|
- **[Keyboard Shortcuts](docs/KEYBOARD_SHORTCUTS.md)** - Comprehensive keyboard shortcuts for efficiency
|
||||||
|
- **[Export System](docs/EXPORT_SYSTEM.md)** - Data export functionality (JSON, XML, PDF)
|
||||||
- **[Development Guide](docs/DEVELOPMENT.md)** - Testing, development, and architecture
|
- **[Development Guide](docs/DEVELOPMENT.md)** - Testing, development, and architecture
|
||||||
- **[Changelog](docs/CHANGELOG.md)** - Version history and feature evolution
|
- **[Changelog](docs/CHANGELOG.md)** - Version history and recent UI improvements
|
||||||
- **[Quick Reference](#quick-reference)** - Common commands and shortcuts
|
- **[Documentation Index](docs/README.md)** - Complete documentation navigation guide
|
||||||
|
|
||||||
|
> 💡 **Quick Start**: New users should start with this README, then explore the [Features Guide](docs/FEATURES.md) for detailed functionality. The [Documentation Index](docs/README.md) provides comprehensive navigation.
|
||||||
|
|
||||||
|
## ✨ Recent Major Updates (v1.9.5)
|
||||||
|
- **🎨 Modern UI/UX**: Professional themes with ttkthemes integration
|
||||||
|
- **⌨️ Keyboard Shortcuts**: Comprehensive shortcut system for all operations
|
||||||
|
- **💡 Smart Tooltips**: Context-sensitive help throughout the application
|
||||||
|
- **🎭 8 Professional Themes**: Arc, Equilux, Adapta, Yaru, Ubuntu, Plastik, Breeze, Elegance
|
||||||
|
- **⚙️ Settings System**: Advanced configuration with theme persistence
|
||||||
|
- **📊 Enhanced Tables**: Improved selection highlighting and alternating row colors
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
- [Prerequisites](#prerequisites)
|
- [Prerequisites](#prerequisites)
|
||||||
@@ -483,6 +494,30 @@ thechart_data.csv # User data (created on first run)
|
|||||||
- **`pyproject.toml`**: Project configuration and dependencies
|
- **`pyproject.toml`**: Project configuration and dependencies
|
||||||
- **`uv.lock`**: Dependency lock file
|
- **`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?
|
## Why uv?
|
||||||
|
|||||||
@@ -5,6 +5,75 @@ 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/),
|
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).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.9.5] - 2025-08-05
|
||||||
|
|
||||||
|
### 🎨 Major UI/UX Overhaul
|
||||||
|
- **Added**: Professional theme system with ttkthemes integration
|
||||||
|
- **Added**: 8 curated themes (Arc, Equilux, Adapta, Yaru, Ubuntu, Plastik, Breeze, Elegance)
|
||||||
|
- **Added**: Dynamic theme switching without restart
|
||||||
|
- **Added**: Theme persistence between sessions
|
||||||
|
- **Added**: Comprehensive settings window with tabbed interface
|
||||||
|
- **Added**: Smart tooltip system with context-sensitive help
|
||||||
|
- **Improved**: Table selection highlighting and alternating row colors
|
||||||
|
- **Improved**: Modern styling for all UI components (buttons, frames, forms)
|
||||||
|
- **Improved**: Professional card-style layouts and enhanced spacing
|
||||||
|
|
||||||
|
### ⚙️ Settings and Configuration System
|
||||||
|
- **Added**: Advanced settings window (accessible via F2)
|
||||||
|
- **Added**: Theme selection with live preview
|
||||||
|
- **Added**: UI preferences and customization options
|
||||||
|
- **Added**: About dialog with detailed application information
|
||||||
|
- **Added**: Settings persistence across application restarts
|
||||||
|
|
||||||
|
### 💡 Enhanced User Experience
|
||||||
|
- **Added**: Intelligent tooltips for all interactive elements
|
||||||
|
- **Added**: Specialized help for pathology scales and medicine options
|
||||||
|
- **Added**: Non-intrusive tooltip timing (500-800ms delay)
|
||||||
|
- **Added**: Quick theme switching via menu bar
|
||||||
|
- **Improved**: Visual hierarchy with better typography and spacing
|
||||||
|
- **Improved**: Professional color schemes across all themes
|
||||||
|
|
||||||
|
### 🏗️ Technical Architecture Improvements
|
||||||
|
- **Added**: Modular theme manager with dependency injection
|
||||||
|
- **Added**: Tooltip management system
|
||||||
|
- **Added**: Enhanced UI manager with theme integration
|
||||||
|
- **Improved**: Code organization with separate concerns
|
||||||
|
- **Improved**: Error handling with graceful theme fallbacks
|
||||||
|
|
||||||
|
## [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
|
## [1.6.1] - 2025-07-31
|
||||||
|
|
||||||
### 📚 Documentation Overhaul
|
### 📚 Documentation Overhaul
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
# Documentation Consolidation Summary
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
This document summarizes the documentation consolidation and updates performed to improve the TheChart project documentation structure.
|
||||||
|
|
||||||
|
## Changes Made
|
||||||
|
|
||||||
|
### 1. Documentation Structure Consolidation
|
||||||
|
- **Removed**: `docs/UI_IMPROVEMENTS.md` (redundant file)
|
||||||
|
- **Consolidated**: UI/UX improvements documentation into `docs/FEATURES.md`
|
||||||
|
- **Enhanced**: Main `README.md` with recent updates section
|
||||||
|
- **Updated**: `docs/README.md` (documentation index) with comprehensive navigation
|
||||||
|
|
||||||
|
### 2. Content Integration
|
||||||
|
|
||||||
|
#### FEATURES.md Enhancements
|
||||||
|
- **Added**: Modern UI/UX System section (new in v1.9.5)
|
||||||
|
- **Added**: Professional Theme Engine documentation
|
||||||
|
- **Added**: Comprehensive Keyboard Shortcuts section
|
||||||
|
- **Added**: Settings and Theme Management documentation
|
||||||
|
- **Added**: Smart Tooltip System documentation
|
||||||
|
- **Added**: Enhanced Technical Architecture section
|
||||||
|
- **Added**: UI/UX Technical Implementation section
|
||||||
|
|
||||||
|
#### CHANGELOG.md Updates
|
||||||
|
- **Added**: Version 1.9.5 with comprehensive UI/UX overhaul documentation
|
||||||
|
- **Added**: Settings and Configuration System section
|
||||||
|
- **Added**: Enhanced User Experience section
|
||||||
|
- **Added**: Technical Architecture Improvements section
|
||||||
|
|
||||||
|
#### README.md Improvements
|
||||||
|
- **Updated**: Title and description to emphasize modern UI/UX
|
||||||
|
- **Added**: Recent Major Updates section highlighting v1.9.5 improvements
|
||||||
|
- **Added**: Quick start guidance for new users
|
||||||
|
- **Updated**: Documentation links with better descriptions
|
||||||
|
- **Added**: Documentation navigation guide reference
|
||||||
|
|
||||||
|
### 3. Cross-Reference Updates
|
||||||
|
- **Updated**: All internal links to reflect consolidated structure
|
||||||
|
- **Enhanced**: Documentation index with comprehensive navigation
|
||||||
|
- **Added**: Task-based navigation in docs/README.md
|
||||||
|
- **Improved**: User type-based documentation guidance
|
||||||
|
|
||||||
|
## Current Documentation Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
docs/
|
||||||
|
├── README.md # Documentation index and navigation guide
|
||||||
|
├── FEATURES.md # Complete feature documentation (includes UI/UX)
|
||||||
|
├── KEYBOARD_SHORTCUTS.md # Comprehensive shortcut reference
|
||||||
|
├── EXPORT_SYSTEM.md # Data export functionality
|
||||||
|
├── DEVELOPMENT.md # Development setup and testing
|
||||||
|
├── CHANGELOG.md # Version history and improvements
|
||||||
|
└── DOCUMENTATION_SUMMARY.md # This summary (new)
|
||||||
|
|
||||||
|
README.md # Main project README with quick start
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation Highlights
|
||||||
|
|
||||||
|
### For End Users
|
||||||
|
1. **Modern UI/UX**: Complete documentation of the new theme system
|
||||||
|
2. **Keyboard Efficiency**: Comprehensive shortcut system documentation
|
||||||
|
3. **Feature Guidance**: Consolidated feature documentation with examples
|
||||||
|
4. **Quick Navigation**: Task-based and user-type-based navigation
|
||||||
|
|
||||||
|
### For Developers
|
||||||
|
1. **Technical Architecture**: Enhanced architecture documentation
|
||||||
|
2. **UI/UX Implementation**: Technical details of theme system
|
||||||
|
3. **Code Organization**: Clear separation of concerns documentation
|
||||||
|
4. **Development Workflow**: Comprehensive development guide
|
||||||
|
|
||||||
|
## Quality Improvements
|
||||||
|
|
||||||
|
### Content Quality
|
||||||
|
- **Comprehensive Coverage**: All major features and improvements documented
|
||||||
|
- **Clear Structure**: Hierarchical organization with clear headings
|
||||||
|
- **Practical Examples**: Code snippets and usage examples maintained
|
||||||
|
- **Cross-References**: Better linking between related sections
|
||||||
|
|
||||||
|
### User Experience
|
||||||
|
- **Progressive Disclosure**: Information organized by user expertise level
|
||||||
|
- **Task-Oriented**: Documentation organized around user tasks
|
||||||
|
- **Quick Access**: Multiple entry points and navigation paths
|
||||||
|
- **Searchable**: Clear headings and consistent formatting
|
||||||
|
|
||||||
|
### Maintenance
|
||||||
|
- **Reduced Redundancy**: Eliminated duplicate information
|
||||||
|
- **Single Source of Truth**: Consolidated information reduces maintenance burden
|
||||||
|
- **Version Alignment**: Documentation synchronized with current codebase
|
||||||
|
- **Future-Proof**: Structure supports easy updates and additions
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
### Recommended Maintenance
|
||||||
|
1. **Keep Features Updated**: Update FEATURES.md as new UI/UX improvements are added
|
||||||
|
2. **Maintain Changelog**: Continue detailed changelog entries for version tracking
|
||||||
|
3. **Review Navigation**: Periodically review docs/README.md navigation for completeness
|
||||||
|
4. **User Feedback**: Collect user feedback on documentation effectiveness
|
||||||
|
|
||||||
|
### Future Enhancements
|
||||||
|
1. **Screenshots**: Consider adding screenshots of the new UI themes
|
||||||
|
2. **Video Guides**: Potential for video demonstrations of key features
|
||||||
|
3. **API Documentation**: If public APIs develop, consider separate API docs
|
||||||
|
4. **Internationalization**: Structure supports future translation efforts
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Documentation consolidation completed**: All major UI/UX improvements are now properly documented and easily discoverable through the improved navigation structure.
|
||||||
+144
-16
@@ -1,7 +1,49 @@
|
|||||||
# TheChart - Features Documentation
|
# TheChart - Features Documentation
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
TheChart is a comprehensive medication tracking application that allows users to monitor medication intake, symptom tracking, and visualize treatment progress over time.
|
TheChart is a comprehensive medication tracking application with a modern, professional UI that allows users to monitor medication intake, track symptoms, and visualize treatment progress over time.
|
||||||
|
|
||||||
|
## 🎨 Modern UI/UX System (New in v1.9.5)
|
||||||
|
|
||||||
|
### Professional Theme Engine
|
||||||
|
TheChart features a sophisticated theme system powered by ttkthemes, offering 8 carefully curated professional themes.
|
||||||
|
|
||||||
|
#### Available Themes:
|
||||||
|
- **Arc**: Modern flat design with subtle shadows
|
||||||
|
- **Equilux**: Dark theme with excellent contrast
|
||||||
|
- **Adapta**: Clean, minimalist design
|
||||||
|
- **Yaru**: Ubuntu-inspired modern interface
|
||||||
|
- **Ubuntu**: Official Ubuntu styling
|
||||||
|
- **Plastik**: Classic professional appearance
|
||||||
|
- **Breeze**: KDE-inspired clean design
|
||||||
|
- **Elegance**: Sophisticated dark theme
|
||||||
|
|
||||||
|
#### UI Enhancements:
|
||||||
|
- **Modern Styling**: Card-style frames, enhanced buttons, professional form controls
|
||||||
|
- **Smart Tooltips**: Context-sensitive help for all interactive elements
|
||||||
|
- **Improved Tables**: Better selection highlighting and alternating row colors
|
||||||
|
- **Settings System**: Comprehensive preferences with theme persistence
|
||||||
|
- **Responsive Design**: Automatic layout adjustments and scaling
|
||||||
|
|
||||||
|
### ⌨️ Comprehensive Keyboard Shortcuts
|
||||||
|
Professional keyboard shortcut system for efficient navigation and operation.
|
||||||
|
|
||||||
|
#### File Operations:
|
||||||
|
- **Ctrl+S**: Save/Add new entry
|
||||||
|
- **Ctrl+Q**: Quit application (with confirmation)
|
||||||
|
- **Ctrl+E**: Export data
|
||||||
|
|
||||||
|
#### Data Management:
|
||||||
|
- **Ctrl+N**: Clear entries
|
||||||
|
- **Ctrl+R / F5**: Refresh data
|
||||||
|
- **Delete**: Delete selected entry
|
||||||
|
- **Escape**: Clear selection
|
||||||
|
|
||||||
|
#### Window Management:
|
||||||
|
- **Ctrl+M**: Manage medicines
|
||||||
|
- **Ctrl+P**: Manage pathologies
|
||||||
|
- **F1**: Show keyboard shortcuts help
|
||||||
|
- **F2**: Open settings window
|
||||||
|
|
||||||
## Core Features
|
## Core Features
|
||||||
|
|
||||||
@@ -37,6 +79,36 @@ Each medicine includes:
|
|||||||
2. **Manual Configuration**: Edit `medicines.json` directly
|
2. **Manual Configuration**: Edit `medicines.json` directly
|
||||||
3. **Programmatically**: Use the MedicineManager API
|
3. **Programmatically**: Use the MedicineManager API
|
||||||
|
|
||||||
|
### ⚙️ Settings and Theme Management
|
||||||
|
Advanced configuration system allowing users to customize their experience.
|
||||||
|
|
||||||
|
#### Settings Window (F2):
|
||||||
|
- **Theme Selection**: Choose from 8 professional themes with live preview
|
||||||
|
- **UI Preferences**: Font scaling, window behavior options
|
||||||
|
- **About Information**: Detailed application and version information
|
||||||
|
- **Tabbed Interface**: Organized settings categories for easy navigation
|
||||||
|
|
||||||
|
#### Theme Features:
|
||||||
|
- **Real-time Switching**: No restart required for theme changes
|
||||||
|
- **Persistence**: Selected theme remembered between sessions
|
||||||
|
- **Quick Access**: Theme menu for instant switching
|
||||||
|
- **Fallback Handling**: Graceful handling if themes fail to load
|
||||||
|
|
||||||
|
### 💡 Smart Tooltip System
|
||||||
|
Context-sensitive help system providing guidance throughout the application.
|
||||||
|
|
||||||
|
#### Tooltip Types:
|
||||||
|
- **Pathology Scales**: Usage guidance for symptom tracking
|
||||||
|
- **Medicine Checkboxes**: Medication information and dosage details
|
||||||
|
- **Action Buttons**: Functionality description with keyboard shortcuts
|
||||||
|
- **Form Controls**: Input guidance and format requirements
|
||||||
|
|
||||||
|
#### Features:
|
||||||
|
- **Delayed Display**: Non-intrusive timing (500-800ms delay)
|
||||||
|
- **Theme-aware Styling**: Tooltips match selected theme
|
||||||
|
- **Smart Positioning**: Automatic placement to avoid screen edges
|
||||||
|
- **Rich Content**: Multi-line descriptions with formatting
|
||||||
|
|
||||||
### 💊 Advanced Dose Tracking
|
### 💊 Advanced Dose Tracking
|
||||||
Comprehensive dose tracking system that records exact timestamps and dosages throughout the day.
|
Comprehensive dose tracking system that records exact timestamps and dosages throughout the day.
|
||||||
|
|
||||||
@@ -159,26 +231,82 @@ Professional testing infrastructure with high code coverage.
|
|||||||
- **Real-time Updates**: Immediate feedback and data updates
|
- **Real-time Updates**: Immediate feedback and data updates
|
||||||
- **Error Handling**: Comprehensive error messages and recovery options
|
- **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
|
## Technical Architecture
|
||||||
|
|
||||||
### 🏗️ Modular Design
|
### � Modern UI Architecture
|
||||||
- **MedicineManager**: Core medicine CRUD operations
|
- **ThemeManager**: Centralized theme management with dynamic switching
|
||||||
- **PathologyManager**: Symptom and pathology management
|
- **TooltipManager**: Smart tooltip system with context-sensitive help
|
||||||
- **GraphManager**: All graph-related operations and visualizations
|
- **UIManager**: Enhanced UI component creation with theme integration
|
||||||
- **UIManager**: User interface creation and management
|
- **SettingsWindow**: Advanced configuration interface with persistence
|
||||||
- **DataManager**: CSV operations and data persistence
|
|
||||||
|
|
||||||
### 🔧 Configuration Management
|
### 🏗️ Core Application Design
|
||||||
- **JSON-based Configuration**: `medicines.json` and `pathologies.json`
|
- **MedicineManager**: Core medicine CRUD operations with JSON persistence
|
||||||
- **Dynamic Loading**: Runtime configuration updates
|
- **PathologyManager**: Symptom and pathology management system
|
||||||
- **Validation**: Input validation and error handling
|
- **GraphManager**: Professional graph rendering with matplotlib integration
|
||||||
- **Backward Compatibility**: Seamless updates and migrations
|
- **DataManager**: Robust CSV operations and data persistence with validation
|
||||||
|
|
||||||
### 📈 Data Processing
|
### 🔧 Configuration and Data Management
|
||||||
|
- **JSON-based Configuration**: `medicines.json` and `pathologies.json` for easy management
|
||||||
|
- **Dynamic Loading**: Runtime configuration updates without restarts
|
||||||
|
- **Data Validation**: Comprehensive input validation and error handling
|
||||||
|
- **Backward Compatibility**: Seamless updates and migrations across versions
|
||||||
|
|
||||||
|
### 📈 Advanced Data Processing
|
||||||
- **Pandas Integration**: Efficient data manipulation and analysis
|
- **Pandas Integration**: Efficient data manipulation and analysis
|
||||||
- **Matplotlib Visualization**: Professional graph rendering
|
- **Real-time Calculations**: Dynamic dose totals, averages, and statistics
|
||||||
- **Robust Parsing**: Handles various data formats and edge cases
|
- **Robust Parsing**: Handles various data formats and edge cases gracefully
|
||||||
- **Real-time Calculations**: Dynamic dose totals and averages
|
- **Performance Optimization**: Efficient batch operations and caching
|
||||||
|
|
||||||
|
## UI/UX Technical Implementation
|
||||||
|
|
||||||
|
### 🎭 Theme System Architecture
|
||||||
|
- **Multiple Theme Support**: 8 curated professional themes
|
||||||
|
- **Dynamic Style Application**: Real-time theme switching without restart
|
||||||
|
- **Color Extraction**: Automatic color scheme detection and application
|
||||||
|
- **Fallback Mechanisms**: Graceful handling when themes fail to load
|
||||||
|
|
||||||
|
### 💡 Enhanced User Experience
|
||||||
|
- **Smart Tooltips**: Context-sensitive help with delayed, non-intrusive display
|
||||||
|
- **Modern Styling**: Card-style frames, enhanced buttons, professional form controls
|
||||||
|
- **Improved Tables**: Better selection highlighting and alternating row colors
|
||||||
|
- **Responsive Design**: Automatic layout adjustments and proper scaling
|
||||||
|
|
||||||
|
### ⚙️ Settings and Persistence
|
||||||
|
- **Configuration Management**: Theme and preference persistence across sessions
|
||||||
|
- **Tabbed Settings Interface**: Organized categories for easy navigation
|
||||||
|
- **Live Preview**: Real-time theme preview in settings
|
||||||
|
- **Error Recovery**: Robust handling of corrupted settings with defaults
|
||||||
|
|
||||||
## Deployment and Distribution
|
## Deployment and Distribution
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
+46
-19
@@ -1,16 +1,27 @@
|
|||||||
# TheChart Documentation
|
# TheChart Documentation
|
||||||
|
|
||||||
Welcome to TheChart documentation! This guide will help you navigate the available documentation.
|
Welcome to TheChart documentation! This guide will help you navigate the available documentation for the modern medication tracking application.
|
||||||
|
|
||||||
## 📖 Documentation Index
|
## 📖 Documentation Index
|
||||||
|
|
||||||
### For Users
|
### For Users
|
||||||
- **[README.md](../README.md)** - Quick start guide and installation
|
- **[README.md](../README.md)** - Quick start guide and installation
|
||||||
- **[Features Guide](FEATURES.md)** - Complete feature documentation
|
- **[Features Guide](FEATURES.md)** - Complete feature documentation including new UI/UX improvements
|
||||||
|
- Modern Theme System (8 Professional Themes)
|
||||||
|
- Advanced Keyboard Shortcuts
|
||||||
|
- Smart Tooltip System
|
||||||
- Modular Medicine System
|
- Modular Medicine System
|
||||||
- Advanced Dose Tracking
|
- Advanced Dose Tracking
|
||||||
- Graph Visualizations
|
- Graph Visualizations
|
||||||
- Data Management
|
- Data Management
|
||||||
|
- **[Keyboard Shortcuts](KEYBOARD_SHORTCUTS.md)** - Comprehensive shortcut reference
|
||||||
|
- File operations shortcuts (Ctrl+S, Ctrl+Q, Ctrl+E)
|
||||||
|
- Data management shortcuts (Ctrl+N, Ctrl+R, F5)
|
||||||
|
- Navigation shortcuts (Ctrl+M, Ctrl+P, F1, F2)
|
||||||
|
- **[Export System](EXPORT_SYSTEM.md)** - Data export functionality and formats
|
||||||
|
- JSON, XML, and PDF export options
|
||||||
|
- Graph visualization inclusion
|
||||||
|
- Export manager architecture
|
||||||
|
|
||||||
### For Developers
|
### For Developers
|
||||||
- **[Development Guide](DEVELOPMENT.md)** - Development setup and testing
|
- **[Development Guide](DEVELOPMENT.md)** - Development setup and testing
|
||||||
@@ -21,53 +32,69 @@ Welcome to TheChart documentation! This guide will help you navigate the availab
|
|||||||
|
|
||||||
### Project History
|
### Project History
|
||||||
- **[Changelog](CHANGELOG.md)** - Version history and feature evolution
|
- **[Changelog](CHANGELOG.md)** - Version history and feature evolution
|
||||||
- Recent updates and improvements
|
- Recent UI/UX overhaul (v1.9.5)
|
||||||
- Migration notes
|
- Keyboard shortcuts system (v1.7.0)
|
||||||
- Future roadmap
|
- Medicine and dose tracking improvements
|
||||||
|
- Migration notes and future roadmap
|
||||||
|
|
||||||
## 🚀 Quick Navigation
|
## 🚀 Quick Navigation
|
||||||
|
|
||||||
### Getting Started
|
### Getting Started
|
||||||
1. **Installation**: See [README.md - Installation](../README.md#installation)
|
1. **Installation**: See [README.md - Installation](../README.md#installation)
|
||||||
2. **First Run**: See [README.md - Running the Application](../README.md#running-the-application)
|
2. **First Run**: See [README.md - Running the Application](../README.md#running-the-application)
|
||||||
3. **Key Features**: See [FEATURES.md](FEATURES.md)
|
3. **UI/UX Features**: See [FEATURES.md - Modern UI/UX System](FEATURES.md#-modern-uiux-system-new-in-v195)
|
||||||
|
|
||||||
|
### Using the Application
|
||||||
|
1. **Theme Selection**: See [FEATURES.md - Settings and Theme Management](FEATURES.md#️-settings-and-theme-management)
|
||||||
|
2. **Keyboard Shortcuts**: See [KEYBOARD_SHORTCUTS.md](KEYBOARD_SHORTCUTS.md)
|
||||||
|
3. **Medicine Management**: See [FEATURES.md - Modular Medicine System](FEATURES.md#-modular-medicine-system)
|
||||||
|
4. **Dose Tracking**: See [FEATURES.md - Advanced Dose Tracking](FEATURES.md#-advanced-dose-tracking)
|
||||||
|
5. **Data Export**: See [EXPORT_SYSTEM.md](EXPORT_SYSTEM.md)
|
||||||
|
|
||||||
### Development
|
### Development
|
||||||
1. **Setup**: See [DEVELOPMENT.md - Development Environment Setup](DEVELOPMENT.md#development-environment-setup)
|
1. **Setup**: See [DEVELOPMENT.md - Development Environment Setup](DEVELOPMENT.md#development-environment-setup)
|
||||||
2. **Testing**: See [DEVELOPMENT.md - Testing Framework](DEVELOPMENT.md#testing-framework)
|
2. **Testing**: See [DEVELOPMENT.md - Testing Framework](DEVELOPMENT.md#testing-framework)
|
||||||
3. **Contributing**: See [DEVELOPMENT.md - Development Workflow](DEVELOPMENT.md#development-workflow)
|
3. **Architecture**: See [FEATURES.md - Technical Architecture](FEATURES.md#technical-architecture)
|
||||||
|
4. **Contributing**: See [DEVELOPMENT.md - Development Workflow](DEVELOPMENT.md#development-workflow)
|
||||||
|
|
||||||
### Advanced Usage
|
## 📋 What's New in Documentation
|
||||||
1. **Medicine Management**: See [FEATURES.md - Modular Medicine System](FEATURES.md#-modular-medicine-system)
|
|
||||||
2. **Dose Tracking**: See [FEATURES.md - Advanced Dose Tracking](FEATURES.md#-advanced-dose-tracking)
|
|
||||||
3. **Visualizations**: See [FEATURES.md - Enhanced Graph Visualization](FEATURES.md#-enhanced-graph-visualization)
|
|
||||||
|
|
||||||
## 📋 Documentation Standards
|
### Recent Updates (v1.9.5)
|
||||||
|
- **Consolidated Structure**: Merged UI improvements into main features documentation
|
||||||
|
- **Enhanced Features Guide**: Added comprehensive UI/UX documentation
|
||||||
|
- **Updated Changelog**: Detailed UI/UX overhaul documentation
|
||||||
|
- **Improved Navigation**: Better cross-referencing between documents
|
||||||
|
|
||||||
All documentation follows these principles:
|
### Documentation Highlights
|
||||||
- **Clear Structure**: Hierarchical organization with clear headings
|
- **Professional UI/UX**: Complete documentation of the new theme system
|
||||||
- **Practical Examples**: Code snippets and usage examples
|
- **Keyboard Efficiency**: Comprehensive shortcut system documentation
|
||||||
- **Up-to-date**: Synchronized with current codebase
|
- **Developer-Friendly**: Enhanced development and testing documentation
|
||||||
- **Comprehensive**: Covers all major features and workflows
|
- **User-Focused**: Clear separation of user vs developer documentation
|
||||||
- **Cross-referenced**: Links between related sections
|
|
||||||
|
|
||||||
## 🔍 Finding Information
|
## 🔍 Finding Information
|
||||||
|
|
||||||
### By Topic
|
### By Topic
|
||||||
- **Installation & Setup** → [README.md](../README.md)
|
- **Installation & Setup** → [README.md](../README.md)
|
||||||
|
- **UI/UX and Themes** → [FEATURES.md - Modern UI/UX System](FEATURES.md#-modern-uiux-system-new-in-v195)
|
||||||
- **Feature Usage** → [FEATURES.md](FEATURES.md)
|
- **Feature Usage** → [FEATURES.md](FEATURES.md)
|
||||||
|
- **Keyboard Shortcuts** → [KEYBOARD_SHORTCUTS.md](KEYBOARD_SHORTCUTS.md)
|
||||||
|
- **Data Export** → [EXPORT_SYSTEM.md](EXPORT_SYSTEM.md)
|
||||||
- **Development** → [DEVELOPMENT.md](DEVELOPMENT.md)
|
- **Development** → [DEVELOPMENT.md](DEVELOPMENT.md)
|
||||||
- **Version History** → [CHANGELOG.md](CHANGELOG.md)
|
- **Version History** → [CHANGELOG.md](CHANGELOG.md)
|
||||||
|
|
||||||
### By User Type
|
### By User Type
|
||||||
- **End Users** → Start with [README.md](../README.md), then [FEATURES.md](FEATURES.md)
|
- **End Users** → Start with [README.md](../README.md), then [FEATURES.md](FEATURES.md)
|
||||||
- **Developers** → [DEVELOPMENT.md](DEVELOPMENT.md) and [CHANGELOG.md](CHANGELOG.md)
|
- **Power Users** → [KEYBOARD_SHORTCUTS.md](KEYBOARD_SHORTCUTS.md) and [EXPORT_SYSTEM.md](EXPORT_SYSTEM.md)
|
||||||
|
- **Developers** → [DEVELOPMENT.md](DEVELOPMENT.md) and [FEATURES.md - Technical Architecture](FEATURES.md#technical-architecture)
|
||||||
- **Contributors** → All documentation, especially [DEVELOPMENT.md](DEVELOPMENT.md)
|
- **Contributors** → All documentation, especially [DEVELOPMENT.md](DEVELOPMENT.md)
|
||||||
|
|
||||||
### By Task
|
### By Task
|
||||||
- **Install TheChart** → [README.md - Installation](../README.md#installation)
|
- **Install TheChart** → [README.md - Installation](../README.md#installation)
|
||||||
|
- **Change Theme** → [FEATURES.md - Settings and Theme Management](FEATURES.md#️-settings-and-theme-management)
|
||||||
|
- **Learn Shortcuts** → [KEYBOARD_SHORTCUTS.md](KEYBOARD_SHORTCUTS.md)
|
||||||
- **Add New Medicine** → [FEATURES.md - Modular Medicine System](FEATURES.md#-modular-medicine-system)
|
- **Add New Medicine** → [FEATURES.md - Modular Medicine System](FEATURES.md#-modular-medicine-system)
|
||||||
- **Track Doses** → [FEATURES.md - Advanced Dose Tracking](FEATURES.md#-advanced-dose-tracking)
|
- **Track Doses** → [FEATURES.md - Advanced Dose Tracking](FEATURES.md#-advanced-dose-tracking)
|
||||||
|
- **Export Data** → [EXPORT_SYSTEM.md](EXPORT_SYSTEM.md)
|
||||||
- **Run Tests** → [DEVELOPMENT.md - Testing Framework](DEVELOPMENT.md#testing-framework)
|
- **Run Tests** → [DEVELOPMENT.md - Testing Framework](DEVELOPMENT.md#testing-framework)
|
||||||
- **Deploy Application** → [README.md - Deployment](../README.md#deployment)
|
- **Deploy Application** → [README.md - Deployment](../README.md#deployment)
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ dependencies = [
|
|||||||
"pandas>=2.3.1",
|
"pandas>=2.3.1",
|
||||||
"reportlab>=4.4.3",
|
"reportlab>=4.4.3",
|
||||||
"tk>=0.1.0",
|
"tk>=0.1.0",
|
||||||
|
"ttkthemes>=3.2.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ matplotlib
|
|||||||
pandas
|
pandas
|
||||||
dotenv
|
dotenv
|
||||||
colorlog
|
colorlog
|
||||||
|
ttkthemes
|
||||||
|
|||||||
+5
-1
@@ -24,7 +24,9 @@ packaging==25.0
|
|||||||
pandas==2.3.1
|
pandas==2.3.1
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
pillow==11.3.0
|
pillow==11.3.0
|
||||||
# via matplotlib
|
# via
|
||||||
|
# matplotlib
|
||||||
|
# ttkthemes
|
||||||
pyparsing==3.2.3
|
pyparsing==3.2.3
|
||||||
# via matplotlib
|
# via matplotlib
|
||||||
python-dateutil==2.9.0.post0
|
python-dateutil==2.9.0.post0
|
||||||
@@ -39,5 +41,7 @@ six==1.17.0
|
|||||||
# via python-dateutil
|
# via python-dateutil
|
||||||
tk==0.1.0
|
tk==0.1.0
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
|
ttkthemes==3.2.2
|
||||||
|
# via -r requirements.in
|
||||||
tzdata==2025.2
|
tzdata==2025.2
|
||||||
# via pandas
|
# via pandas
|
||||||
|
|||||||
@@ -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)
|
||||||
+220
-13
@@ -17,6 +17,8 @@ from medicine_management_window import MedicineManagementWindow
|
|||||||
from medicine_manager import MedicineManager
|
from medicine_manager import MedicineManager
|
||||||
from pathology_management_window import PathologyManagementWindow
|
from pathology_management_window import PathologyManagementWindow
|
||||||
from pathology_manager import PathologyManager
|
from pathology_manager import PathologyManager
|
||||||
|
from settings_window import SettingsWindow
|
||||||
|
from theme_manager import ThemeManager
|
||||||
from ui_manager import UIManager
|
from ui_manager import UIManager
|
||||||
|
|
||||||
|
|
||||||
@@ -44,6 +46,9 @@ class MedTrackerApp:
|
|||||||
|
|
||||||
logger.info(f"Log level: {LOG_LEVEL}")
|
logger.info(f"Log level: {LOG_LEVEL}")
|
||||||
|
|
||||||
|
# Initialize theme manager first
|
||||||
|
self.theme_manager: ThemeManager = ThemeManager(self.root, logger)
|
||||||
|
|
||||||
if LOG_LEVEL == "DEBUG":
|
if LOG_LEVEL == "DEBUG":
|
||||||
logger.debug(f"Script name: {sys.argv[0]}")
|
logger.debug(f"Script name: {sys.argv[0]}")
|
||||||
logger.debug(f"Logs path: {LOG_PATH}")
|
logger.debug(f"Logs path: {LOG_PATH}")
|
||||||
@@ -54,7 +59,11 @@ class MedTrackerApp:
|
|||||||
self.medicine_manager: MedicineManager = MedicineManager(logger=logger)
|
self.medicine_manager: MedicineManager = MedicineManager(logger=logger)
|
||||||
self.pathology_manager: PathologyManager = PathologyManager(logger=logger)
|
self.pathology_manager: PathologyManager = PathologyManager(logger=logger)
|
||||||
self.ui_manager: UIManager = UIManager(
|
self.ui_manager: UIManager = UIManager(
|
||||||
root, logger, self.medicine_manager, self.pathology_manager
|
root,
|
||||||
|
logger,
|
||||||
|
self.medicine_manager,
|
||||||
|
self.pathology_manager,
|
||||||
|
self.theme_manager,
|
||||||
)
|
)
|
||||||
self.data_manager: DataManager = DataManager(
|
self.data_manager: DataManager = DataManager(
|
||||||
self.filename, logger, self.medicine_manager, self.pathology_manager
|
self.filename, logger, self.medicine_manager, self.pathology_manager
|
||||||
@@ -72,6 +81,9 @@ class MedTrackerApp:
|
|||||||
# Add menu bar
|
# Add menu bar
|
||||||
self._setup_menu()
|
self._setup_menu()
|
||||||
|
|
||||||
|
# Setup keyboard shortcuts
|
||||||
|
self._setup_keyboard_shortcuts()
|
||||||
|
|
||||||
# Center the window on screen
|
# Center the window on screen
|
||||||
self._center_window()
|
self._center_window()
|
||||||
|
|
||||||
@@ -100,7 +112,7 @@ class MedTrackerApp:
|
|||||||
import tkinter.ttk as ttk
|
import tkinter.ttk as ttk
|
||||||
|
|
||||||
# --- Main Frame ---
|
# --- Main Frame ---
|
||||||
main_frame: ttk.Frame = ttk.Frame(self.root, padding="10")
|
main_frame: ttk.Frame = ttk.Frame(self.root, padding="10", style="Card.TFrame")
|
||||||
main_frame.grid(row=0, column=0, sticky="nsew")
|
main_frame.grid(row=0, column=0, sticky="nsew")
|
||||||
|
|
||||||
# Configure root window grid
|
# Configure root window grid
|
||||||
@@ -141,12 +153,12 @@ class MedTrackerApp:
|
|||||||
self.input_frame,
|
self.input_frame,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"text": "Add Entry",
|
"text": "Add Entry (Ctrl+S)",
|
||||||
"command": self.add_new_entry,
|
"command": self.add_new_entry,
|
||||||
"fill": "both",
|
"fill": "both",
|
||||||
"expand": True,
|
"expand": True,
|
||||||
},
|
},
|
||||||
{"text": "Quit", "command": self.handle_window_closing},
|
{"text": "Quit (Ctrl+Q)", "command": self.handle_window_closing},
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -172,19 +184,168 @@ class MedTrackerApp:
|
|||||||
# File menu
|
# File menu
|
||||||
file_menu = tk.Menu(menubar, tearoff=0)
|
file_menu = tk.Menu(menubar, tearoff=0)
|
||||||
menubar.add_cascade(label="File", menu=file_menu)
|
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_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
|
||||||
tools_menu = tk.Menu(menubar, tearoff=0)
|
tools_menu = tk.Menu(menubar, tearoff=0)
|
||||||
menubar.add_cascade(label="Tools", menu=tools_menu)
|
menubar.add_cascade(label="Tools", menu=tools_menu)
|
||||||
tools_menu.add_command(
|
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(
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Theme menu
|
||||||
|
theme_menu = tk.Menu(menubar, tearoff=0)
|
||||||
|
menubar.add_cascade(label="Theme", menu=theme_menu)
|
||||||
|
|
||||||
|
# Add quick theme options
|
||||||
|
available_themes = self.theme_manager.get_available_themes()
|
||||||
|
current_theme = self.theme_manager.get_current_theme()
|
||||||
|
|
||||||
|
for theme in available_themes:
|
||||||
|
theme_menu.add_radiobutton(
|
||||||
|
label=theme.title(),
|
||||||
|
command=lambda t=theme: self._change_theme(t),
|
||||||
|
value=theme == current_theme,
|
||||||
|
)
|
||||||
|
|
||||||
|
theme_menu.add_separator()
|
||||||
|
theme_menu.add_command(
|
||||||
|
label="More Settings...",
|
||||||
|
command=self._open_settings_window,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Help menu
|
||||||
|
help_menu = tk.Menu(menubar, tearoff=0)
|
||||||
|
menubar.add_cascade(label="Help", menu=help_menu)
|
||||||
|
help_menu.add_command(
|
||||||
|
label="Settings...",
|
||||||
|
command=self._open_settings_window,
|
||||||
|
accelerator="F2",
|
||||||
|
)
|
||||||
|
help_menu.add_separator()
|
||||||
|
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())
|
||||||
|
self.root.bind("<F2>", lambda e: self._open_settings_window())
|
||||||
|
|
||||||
|
# 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
|
||||||
|
• F2: Open settings window"""
|
||||||
|
|
||||||
|
messagebox.showinfo("Keyboard Shortcuts", shortcuts_text, parent=self.root)
|
||||||
|
|
||||||
|
def _change_theme(self, theme_name: str) -> None:
|
||||||
|
"""Change the application theme."""
|
||||||
|
if self.theme_manager.apply_theme(theme_name):
|
||||||
|
self.ui_manager.update_status(
|
||||||
|
f"Theme changed to: {theme_name.title()}", "info"
|
||||||
|
)
|
||||||
|
# Refresh the menu to update radio button selection
|
||||||
|
self._setup_menu()
|
||||||
|
else:
|
||||||
|
self.ui_manager.update_status(
|
||||||
|
f"Failed to apply theme: {theme_name}", "error"
|
||||||
|
)
|
||||||
|
|
||||||
|
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:
|
def _open_export_window(self) -> None:
|
||||||
"""Open the export window."""
|
"""Open the export window."""
|
||||||
@@ -205,6 +366,11 @@ class MedTrackerApp:
|
|||||||
self.root, self.medicine_manager, self._refresh_ui_after_config_change
|
self.root, self.medicine_manager, self._refresh_ui_after_config_change
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _open_settings_window(self) -> None:
|
||||||
|
"""Open the settings window."""
|
||||||
|
self.ui_manager.update_status("Opening settings window", "info")
|
||||||
|
SettingsWindow(self.root, self.theme_manager, self.ui_manager)
|
||||||
|
|
||||||
def _refresh_ui_after_config_change(self) -> None:
|
def _refresh_ui_after_config_change(self) -> None:
|
||||||
"""Refresh UI components after pathology or medicine configuration changes."""
|
"""Refresh UI components after pathology or medicine configuration changes."""
|
||||||
self.ui_manager.update_status(
|
self.ui_manager.update_status(
|
||||||
@@ -231,12 +397,12 @@ class MedTrackerApp:
|
|||||||
self.input_frame,
|
self.input_frame,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"text": "Add Entry",
|
"text": "Add Entry (Ctrl+S)",
|
||||||
"command": self.add_new_entry,
|
"command": self.add_new_entry,
|
||||||
"fill": "both",
|
"fill": "both",
|
||||||
"expand": True,
|
"expand": True,
|
||||||
},
|
},
|
||||||
{"text": "Quit", "command": self.handle_window_closing},
|
{"text": "Quit (Ctrl+Q)", "command": self.handle_window_closing},
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -254,6 +420,43 @@ class MedTrackerApp:
|
|||||||
# Update status to show completion
|
# Update status to show completion
|
||||||
self.ui_manager.update_status("UI refreshed successfully", "success")
|
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:
|
def handle_double_click(self, event: tk.Event) -> None:
|
||||||
"""Handle double-click event to edit an entry."""
|
"""Handle double-click event to edit an entry."""
|
||||||
logger.debug("Double-click event triggered on treeview.")
|
logger.debug("Double-click event triggered on treeview.")
|
||||||
@@ -531,9 +734,13 @@ class MedTrackerApp:
|
|||||||
# Fallback - just use all columns
|
# Fallback - just use all columns
|
||||||
display_df = df
|
display_df = df
|
||||||
|
|
||||||
# Batch insert for better performance
|
# Batch insert for better performance with alternating row colors
|
||||||
for _index, row in display_df.iterrows():
|
for index, row in display_df.iterrows():
|
||||||
self.tree.insert(parent="", index="end", values=list(row))
|
# Add alternating row tags for better visibility
|
||||||
|
tag = "evenrow" if index % 2 == 0 else "oddrow"
|
||||||
|
self.tree.insert(
|
||||||
|
parent="", index="end", values=list(row), tags=(tag,)
|
||||||
|
)
|
||||||
logger.debug(f"Loaded {len(display_df)} entries into treeview.")
|
logger.debug(f"Loaded {len(display_df)} entries into treeview.")
|
||||||
|
|
||||||
# Update the graph
|
# Update the graph
|
||||||
|
|||||||
@@ -0,0 +1,324 @@
|
|||||||
|
"""Settings window for TheChart application."""
|
||||||
|
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import messagebox, ttk
|
||||||
|
|
||||||
|
|
||||||
|
class SettingsWindow:
|
||||||
|
"""Settings window for application preferences."""
|
||||||
|
|
||||||
|
def __init__(self, parent: tk.Tk, theme_manager, ui_manager) -> None:
|
||||||
|
self.parent = parent
|
||||||
|
self.theme_manager = theme_manager
|
||||||
|
self.ui_manager = ui_manager
|
||||||
|
|
||||||
|
# Create window
|
||||||
|
self.window = tk.Toplevel(parent)
|
||||||
|
self.window.title("Settings - TheChart")
|
||||||
|
self.window.geometry("500x400")
|
||||||
|
self.window.resizable(False, False)
|
||||||
|
|
||||||
|
# Make window modal
|
||||||
|
self.window.transient(parent)
|
||||||
|
self.window.grab_set()
|
||||||
|
|
||||||
|
# Center the window
|
||||||
|
self._center_window()
|
||||||
|
|
||||||
|
# Setup UI
|
||||||
|
self._setup_ui()
|
||||||
|
|
||||||
|
# Set initial values
|
||||||
|
self._load_current_settings()
|
||||||
|
|
||||||
|
def _center_window(self) -> None:
|
||||||
|
"""Center the settings window on the parent."""
|
||||||
|
self.window.update_idletasks()
|
||||||
|
|
||||||
|
# Get window dimensions
|
||||||
|
window_width = self.window.winfo_reqwidth()
|
||||||
|
window_height = self.window.winfo_reqheight()
|
||||||
|
|
||||||
|
# Get parent window position and size
|
||||||
|
parent_x = self.parent.winfo_x()
|
||||||
|
parent_y = self.parent.winfo_y()
|
||||||
|
parent_width = self.parent.winfo_width()
|
||||||
|
parent_height = self.parent.winfo_height()
|
||||||
|
|
||||||
|
# Calculate centered position
|
||||||
|
x = parent_x + (parent_width // 2) - (window_width // 2)
|
||||||
|
y = parent_y + (parent_height // 2) - (window_height // 2)
|
||||||
|
|
||||||
|
self.window.geometry(f"{window_width}x{window_height}+{x}+{y}")
|
||||||
|
|
||||||
|
def _setup_ui(self) -> None:
|
||||||
|
"""Setup the settings UI."""
|
||||||
|
# Main container
|
||||||
|
main_frame = ttk.Frame(self.window, padding="20", style="Card.TFrame")
|
||||||
|
main_frame.pack(fill="both", expand=True)
|
||||||
|
|
||||||
|
# Title
|
||||||
|
title_label = ttk.Label(
|
||||||
|
main_frame,
|
||||||
|
text="Application Settings",
|
||||||
|
font=("TkDefaultFont", 16, "bold"),
|
||||||
|
)
|
||||||
|
title_label.pack(pady=(0, 20))
|
||||||
|
|
||||||
|
# Create notebook for different setting categories
|
||||||
|
notebook = ttk.Notebook(main_frame, style="Modern.TNotebook")
|
||||||
|
notebook.pack(fill="both", expand=True, pady=(0, 20))
|
||||||
|
|
||||||
|
# Theme settings tab
|
||||||
|
self._create_theme_tab(notebook)
|
||||||
|
|
||||||
|
# UI settings tab
|
||||||
|
self._create_ui_tab(notebook)
|
||||||
|
|
||||||
|
# About tab
|
||||||
|
self._create_about_tab(notebook)
|
||||||
|
|
||||||
|
# Button frame
|
||||||
|
button_frame = ttk.Frame(main_frame)
|
||||||
|
button_frame.pack(fill="x", pady=(10, 0))
|
||||||
|
|
||||||
|
# Buttons
|
||||||
|
ttk.Button(
|
||||||
|
button_frame,
|
||||||
|
text="Apply",
|
||||||
|
command=self._apply_settings,
|
||||||
|
style="Action.TButton",
|
||||||
|
).pack(side="right", padx=(5, 0))
|
||||||
|
|
||||||
|
ttk.Button(
|
||||||
|
button_frame,
|
||||||
|
text="Cancel",
|
||||||
|
command=self._cancel,
|
||||||
|
style="Action.TButton",
|
||||||
|
).pack(side="right")
|
||||||
|
|
||||||
|
ttk.Button(
|
||||||
|
button_frame,
|
||||||
|
text="OK",
|
||||||
|
command=self._ok,
|
||||||
|
style="Action.TButton",
|
||||||
|
).pack(side="right", padx=(0, 5))
|
||||||
|
|
||||||
|
def _create_theme_tab(self, notebook: ttk.Notebook) -> None:
|
||||||
|
"""Create the theme settings tab."""
|
||||||
|
theme_frame = ttk.Frame(notebook, style="Card.TFrame")
|
||||||
|
notebook.add(theme_frame, text="Theme")
|
||||||
|
|
||||||
|
# Theme selection
|
||||||
|
theme_label_frame = ttk.LabelFrame(
|
||||||
|
theme_frame, text="Theme Selection", style="Card.TLabelframe"
|
||||||
|
)
|
||||||
|
theme_label_frame.pack(fill="x", padx=10, pady=10)
|
||||||
|
|
||||||
|
ttk.Label(
|
||||||
|
theme_label_frame,
|
||||||
|
text="Choose your preferred theme:",
|
||||||
|
font=("TkDefaultFont", 10),
|
||||||
|
).pack(anchor="w", padx=10, pady=(10, 5))
|
||||||
|
|
||||||
|
# Theme radio buttons
|
||||||
|
self.theme_var = tk.StringVar()
|
||||||
|
themes = self.theme_manager.get_available_themes()
|
||||||
|
|
||||||
|
theme_buttons_frame = ttk.Frame(theme_label_frame)
|
||||||
|
theme_buttons_frame.pack(fill="x", padx=10, pady=(0, 10))
|
||||||
|
|
||||||
|
# Create radio buttons in a grid
|
||||||
|
for i, theme in enumerate(themes):
|
||||||
|
row = i // 3
|
||||||
|
col = i % 3
|
||||||
|
|
||||||
|
ttk.Radiobutton(
|
||||||
|
theme_buttons_frame,
|
||||||
|
text=theme.title(),
|
||||||
|
variable=self.theme_var,
|
||||||
|
value=theme,
|
||||||
|
style="Modern.TCheckbutton",
|
||||||
|
).grid(row=row, column=col, sticky="w", padx=5, pady=2)
|
||||||
|
|
||||||
|
# Theme preview info
|
||||||
|
preview_frame = ttk.LabelFrame(
|
||||||
|
theme_frame, text="Theme Preview", style="Card.TLabelframe"
|
||||||
|
)
|
||||||
|
preview_frame.pack(fill="both", expand=True, padx=10, pady=(0, 10))
|
||||||
|
|
||||||
|
preview_text = tk.Text(
|
||||||
|
preview_frame,
|
||||||
|
height=6,
|
||||||
|
wrap="word",
|
||||||
|
font=("TkDefaultFont", 9),
|
||||||
|
state="disabled",
|
||||||
|
)
|
||||||
|
preview_text.pack(fill="both", expand=True, padx=10, pady=10)
|
||||||
|
|
||||||
|
# Theme change callback
|
||||||
|
def on_theme_change():
|
||||||
|
selected_theme = self.theme_var.get()
|
||||||
|
preview_text.config(state="normal")
|
||||||
|
preview_text.delete("1.0", "end")
|
||||||
|
preview_text.insert(
|
||||||
|
"1.0",
|
||||||
|
f"Selected theme: {selected_theme.title()}\\n\\n"
|
||||||
|
"Theme changes will be applied when you click 'Apply' or 'OK'. "
|
||||||
|
"The new theme will affect all windows and UI elements "
|
||||||
|
"in the application.",
|
||||||
|
)
|
||||||
|
preview_text.config(state="disabled")
|
||||||
|
|
||||||
|
self.theme_var.trace("w", lambda *args: on_theme_change())
|
||||||
|
|
||||||
|
def _create_ui_tab(self, notebook: ttk.Notebook) -> None:
|
||||||
|
"""Create the UI settings tab."""
|
||||||
|
ui_frame = ttk.Frame(notebook, style="Card.TFrame")
|
||||||
|
notebook.add(ui_frame, text="Interface")
|
||||||
|
|
||||||
|
# Font settings
|
||||||
|
font_frame = ttk.LabelFrame(
|
||||||
|
ui_frame, text="Font Settings", style="Card.TLabelframe"
|
||||||
|
)
|
||||||
|
font_frame.pack(fill="x", padx=10, pady=10)
|
||||||
|
|
||||||
|
ttk.Label(
|
||||||
|
font_frame,
|
||||||
|
text="Font size adjustments (requires restart):",
|
||||||
|
font=("TkDefaultFont", 10),
|
||||||
|
).pack(anchor="w", padx=10, pady=10)
|
||||||
|
|
||||||
|
# Font size scale
|
||||||
|
self.font_scale_var = tk.DoubleVar(value=1.0)
|
||||||
|
font_scale = ttk.Scale(
|
||||||
|
font_frame,
|
||||||
|
from_=0.8,
|
||||||
|
to=1.5,
|
||||||
|
variable=self.font_scale_var,
|
||||||
|
orient="horizontal",
|
||||||
|
style="Modern.Horizontal.TScale",
|
||||||
|
)
|
||||||
|
font_scale.pack(fill="x", padx=10, pady=(0, 10))
|
||||||
|
|
||||||
|
# Scale labels
|
||||||
|
scale_labels_frame = ttk.Frame(font_frame)
|
||||||
|
scale_labels_frame.pack(fill="x", padx=10, pady=(0, 10))
|
||||||
|
|
||||||
|
ttk.Label(scale_labels_frame, text="Small").pack(side="left")
|
||||||
|
ttk.Label(scale_labels_frame, text="Large").pack(side="right")
|
||||||
|
ttk.Label(scale_labels_frame, text="Normal").pack()
|
||||||
|
|
||||||
|
# Window settings
|
||||||
|
window_frame = ttk.LabelFrame(
|
||||||
|
ui_frame, text="Window Settings", style="Card.TLabelframe"
|
||||||
|
)
|
||||||
|
window_frame.pack(fill="x", padx=10, pady=(0, 10))
|
||||||
|
|
||||||
|
# Remember window size
|
||||||
|
self.remember_size_var = tk.BooleanVar(value=True)
|
||||||
|
ttk.Checkbutton(
|
||||||
|
window_frame,
|
||||||
|
text="Remember window size and position",
|
||||||
|
variable=self.remember_size_var,
|
||||||
|
style="Modern.TCheckbutton",
|
||||||
|
).pack(anchor="w", padx=10, pady=10)
|
||||||
|
|
||||||
|
# Always on top
|
||||||
|
self.always_on_top_var = tk.BooleanVar(value=False)
|
||||||
|
ttk.Checkbutton(
|
||||||
|
window_frame,
|
||||||
|
text="Keep window always on top",
|
||||||
|
variable=self.always_on_top_var,
|
||||||
|
style="Modern.TCheckbutton",
|
||||||
|
).pack(anchor="w", padx=10, pady=(0, 10))
|
||||||
|
|
||||||
|
def _create_about_tab(self, notebook: ttk.Notebook) -> None:
|
||||||
|
"""Create the about tab."""
|
||||||
|
about_frame = ttk.Frame(notebook, style="Card.TFrame")
|
||||||
|
notebook.add(about_frame, text="About")
|
||||||
|
|
||||||
|
# App info
|
||||||
|
info_frame = ttk.LabelFrame(
|
||||||
|
about_frame, text="Application Information", style="Card.TLabelframe"
|
||||||
|
)
|
||||||
|
info_frame.pack(fill="both", expand=True, padx=10, pady=10)
|
||||||
|
|
||||||
|
about_text = tk.Text(
|
||||||
|
info_frame,
|
||||||
|
wrap="word",
|
||||||
|
font=("TkDefaultFont", 10),
|
||||||
|
state="disabled",
|
||||||
|
bg=self.theme_manager.get_theme_colors()["bg"],
|
||||||
|
fg=self.theme_manager.get_theme_colors()["fg"],
|
||||||
|
)
|
||||||
|
about_text.pack(fill="both", expand=True, padx=10, pady=10)
|
||||||
|
|
||||||
|
about_content = """TheChart - Medication Tracker
|
||||||
|
|
||||||
|
Version: 1.9.5
|
||||||
|
Built with: Python, Tkinter, ttkthemes
|
||||||
|
|
||||||
|
Features:
|
||||||
|
• Modern themed interface with multiple themes
|
||||||
|
• Medication and pathology tracking
|
||||||
|
• Visual graphs and charts
|
||||||
|
• Data export capabilities
|
||||||
|
• Keyboard shortcuts for efficiency
|
||||||
|
• Customizable UI settings
|
||||||
|
|
||||||
|
This application helps you track your daily medications and health
|
||||||
|
conditions with an intuitive, modern interface.
|
||||||
|
|
||||||
|
Enhanced with ttkthemes for better visual appeal and user experience."""
|
||||||
|
|
||||||
|
about_text.config(state="normal")
|
||||||
|
about_text.insert("1.0", about_content)
|
||||||
|
about_text.config(state="disabled")
|
||||||
|
|
||||||
|
def _load_current_settings(self) -> None:
|
||||||
|
"""Load current application settings."""
|
||||||
|
# Set current theme
|
||||||
|
current_theme = self.theme_manager.get_current_theme()
|
||||||
|
self.theme_var.set(current_theme)
|
||||||
|
|
||||||
|
# Trigger theme change to update preview
|
||||||
|
if hasattr(self, "theme_var"):
|
||||||
|
self.theme_var.set(current_theme)
|
||||||
|
|
||||||
|
def _apply_settings(self) -> None:
|
||||||
|
"""Apply the selected settings."""
|
||||||
|
# Apply theme if changed
|
||||||
|
selected_theme = self.theme_var.get()
|
||||||
|
current_theme = self.theme_manager.get_current_theme()
|
||||||
|
|
||||||
|
if selected_theme != current_theme:
|
||||||
|
if self.theme_manager.apply_theme(selected_theme):
|
||||||
|
self.ui_manager.update_status(
|
||||||
|
f"Theme changed to: {selected_theme.title()}", "info"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
messagebox.showerror(
|
||||||
|
"Error",
|
||||||
|
f"Failed to apply theme: {selected_theme}",
|
||||||
|
parent=self.window,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Apply other settings (font size, window settings, etc.)
|
||||||
|
# These would typically be saved to a config file
|
||||||
|
|
||||||
|
messagebox.showinfo(
|
||||||
|
"Settings Applied",
|
||||||
|
"Settings have been applied successfully!",
|
||||||
|
parent=self.window,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _ok(self) -> None:
|
||||||
|
"""Apply settings and close window."""
|
||||||
|
self._apply_settings()
|
||||||
|
self.window.destroy()
|
||||||
|
|
||||||
|
def _cancel(self) -> None:
|
||||||
|
"""Close window without applying settings."""
|
||||||
|
self.window.destroy()
|
||||||
@@ -0,0 +1,298 @@
|
|||||||
|
"""Theme manager for the application using ttkthemes."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import ttk
|
||||||
|
|
||||||
|
from ttkthemes import ThemedStyle
|
||||||
|
|
||||||
|
|
||||||
|
class ThemeManager:
|
||||||
|
"""Manages application themes and styling."""
|
||||||
|
|
||||||
|
def __init__(self, root: tk.Tk, logger: logging.Logger) -> None:
|
||||||
|
self.root = root
|
||||||
|
self.logger = logger
|
||||||
|
self.style: ThemedStyle | None = None
|
||||||
|
self.current_theme: str = "arc" # Default theme
|
||||||
|
|
||||||
|
# Available themes - these are some of the best looking ones
|
||||||
|
self.available_themes = [
|
||||||
|
"arc",
|
||||||
|
"equilux",
|
||||||
|
"adapta",
|
||||||
|
"yaru",
|
||||||
|
"ubuntu",
|
||||||
|
"plastik",
|
||||||
|
"breeze",
|
||||||
|
"elegance",
|
||||||
|
]
|
||||||
|
|
||||||
|
self.initialize_theme()
|
||||||
|
|
||||||
|
def initialize_theme(self) -> None:
|
||||||
|
"""Initialize the themed style."""
|
||||||
|
try:
|
||||||
|
self.style = ThemedStyle(self.root)
|
||||||
|
self.apply_theme(self.current_theme)
|
||||||
|
self._configure_custom_styles()
|
||||||
|
self.logger.info(
|
||||||
|
f"Theme manager initialized with theme: {self.current_theme}"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Failed to initialize theme manager: {e}")
|
||||||
|
# Fallback to default ttk styling
|
||||||
|
self.style = ttk.Style()
|
||||||
|
|
||||||
|
def apply_theme(self, theme_name: str) -> bool:
|
||||||
|
"""Apply a specific theme."""
|
||||||
|
try:
|
||||||
|
if self.style and theme_name in self.get_available_themes():
|
||||||
|
self.style.set_theme(theme_name)
|
||||||
|
self.current_theme = theme_name
|
||||||
|
self._configure_custom_styles()
|
||||||
|
self.logger.info(f"Applied theme: {theme_name}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self.logger.warning(f"Theme '{theme_name}' not available")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Failed to apply theme '{theme_name}': {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_available_themes(self) -> list[str]:
|
||||||
|
"""Get list of available themes."""
|
||||||
|
if self.style:
|
||||||
|
try:
|
||||||
|
# Get all available themes from ttkthemes
|
||||||
|
all_themes = self.style.theme_names()
|
||||||
|
# Filter to only include our curated list
|
||||||
|
return [theme for theme in self.available_themes if theme in all_themes]
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Failed to get available themes: {e}")
|
||||||
|
return self.available_themes
|
||||||
|
return self.available_themes
|
||||||
|
|
||||||
|
def get_current_theme(self) -> str:
|
||||||
|
"""Get the currently active theme."""
|
||||||
|
return self.current_theme
|
||||||
|
|
||||||
|
def _configure_custom_styles(self) -> None:
|
||||||
|
"""Configure custom styles for better appearance."""
|
||||||
|
if not self.style:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Get current theme colors for consistent styling
|
||||||
|
colors = self.get_theme_colors()
|
||||||
|
|
||||||
|
# Configure frame styles with better padding and borders
|
||||||
|
self.style.configure(
|
||||||
|
"Card.TFrame",
|
||||||
|
relief="flat",
|
||||||
|
borderwidth=0,
|
||||||
|
background=colors["bg"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Configure label frame styles with modern appearance
|
||||||
|
self.style.configure(
|
||||||
|
"Card.TLabelframe",
|
||||||
|
relief="solid",
|
||||||
|
borderwidth=1,
|
||||||
|
background=colors["bg"],
|
||||||
|
foreground=colors["fg"],
|
||||||
|
padding=(10, 5, 10, 10),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.style.configure(
|
||||||
|
"Card.TLabelframe.Label",
|
||||||
|
background=colors["bg"],
|
||||||
|
foreground=colors["fg"],
|
||||||
|
font=("TkDefaultFont", 10, "bold"),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Configure button styles for better appearance
|
||||||
|
self.style.configure(
|
||||||
|
"Action.TButton",
|
||||||
|
padding=(15, 8),
|
||||||
|
font=("TkDefaultFont", 9, "normal"),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Configure entry styles with modern look
|
||||||
|
self.style.configure(
|
||||||
|
"Modern.TEntry",
|
||||||
|
padding=(8, 5),
|
||||||
|
borderwidth=1,
|
||||||
|
relief="solid",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Configure scale styles for pathology inputs
|
||||||
|
self.style.configure(
|
||||||
|
"Modern.Horizontal.TScale",
|
||||||
|
borderwidth=0,
|
||||||
|
background=colors["bg"],
|
||||||
|
troughcolor="#e0e0e0",
|
||||||
|
lightcolor=colors["select_bg"],
|
||||||
|
darkcolor=colors["select_bg"],
|
||||||
|
focuscolor=colors["select_bg"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Configure treeview for better data display
|
||||||
|
self.style.configure(
|
||||||
|
"Modern.Treeview",
|
||||||
|
rowheight=28,
|
||||||
|
borderwidth=1,
|
||||||
|
relief="solid",
|
||||||
|
background=colors["bg"],
|
||||||
|
foreground=colors["fg"],
|
||||||
|
fieldbackground=colors["bg"],
|
||||||
|
selectbackground=colors["select_bg"],
|
||||||
|
selectforeground=colors["select_fg"],
|
||||||
|
)
|
||||||
|
|
||||||
|
self.style.configure(
|
||||||
|
"Modern.Treeview.Heading",
|
||||||
|
padding=(8, 6),
|
||||||
|
relief="flat",
|
||||||
|
borderwidth=1,
|
||||||
|
background=colors["select_bg"],
|
||||||
|
foreground=colors["select_fg"],
|
||||||
|
font=("TkDefaultFont", 9, "bold"),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Configure comprehensive row selection colors for better visibility
|
||||||
|
self.style.map(
|
||||||
|
"Modern.Treeview",
|
||||||
|
background=[
|
||||||
|
("selected", colors["select_bg"]),
|
||||||
|
("active", colors["select_bg"]),
|
||||||
|
("focus", colors["select_bg"]),
|
||||||
|
("", colors["bg"]),
|
||||||
|
],
|
||||||
|
foreground=[
|
||||||
|
("selected", colors["select_fg"]),
|
||||||
|
("active", colors["select_fg"]),
|
||||||
|
("focus", colors["select_fg"]),
|
||||||
|
("", colors["fg"]),
|
||||||
|
],
|
||||||
|
selectbackground=[
|
||||||
|
("focus", colors["select_bg"]),
|
||||||
|
("", colors["select_bg"]),
|
||||||
|
],
|
||||||
|
selectforeground=[
|
||||||
|
("focus", colors["select_fg"]),
|
||||||
|
("", colors["select_fg"]),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Configure notebook tabs with modern styling
|
||||||
|
self.style.configure(
|
||||||
|
"Modern.TNotebook.Tab",
|
||||||
|
padding=(15, 8),
|
||||||
|
borderwidth=1,
|
||||||
|
relief="flat",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.style.map(
|
||||||
|
"Modern.TNotebook.Tab",
|
||||||
|
background=[("selected", colors["select_bg"])],
|
||||||
|
foreground=[("selected", colors["select_fg"])],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Configure checkbutton for medicine selection
|
||||||
|
self.style.configure(
|
||||||
|
"Modern.TCheckbutton",
|
||||||
|
padding=(8, 4),
|
||||||
|
background=colors["bg"],
|
||||||
|
foreground=colors["fg"],
|
||||||
|
focuscolor=colors["select_bg"],
|
||||||
|
)
|
||||||
|
|
||||||
|
self.logger.debug("Enhanced custom styles configured")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Failed to configure custom styles: {e}")
|
||||||
|
|
||||||
|
def configure_widget_style(self, widget: tk.Widget, style_name: str) -> None:
|
||||||
|
"""Apply a specific style to a widget."""
|
||||||
|
try:
|
||||||
|
if hasattr(widget, "configure") and self.style:
|
||||||
|
widget.configure(style=style_name)
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Failed to configure widget style '{style_name}': {e}")
|
||||||
|
|
||||||
|
def get_theme_colors(self) -> dict[str, str]:
|
||||||
|
"""Get current theme colors for custom widgets."""
|
||||||
|
if not self.style:
|
||||||
|
return {
|
||||||
|
"bg": "#ffffff",
|
||||||
|
"fg": "#000000",
|
||||||
|
"select_bg": "#3584e4",
|
||||||
|
"select_fg": "#ffffff",
|
||||||
|
"alt_bg": "#f5f5f5",
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Get colors from current theme
|
||||||
|
bg = self.style.lookup("TFrame", "background") or "#ffffff"
|
||||||
|
fg = self.style.lookup("TLabel", "foreground") or "#000000"
|
||||||
|
|
||||||
|
# Try to get better selection colors from different widget states
|
||||||
|
select_bg = (
|
||||||
|
self.style.lookup("TButton", "background", ["pressed"])
|
||||||
|
or self.style.lookup("TButton", "background", ["active"])
|
||||||
|
or self.style.lookup("Treeview", "selectbackground")
|
||||||
|
or "#0078d4" # Modern blue fallback
|
||||||
|
)
|
||||||
|
select_fg = (
|
||||||
|
self.style.lookup("TButton", "foreground", ["pressed"])
|
||||||
|
or self.style.lookup("TButton", "foreground", ["active"])
|
||||||
|
or self.style.lookup("Treeview", "selectforeground")
|
||||||
|
or "#ffffff" # White fallback
|
||||||
|
)
|
||||||
|
|
||||||
|
# Ensure contrast - if selection colors are too similar to background,
|
||||||
|
# use fallbacks
|
||||||
|
if select_bg == bg or select_bg.lower() == bg.lower():
|
||||||
|
select_bg = "#0078d4" if bg != "#0078d4" else "#0066cc"
|
||||||
|
|
||||||
|
if select_fg == fg or select_fg.lower() == fg.lower():
|
||||||
|
select_fg = "#ffffff" if fg != "#ffffff" else "#000000"
|
||||||
|
|
||||||
|
# Calculate alternating row color
|
||||||
|
if bg.startswith("#"):
|
||||||
|
try:
|
||||||
|
rgb = tuple(int(bg[i : i + 2], 16) for i in (1, 3, 5))
|
||||||
|
if sum(rgb) > 384: # Light theme
|
||||||
|
alt_bg = (
|
||||||
|
f"#{max(0, rgb[0] - 10):02x}"
|
||||||
|
f"{max(0, rgb[1] - 10):02x}"
|
||||||
|
f"{max(0, rgb[2] - 10):02x}"
|
||||||
|
)
|
||||||
|
else: # Dark theme
|
||||||
|
alt_bg = (
|
||||||
|
f"#{min(255, rgb[0] + 10):02x}"
|
||||||
|
f"{min(255, rgb[1] + 10):02x}"
|
||||||
|
f"{min(255, rgb[2] + 10):02x}"
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
alt_bg = "#f5f5f5"
|
||||||
|
else:
|
||||||
|
alt_bg = "#f5f5f5"
|
||||||
|
|
||||||
|
return {
|
||||||
|
"bg": bg,
|
||||||
|
"fg": fg,
|
||||||
|
"select_bg": select_bg,
|
||||||
|
"select_fg": select_fg,
|
||||||
|
"alt_bg": alt_bg, # Add alternating background color
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Failed to get theme colors: {e}")
|
||||||
|
return {
|
||||||
|
"bg": "#ffffff",
|
||||||
|
"fg": "#000000",
|
||||||
|
"select_bg": "#3584e4",
|
||||||
|
"select_fg": "#ffffff",
|
||||||
|
"alt_bg": "#f5f5f5",
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
"""Tooltip system for enhanced user experience."""
|
||||||
|
|
||||||
|
import tkinter as tk
|
||||||
|
|
||||||
|
|
||||||
|
class ToolTip:
|
||||||
|
"""Create a tooltip for a given widget."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
widget: tk.Widget,
|
||||||
|
text: str,
|
||||||
|
delay: int = 500,
|
||||||
|
wrap_length: int = 250,
|
||||||
|
) -> None:
|
||||||
|
self.widget = widget
|
||||||
|
self.text = text
|
||||||
|
self.delay = delay
|
||||||
|
self.wrap_length = wrap_length
|
||||||
|
self.tooltip: tk.Toplevel | None = None
|
||||||
|
self.id_after: str | None = None
|
||||||
|
|
||||||
|
# Bind events
|
||||||
|
self.widget.bind("<Enter>", self._on_enter)
|
||||||
|
self.widget.bind("<Leave>", self._on_leave)
|
||||||
|
self.widget.bind("<ButtonPress>", self._on_leave)
|
||||||
|
|
||||||
|
def _on_enter(self, event: tk.Event | None = None) -> None:
|
||||||
|
"""Mouse entered widget - schedule tooltip."""
|
||||||
|
self._cancel_scheduled()
|
||||||
|
self.id_after = self.widget.after(self.delay, self._show_tooltip)
|
||||||
|
|
||||||
|
def _on_leave(self, event: tk.Event | None = None) -> None:
|
||||||
|
"""Mouse left widget - hide tooltip."""
|
||||||
|
self._cancel_scheduled()
|
||||||
|
self._hide_tooltip()
|
||||||
|
|
||||||
|
def _cancel_scheduled(self) -> None:
|
||||||
|
"""Cancel any scheduled tooltip."""
|
||||||
|
if self.id_after:
|
||||||
|
self.widget.after_cancel(self.id_after)
|
||||||
|
self.id_after = None
|
||||||
|
|
||||||
|
def _show_tooltip(self) -> None:
|
||||||
|
"""Display the tooltip."""
|
||||||
|
if self.tooltip:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get widget position
|
||||||
|
x = self.widget.winfo_rootx() + 25
|
||||||
|
y = self.widget.winfo_rooty() + 25
|
||||||
|
|
||||||
|
# Create tooltip window
|
||||||
|
self.tooltip = tk.Toplevel(self.widget)
|
||||||
|
self.tooltip.wm_overrideredirect(True)
|
||||||
|
self.tooltip.wm_geometry(f"+{x}+{y}")
|
||||||
|
|
||||||
|
# Create tooltip content
|
||||||
|
label = tk.Label(
|
||||||
|
self.tooltip,
|
||||||
|
text=self.text,
|
||||||
|
justify="left",
|
||||||
|
background="#ffffe0",
|
||||||
|
foreground="#000000",
|
||||||
|
relief="solid",
|
||||||
|
borderwidth=1,
|
||||||
|
font=("TkDefaultFont", "9", "normal"),
|
||||||
|
wraplength=self.wrap_length,
|
||||||
|
padx=8,
|
||||||
|
pady=6,
|
||||||
|
)
|
||||||
|
label.pack()
|
||||||
|
|
||||||
|
# Make sure tooltip appears above other windows
|
||||||
|
self.tooltip.lift()
|
||||||
|
|
||||||
|
def _hide_tooltip(self) -> None:
|
||||||
|
"""Hide the tooltip."""
|
||||||
|
if self.tooltip:
|
||||||
|
self.tooltip.destroy()
|
||||||
|
self.tooltip = None
|
||||||
|
|
||||||
|
def update_text(self, new_text: str) -> None:
|
||||||
|
"""Update the tooltip text."""
|
||||||
|
self.text = new_text
|
||||||
|
|
||||||
|
|
||||||
|
class TooltipManager:
|
||||||
|
"""Manages tooltips for UI elements."""
|
||||||
|
|
||||||
|
def __init__(self, theme_manager) -> None:
|
||||||
|
self.theme_manager = theme_manager
|
||||||
|
self.tooltips: list[ToolTip] = []
|
||||||
|
|
||||||
|
def add_tooltip(
|
||||||
|
self,
|
||||||
|
widget: tk.Widget,
|
||||||
|
text: str,
|
||||||
|
delay: int = 500,
|
||||||
|
wrap_length: int = 250,
|
||||||
|
) -> ToolTip:
|
||||||
|
"""Add a tooltip to a widget."""
|
||||||
|
tooltip = ToolTip(widget, text, delay, wrap_length)
|
||||||
|
self.tooltips.append(tooltip)
|
||||||
|
return tooltip
|
||||||
|
|
||||||
|
def add_scale_tooltip(self, scale_widget: tk.Widget, pathology_name: str) -> None:
|
||||||
|
"""Add a specialized tooltip for pathology scales."""
|
||||||
|
text = (
|
||||||
|
f"Adjust your {pathology_name} level\\n"
|
||||||
|
"• Drag the slider to set your current level\\n"
|
||||||
|
"• Higher values typically indicate worse symptoms\\n"
|
||||||
|
"• Use the full range for accurate tracking"
|
||||||
|
)
|
||||||
|
self.add_tooltip(scale_widget, text, delay=800)
|
||||||
|
|
||||||
|
def add_medicine_tooltip(self, widget: tk.Widget, medicine_name: str) -> None:
|
||||||
|
"""Add a specialized tooltip for medicine checkboxes."""
|
||||||
|
text = (
|
||||||
|
f"Mark if you took {medicine_name} today\\n"
|
||||||
|
"• Check the box when you've taken this medication\\n"
|
||||||
|
"• This helps track your medication adherence\\n"
|
||||||
|
"• You can add dose details when editing entries"
|
||||||
|
)
|
||||||
|
self.add_tooltip(widget, text, delay=600)
|
||||||
|
|
||||||
|
def add_button_tooltip(self, widget: tk.Widget, action: str) -> None:
|
||||||
|
"""Add a tooltip for action buttons."""
|
||||||
|
tooltips_map = {
|
||||||
|
"save": (
|
||||||
|
"Save your current entry (Ctrl+S)\\nThis will add a new daily record"
|
||||||
|
),
|
||||||
|
"export": (
|
||||||
|
"Export your data to various formats\\n"
|
||||||
|
"Supports CSV, PDF, and image exports"
|
||||||
|
),
|
||||||
|
"refresh": (
|
||||||
|
"Reload data from file (F5)\\nUpdates the display with latest changes"
|
||||||
|
),
|
||||||
|
"settings": (
|
||||||
|
"Open application settings (F2)\\nCustomize themes and preferences"
|
||||||
|
),
|
||||||
|
"quit": (
|
||||||
|
"Exit the application (Ctrl+Q)\\nYour data will be automatically saved"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
text = tooltips_map.get(action, f"Perform {action} action")
|
||||||
|
self.add_tooltip(widget, text, delay=400)
|
||||||
|
|
||||||
|
def add_menu_tooltip(self, widget: tk.Widget, menu_type: str) -> None:
|
||||||
|
"""Add tooltips for menu items."""
|
||||||
|
tooltips_map = {
|
||||||
|
"theme": (
|
||||||
|
"Quick theme selection\\nClick to instantly change the app's appearance"
|
||||||
|
),
|
||||||
|
"file": "File operations\\nExport data and manage files",
|
||||||
|
"tools": ("Data management tools\\nConfigure medicines and pathologies"),
|
||||||
|
"help": ("Get help and information\\nKeyboard shortcuts and about dialog"),
|
||||||
|
}
|
||||||
|
|
||||||
|
text = tooltips_map.get(menu_type, "Menu options")
|
||||||
|
self.add_tooltip(widget, text, delay=600)
|
||||||
+98
-16
@@ -11,6 +11,7 @@ from PIL import Image, ImageTk
|
|||||||
|
|
||||||
from medicine_manager import MedicineManager
|
from medicine_manager import MedicineManager
|
||||||
from pathology_manager import PathologyManager
|
from pathology_manager import PathologyManager
|
||||||
|
from tooltip_system import TooltipManager
|
||||||
|
|
||||||
|
|
||||||
class UIManager:
|
class UIManager:
|
||||||
@@ -22,17 +23,22 @@ class UIManager:
|
|||||||
logger: logging.Logger,
|
logger: logging.Logger,
|
||||||
medicine_manager: MedicineManager,
|
medicine_manager: MedicineManager,
|
||||||
pathology_manager: PathologyManager,
|
pathology_manager: PathologyManager,
|
||||||
|
theme_manager, # Import would create circular dependency
|
||||||
) -> None:
|
) -> None:
|
||||||
self.root: tk.Tk = root
|
self.root: tk.Tk = root
|
||||||
self.logger: logging.Logger = logger
|
self.logger: logging.Logger = logger
|
||||||
self.medicine_manager = medicine_manager
|
self.medicine_manager = medicine_manager
|
||||||
self.pathology_manager = pathology_manager
|
self.pathology_manager = pathology_manager
|
||||||
|
self.theme_manager = theme_manager
|
||||||
|
|
||||||
# Status bar attributes
|
# Status bar attributes
|
||||||
self.status_bar: tk.Frame | None = None
|
self.status_bar: tk.Frame | None = None
|
||||||
self.status_label: tk.Label | None = None
|
self.status_label: tk.Label | None = None
|
||||||
self.file_info_label: tk.Label | None = None
|
self.file_info_label: tk.Label | None = None
|
||||||
|
|
||||||
|
# Initialize tooltip manager
|
||||||
|
self.tooltip_manager = TooltipManager(theme_manager)
|
||||||
|
|
||||||
def setup_application_icon(self, img_path: str) -> bool:
|
def setup_application_icon(self, img_path: str) -> bool:
|
||||||
"""Set up the application icon."""
|
"""Set up the application icon."""
|
||||||
try:
|
try:
|
||||||
@@ -70,13 +76,20 @@ class UIManager:
|
|||||||
def create_input_frame(self, parent_frame: ttk.Frame) -> dict[str, Any]:
|
def create_input_frame(self, parent_frame: ttk.Frame) -> dict[str, Any]:
|
||||||
"""Create and configure the input frame with all widgets."""
|
"""Create and configure the input frame with all widgets."""
|
||||||
# Create main container for the scrollable input frame
|
# Create main container for the scrollable input frame
|
||||||
main_container = ttk.LabelFrame(parent_frame, text="New Entry")
|
main_container = ttk.LabelFrame(
|
||||||
|
parent_frame, text="New Entry", style="Card.TLabelframe"
|
||||||
|
)
|
||||||
main_container.grid(row=1, column=0, padx=10, pady=10, sticky="nsew")
|
main_container.grid(row=1, column=0, padx=10, pady=10, sticky="nsew")
|
||||||
main_container.grid_rowconfigure(0, weight=1)
|
main_container.grid_rowconfigure(0, weight=1)
|
||||||
main_container.grid_columnconfigure(0, weight=1)
|
main_container.grid_columnconfigure(0, weight=1)
|
||||||
|
|
||||||
# Create canvas and scrollbar for scrolling
|
# Create canvas and scrollbar for scrolling
|
||||||
canvas = tk.Canvas(main_container, highlightthickness=0)
|
theme_colors = self.theme_manager.get_theme_colors()
|
||||||
|
canvas = tk.Canvas(
|
||||||
|
main_container,
|
||||||
|
highlightthickness=0,
|
||||||
|
bg=theme_colors["bg"],
|
||||||
|
)
|
||||||
scrollbar = ttk.Scrollbar(
|
scrollbar = ttk.Scrollbar(
|
||||||
main_container, orient="vertical", command=canvas.yview
|
main_container, orient="vertical", command=canvas.yview
|
||||||
)
|
)
|
||||||
@@ -164,7 +177,9 @@ class UIManager:
|
|||||||
ttk.Label(input_frame, text="Treatment:").grid(
|
ttk.Label(input_frame, text="Treatment:").grid(
|
||||||
row=medicine_row, column=0, sticky="w", padx=5, pady=2
|
row=medicine_row, column=0, sticky="w", padx=5, pady=2
|
||||||
)
|
)
|
||||||
medicine_frame = ttk.LabelFrame(input_frame, text="Medicine")
|
medicine_frame = ttk.LabelFrame(
|
||||||
|
input_frame, text="Medicine", style="Card.TLabelframe"
|
||||||
|
)
|
||||||
medicine_frame.grid(row=medicine_row, column=1, padx=0, pady=10, sticky="nsew")
|
medicine_frame.grid(row=medicine_row, column=1, padx=0, pady=10, sticky="nsew")
|
||||||
medicine_frame.grid_columnconfigure(0, weight=1)
|
medicine_frame.grid_columnconfigure(0, weight=1)
|
||||||
|
|
||||||
@@ -178,11 +193,19 @@ class UIManager:
|
|||||||
text = f"{medicine.display_name} {medicine.dosage_info}"
|
text = f"{medicine.display_name} {medicine.dosage_info}"
|
||||||
medicine_vars[medicine_key] = (var, text)
|
medicine_vars[medicine_key] = (var, text)
|
||||||
|
|
||||||
for idx, (_med_name, (var, text)) in enumerate(medicine_vars.items()):
|
for idx, (med_key, (var, text)) in enumerate(medicine_vars.items()):
|
||||||
# Just checkbox for medicine taken
|
# Just checkbox for medicine taken
|
||||||
ttk.Checkbutton(medicine_frame, text=text, variable=var).grid(
|
checkbox = ttk.Checkbutton(
|
||||||
row=idx, column=0, sticky="w", padx=5, pady=2
|
medicine_frame, text=text, variable=var, style="Modern.TCheckbutton"
|
||||||
)
|
)
|
||||||
|
checkbox.grid(row=idx, column=0, sticky="w", padx=5, pady=2)
|
||||||
|
|
||||||
|
# Add tooltip for medicine checkbox
|
||||||
|
medicine = self.medicine_manager.get_medicine(med_key)
|
||||||
|
if medicine:
|
||||||
|
self.tooltip_manager.add_medicine_tooltip(
|
||||||
|
checkbox, medicine.display_name
|
||||||
|
)
|
||||||
|
|
||||||
# Note and Date fields - adjust row numbers
|
# Note and Date fields - adjust row numbers
|
||||||
note_row = medicine_row + 1
|
note_row = medicine_row + 1
|
||||||
@@ -194,16 +217,19 @@ class UIManager:
|
|||||||
ttk.Label(input_frame, text="Note:").grid(
|
ttk.Label(input_frame, text="Note:").grid(
|
||||||
row=note_row, column=0, sticky="w", padx=5, pady=2
|
row=note_row, column=0, sticky="w", padx=5, pady=2
|
||||||
)
|
)
|
||||||
ttk.Entry(input_frame, textvariable=note_var).grid(
|
ttk.Entry(input_frame, textvariable=note_var, style="Modern.TEntry").grid(
|
||||||
row=note_row, column=1, sticky="ew", padx=5, pady=2
|
row=note_row, column=1, sticky="ew", padx=5, pady=2
|
||||||
)
|
)
|
||||||
|
|
||||||
ttk.Label(input_frame, text="Date (mm/dd/yyyy):").grid(
|
ttk.Label(input_frame, text="Date (mm/dd/yyyy):").grid(
|
||||||
row=date_row, column=0, sticky="w", padx=5, pady=2
|
row=date_row, column=0, sticky="w", padx=5, pady=2
|
||||||
)
|
)
|
||||||
ttk.Entry(input_frame, textvariable=date_var, justify="center").grid(
|
ttk.Entry(
|
||||||
row=date_row, column=1, sticky="ew", padx=5, pady=2
|
input_frame,
|
||||||
)
|
textvariable=date_var,
|
||||||
|
justify="center",
|
||||||
|
style="Modern.TEntry",
|
||||||
|
).grid(row=date_row, column=1, sticky="ew", padx=5, pady=2)
|
||||||
|
|
||||||
# Set default date to today
|
# Set default date to today
|
||||||
date_var.set(datetime.now().strftime("%m/%d/%Y"))
|
date_var.set(datetime.now().strftime("%m/%d/%Y"))
|
||||||
@@ -225,7 +251,7 @@ class UIManager:
|
|||||||
def create_table_frame(self, parent_frame: ttk.Frame) -> dict[str, Any]:
|
def create_table_frame(self, parent_frame: ttk.Frame) -> dict[str, Any]:
|
||||||
"""Create and configure the table frame with a treeview."""
|
"""Create and configure the table frame with a treeview."""
|
||||||
table_frame: ttk.LabelFrame = ttk.LabelFrame(
|
table_frame: ttk.LabelFrame = ttk.LabelFrame(
|
||||||
parent_frame, text="Log (Double-click to edit)"
|
parent_frame, text="Log (Double-click to edit)", style="Card.TLabelframe"
|
||||||
)
|
)
|
||||||
table_frame.grid(row=1, column=1, padx=10, pady=10, sticky="nsew")
|
table_frame.grid(row=1, column=1, padx=10, pady=10, sticky="nsew")
|
||||||
|
|
||||||
@@ -258,7 +284,34 @@ class UIManager:
|
|||||||
col_labels.append("Note")
|
col_labels.append("Note")
|
||||||
col_settings.append(("Note", 300, "w"))
|
col_settings.append(("Note", 300, "w"))
|
||||||
|
|
||||||
tree: ttk.Treeview = ttk.Treeview(table_frame, columns=columns, show="headings")
|
tree: ttk.Treeview = ttk.Treeview(
|
||||||
|
table_frame, columns=columns, show="headings", style="Modern.Treeview"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Configure treeview selection behavior
|
||||||
|
tree.configure(selectmode="browse") # Single selection mode
|
||||||
|
|
||||||
|
# Configure row tags for alternating colors
|
||||||
|
theme_colors = self.theme_manager.get_theme_colors()
|
||||||
|
tree.tag_configure("evenrow", background=theme_colors["bg"])
|
||||||
|
tree.tag_configure("oddrow", background=theme_colors["alt_bg"])
|
||||||
|
|
||||||
|
# Configure selection highlighting
|
||||||
|
tree.tag_configure(
|
||||||
|
"selected",
|
||||||
|
background=theme_colors["select_bg"],
|
||||||
|
foreground=theme_colors["select_fg"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Bind selection events to ensure proper highlighting
|
||||||
|
def on_selection_change(event):
|
||||||
|
"""Handle treeview selection changes to ensure proper highlighting."""
|
||||||
|
selection = tree.selection()
|
||||||
|
if selection:
|
||||||
|
# Force focus to ensure selection is visible
|
||||||
|
tree.focus(selection[0])
|
||||||
|
|
||||||
|
tree.bind("<<TreeviewSelect>>", on_selection_change)
|
||||||
|
|
||||||
for col, label in zip(columns, col_labels, strict=False):
|
for col, label in zip(columns, col_labels, strict=False):
|
||||||
tree.heading(col, text=label)
|
tree.heading(col, text=label)
|
||||||
@@ -277,7 +330,9 @@ class UIManager:
|
|||||||
|
|
||||||
def create_graph_frame(self, parent_frame: ttk.Frame) -> ttk.LabelFrame:
|
def create_graph_frame(self, parent_frame: ttk.Frame) -> ttk.LabelFrame:
|
||||||
"""Create and configure the graph frame."""
|
"""Create and configure the graph frame."""
|
||||||
graph_frame: ttk.LabelFrame = ttk.LabelFrame(parent_frame, text="Evolution")
|
graph_frame: ttk.LabelFrame = ttk.LabelFrame(
|
||||||
|
parent_frame, text="Evolution", style="Card.TLabelframe"
|
||||||
|
)
|
||||||
graph_frame.grid(row=0, column=0, columnspan=2, padx=10, pady=10, sticky="nsew")
|
graph_frame.grid(row=0, column=0, columnspan=2, padx=10, pady=10, sticky="nsew")
|
||||||
return graph_frame
|
return graph_frame
|
||||||
|
|
||||||
@@ -289,23 +344,40 @@ class UIManager:
|
|||||||
button_frame.grid(row=7, column=0, columnspan=2, pady=10)
|
button_frame.grid(row=7, column=0, columnspan=2, pady=10)
|
||||||
|
|
||||||
for btn_config in buttons_config:
|
for btn_config in buttons_config:
|
||||||
ttk.Button(
|
button = ttk.Button(
|
||||||
button_frame,
|
button_frame,
|
||||||
text=btn_config["text"],
|
text=btn_config["text"],
|
||||||
command=btn_config["command"],
|
command=btn_config["command"],
|
||||||
).pack(
|
style="Action.TButton",
|
||||||
|
)
|
||||||
|
button.pack(
|
||||||
side="left",
|
side="left",
|
||||||
padx=5,
|
padx=5,
|
||||||
fill=btn_config.get("fill", None),
|
fill=btn_config.get("fill", None),
|
||||||
expand=btn_config.get("expand", False),
|
expand=btn_config.get("expand", False),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Add tooltips based on button text
|
||||||
|
button_text = btn_config["text"].lower()
|
||||||
|
if "add" in button_text or "save" in button_text:
|
||||||
|
self.tooltip_manager.add_button_tooltip(button, "save")
|
||||||
|
elif "quit" in button_text or "exit" in button_text:
|
||||||
|
self.tooltip_manager.add_button_tooltip(button, "quit")
|
||||||
|
|
||||||
return button_frame
|
return button_frame
|
||||||
|
|
||||||
def create_status_bar(self, parent_frame: tk.Widget) -> tk.Frame:
|
def create_status_bar(self, parent_frame: tk.Widget) -> tk.Frame:
|
||||||
"""Create and configure the status bar at the bottom of the application."""
|
"""Create and configure the status bar at the bottom of the application."""
|
||||||
|
# Get theme colors for consistent styling
|
||||||
|
theme_colors = self.theme_manager.get_theme_colors()
|
||||||
|
|
||||||
# Create the status bar frame
|
# Create the status bar frame
|
||||||
self.status_bar = tk.Frame(parent_frame, relief=tk.SUNKEN, bd=1)
|
self.status_bar = tk.Frame(
|
||||||
|
parent_frame,
|
||||||
|
relief=tk.SUNKEN,
|
||||||
|
bd=1,
|
||||||
|
bg=theme_colors["bg"],
|
||||||
|
)
|
||||||
self.status_bar.grid(row=2, column=0, columnspan=2, sticky="ew", padx=5, pady=2)
|
self.status_bar.grid(row=2, column=0, columnspan=2, sticky="ew", padx=5, pady=2)
|
||||||
|
|
||||||
# Configure the parent to make the status bar stretch
|
# Configure the parent to make the status bar stretch
|
||||||
@@ -319,6 +391,8 @@ class UIManager:
|
|||||||
font=("TkDefaultFont", 9),
|
font=("TkDefaultFont", 9),
|
||||||
padx=10,
|
padx=10,
|
||||||
pady=2,
|
pady=2,
|
||||||
|
bg=theme_colors["bg"],
|
||||||
|
fg=theme_colors["fg"],
|
||||||
)
|
)
|
||||||
self.status_label.pack(side=tk.LEFT, fill=tk.X, expand=True)
|
self.status_label.pack(side=tk.LEFT, fill=tk.X, expand=True)
|
||||||
|
|
||||||
@@ -330,6 +404,8 @@ class UIManager:
|
|||||||
font=("TkDefaultFont", 9),
|
font=("TkDefaultFont", 9),
|
||||||
padx=10,
|
padx=10,
|
||||||
pady=2,
|
pady=2,
|
||||||
|
bg=theme_colors["bg"],
|
||||||
|
fg=theme_colors["fg"],
|
||||||
)
|
)
|
||||||
self.file_info_label.pack(side=tk.RIGHT)
|
self.file_info_label.pack(side=tk.RIGHT)
|
||||||
|
|
||||||
@@ -793,9 +869,15 @@ class UIManager:
|
|||||||
variable=vars_dict[key],
|
variable=vars_dict[key],
|
||||||
orient=tk.HORIZONTAL,
|
orient=tk.HORIZONTAL,
|
||||||
length=250,
|
length=250,
|
||||||
|
style="Modern.Horizontal.TScale",
|
||||||
)
|
)
|
||||||
scale.grid(row=0, column=1, sticky="ew")
|
scale.grid(row=0, column=1, sticky="ew")
|
||||||
|
|
||||||
|
# Add tooltip for the scale
|
||||||
|
pathology = self.pathology_manager.get_pathology(key)
|
||||||
|
if pathology:
|
||||||
|
self.tooltip_manager.add_scale_tooltip(scale, pathology.display_name)
|
||||||
|
|
||||||
# Scale labels
|
# Scale labels
|
||||||
labels_frame = ttk.Frame(scale_container)
|
labels_frame = ttk.Frame(scale_container)
|
||||||
labels_frame.grid(row=1, column=0, sticky="ew", pady=(5, 0))
|
labels_frame.grid(row=1, column=0, sticky="ew", pady=(5, 0))
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
version = 1
|
version = 1
|
||||||
revision = 2
|
revision = 3
|
||||||
requires-python = ">=3.13"
|
requires-python = ">=3.13"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -767,6 +767,7 @@ dependencies = [
|
|||||||
{ name = "pandas" },
|
{ name = "pandas" },
|
||||||
{ name = "reportlab" },
|
{ name = "reportlab" },
|
||||||
{ name = "tk" },
|
{ name = "tk" },
|
||||||
|
{ name = "ttkthemes" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dev-dependencies]
|
[package.dev-dependencies]
|
||||||
@@ -789,6 +790,7 @@ requires-dist = [
|
|||||||
{ name = "pandas", specifier = ">=2.3.1" },
|
{ name = "pandas", specifier = ">=2.3.1" },
|
||||||
{ name = "reportlab", specifier = ">=4.4.3" },
|
{ name = "reportlab", specifier = ">=4.4.3" },
|
||||||
{ name = "tk", specifier = ">=0.1.0" },
|
{ name = "tk", specifier = ">=0.1.0" },
|
||||||
|
{ name = "ttkthemes", specifier = ">=3.2.2" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata.requires-dev]
|
[package.metadata.requires-dev]
|
||||||
@@ -811,6 +813,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/1e/0b/029cbdb868bb555fed99bf6540fff072d500b3f895873709f25084e85e33/tk-0.1.0-py3-none-any.whl", hash = "sha256:703a69ff0d5ba2bd2f7440582ad10160e4a6561595d33457dc6caa79b9bf4930", size = 3879, upload-time = "2019-07-08T06:51:55.175Z" },
|
{ url = "https://files.pythonhosted.org/packages/1e/0b/029cbdb868bb555fed99bf6540fff072d500b3f895873709f25084e85e33/tk-0.1.0-py3-none-any.whl", hash = "sha256:703a69ff0d5ba2bd2f7440582ad10160e4a6561595d33457dc6caa79b9bf4930", size = 3879, upload-time = "2019-07-08T06:51:55.175Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ttkthemes"
|
||||||
|
version = "3.2.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "pillow" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/fa/45/ab8ada55281af99a03bc0f8be53a502eb37ee34b94819a9ced89e8b0c12f/ttkthemes-3.2.2.tar.gz", hash = "sha256:01daed001f2ff0e4f32832a0d9ea48176c0c505203b030756bdde3bd1bcb21d2", size = 891159, upload-time = "2021-02-15T12:57:14.719Z" }
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tzdata"
|
name = "tzdata"
|
||||||
version = "2025.2"
|
version = "2025.2"
|
||||||
|
|||||||
Reference in New Issue
Block a user