Initial commit: Kivy database interface application with search, add/update, delete functionality and Windows build support

This commit is contained in:
ske087
2025-10-20 13:21:29 +03:00
commit 8ae60a77e4
17 changed files with 11624 additions and 0 deletions

41
.gitignore vendored Normal file
View File

@@ -0,0 +1,41 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
venv/
env/
ENV/
*.egg-info/
dist/
build/
*.egg
# PyInstaller
*.spec.bak
*.manifest
*.exe
*.dll
*.dylib
# Database
*.db
*.sqlite
*.sqlite3
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
*.log
# Temporary files
*.tmp
*.bak

163
BUILD_WINDOWS_GUIDE.md Normal file
View File

@@ -0,0 +1,163 @@
# Building Windows Executable with PyInstaller
## Prerequisites
You need to build the executable **on a Windows machine** (PyInstaller creates platform-specific executables).
## Step 1: Install PyInstaller
On Windows:
```cmd
# Activate virtual environment
venv\Scripts\activate
# Install PyInstaller
pip install pyinstaller
```
## Step 2: Build the Executable
### Option A: Using the build script (Recommended)
```cmd
python build_windows.py
```
### Option B: Manual PyInstaller command
```cmd
pyinstaller --name=DatabaseApp --onefile --windowed --hidden-import=mysql.connector --collect-all=kivy main.py
```
## Step 3: Find Your Executable
After building, the executable will be in:
```
dist/DatabaseApp.exe
```
## Step 4: Distribution
Copy the following to your target Windows machine:
- `DatabaseApp.exe` (from the `dist` folder)
That's it! The executable is fully standalone and doesn't require Python installation.
## Advanced Build Options
### Creating a Spec File for Customization
```cmd
pyi-makespec --onefile --windowed main.py
```
Then edit `main.spec` and build:
```cmd
pyinstaller main.spec
```
### Adding an Application Icon
1. Get an `.ico` file (you can convert PNG to ICO online)
2. Save it as `app_icon.ico` in the project folder
3. Build with icon:
```cmd
pyinstaller --name=DatabaseApp --onefile --windowed --icon=app_icon.ico --collect-all=kivy main.py
```
## Troubleshooting
### "Failed to execute script"
- Make sure all dependencies are included
- Try building with `--onedir` instead of `--onefile` for debugging:
```cmd
pyinstaller --name=DatabaseApp --onedir --windowed --collect-all=kivy main.py
```
### Missing modules
Add hidden imports:
```cmd
pyinstaller --name=DatabaseApp --onefile --windowed --hidden-import=module_name --collect-all=kivy main.py
```
### Large executable size
This is normal for Kivy apps. Typical size: 50-100 MB
To reduce size:
- Use `--onedir` instead of `--onefile`
- Use UPX compression: `pip install pyinstaller[encryption]`
## Build Spec File (Advanced)
Create `DatabaseApp.spec` for more control:
```python
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['main.py'],
pathex=[],
binaries=[],
datas=[('database_manager.py', '.')],
hiddenimports=['mysql.connector', 'kivy'],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
# Collect all Kivy dependencies
a.datas += Tree('venv/Lib/site-packages/kivy', prefix='kivy')
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='DatabaseApp',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False, # No console window
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon='app_icon.ico' # Optional
)
```
Then build:
```cmd
pyinstaller DatabaseApp.spec
```
## Testing the Executable
1. Copy `DatabaseApp.exe` to a test Windows machine without Python
2. Make sure MariaDB/MySQL is accessible (local or remote)
3. Run the executable
4. Use the Settings button to configure the database server IP if needed
## Notes
- The executable size will be ~50-100 MB due to Kivy framework
- Build time: 2-5 minutes depending on your system
- The executable is completely standalone - no Python needed
- Database server must still be accessible (local or network)
- Settings are not persistent between runs (saved in memory only)
## Creating an Installer (Optional)
Use Inno Setup to create a Windows installer:
1. Download Inno Setup: https://jrsoftware.org/isinfo.php
2. Create an installer script
3. Package `DatabaseApp.exe` with installer
This makes distribution even more professional.

38
DatabaseApp.spec Normal file
View File

@@ -0,0 +1,38 @@
# -*- mode: python ; coding: utf-8 -*-
a = Analysis(
['main.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=['mysql.connector', 'kivy.core.window.window_sdl2', 'win32timezone'],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=['_tkinter', 'matplotlib', 'numpy'],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='DatabaseApp',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)

100
IMPORTANT_BUILD_NOTE.md Normal file
View File

@@ -0,0 +1,100 @@
# Important Note About Building Windows Executables
## ⚠️ Cross-Platform Building Limitation
**PyInstaller creates platform-specific executables.** This means:
- ❌ You **CANNOT** build a Windows `.exe` from Linux/Raspberry Pi
- ✅ You **MUST** build on Windows to create a Windows executable
- ✅ You **CAN** build on Linux to create a Linux executable
## Solution: Build on Windows
### Option 1: Use a Windows Machine
1. Copy all project files to a Windows computer
2. Install Python 3.8+ on Windows
3. Run the build:
```cmd
python -m venv venv
venv\Scripts\activate
pip install -r requirements.txt
python build_windows.py
```
### Option 2: Use a Windows Virtual Machine
1. Install VirtualBox or VMware
2. Create a Windows VM
3. Follow the same steps as Option 1
### Option 3: Use Wine (Advanced, Not Recommended)
Wine can sometimes work but is unreliable for Kivy applications.
## What You CAN Do on Raspberry Pi/Linux
### 1. Build Linux Executable
```bash
source venv/bin/activate
pip install pyinstaller
pyinstaller --onefile --windowed main.py
```
This creates: `dist/main` (Linux executable)
### 2. Run Directly with Python (Recommended for Development)
```bash
source venv/bin/activate
python main.py
```
### 3. Transfer Files to Windows for Building
Use:
- USB drive
- Network share
- Git repository
- Cloud storage (Dropbox, Google Drive, etc.)
## Recommended Workflow
**For Distribution:**
1. Develop on Raspberry Pi/Linux (as you're doing now)
2. When ready to distribute, transfer project to Windows
3. Build Windows executable on Windows machine
4. Distribute the `.exe` file to end users
**For Testing:**
- Keep developing and testing with `python main.py` on your Raspberry Pi
- The app works perfectly without building an executable
## Alternative: Distribute as Python App
Instead of an executable, you can distribute:
1. **Python files + instructions**
```
- Give users: main.py, database_manager.py, requirements.txt
- Users install Python and run: pip install -r requirements.txt && python main.py
```
2. **Docker container** (works on any platform)
```bash
docker build -t database-app .
docker run database-app
```
## Files Ready for Windows Build
✅ All necessary files are ready:
- `build_windows.py` - Automated build script
- `build.bat` - Windows batch file
- `DatabaseApp.spec` - PyInstaller configuration
- `BUILD_WINDOWS_GUIDE.md` - Complete instructions
- `main.py` - Main application
- `database_manager.py` - Database logic
- `requirements.txt` - Dependencies
**Just transfer these files to Windows and run `build.bat`**
## Questions?
- The app works perfectly as-is with Python on any platform
- Building executables is only needed for distribution to users without Python
- For personal use or development, keep using `python main.py`

152
README.md Normal file
View File

@@ -0,0 +1,152 @@
# Database Manager Kivy App
A simple Kivy application for managing a MariaDB database with two columns (id and mass) for the `offsystemsCounting` table.
## Features
- **Search**: Look up records by ID
- **Add/Update**: Add new records or update existing ones
- **Delete**: Remove records from the database
- **View All**: Display all records in the database
- **Auto-create**: Table is created automatically if it doesn't exist
## Prerequisites
### MariaDB Server Setup
1. **Install MariaDB server** (if not already installed):
```bash
sudo apt update
sudo apt install mariadb-server
```
2. **Start MariaDB service**:
```bash
sudo systemctl start mariadb
sudo systemctl enable mariadb
```
3. **Secure MariaDB installation**:
```bash
sudo mysql_secure_installation
```
4. **Create the database and user**:
```bash
sudo mysql -u root -p
```
Then run these SQL commands:
```sql
CREATE DATABASE cantare_injectie;
CREATE USER 'omron'@'localhost' IDENTIFIED BY 'Initial01!';
GRANT ALL PRIVILEGES ON cantare_injectie.* TO 'omron'@'localhost';
FLUSH PRIVILEGES;
EXIT;
```
5. **Test the connection**:
```bash
mysql -u omron -p cantare_injectie
```
Enter password: `Initial01!`
## Installation
1. Install Python 3.7+ if not already installed
2. Install the required dependencies:
```bash
pip install -r requirements.txt
```
Or using virtual environment:
```bash
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```
## Usage
1. **Make sure MariaDB server is running**:
```bash
sudo systemctl status mariadb
```
2. **Run the application**:
```bash
python main.py
```
Or:
```bash
chmod +x run_app.sh
./run_app.sh
```
3. **To search for a record:**
- Enter an ID in the "ID" field (max 20 characters)
- Click "Search"
- If found, the mass will be displayed in the "Mass" field
4. **To add or update a record:**
- Enter both ID and mass value
- Click "Add/Update"
- If the ID exists, it will be updated; otherwise, a new record will be created
5. **To delete a record:**
- Enter the ID to delete
- Click "Delete"
- Confirm the deletion in the popup
6. **To refresh the display:**
- Click "Refresh All" to reload all data from the database
## Database Structure
The app connects to MariaDB database `cantare_injectie` with the following table structure:
```sql
CREATE TABLE offsystemsCounting (
id VARCHAR(20) PRIMARY KEY,
mass REAL NOT NULL
)
```
## Files
- `main.py`: Main Kivy application
- `database_manager.py`: MariaDB database operations class
- `requirements.txt`: Python dependencies
- `test_database.py`: Test script for database operations
- `run_app.sh`: Startup script
- `README.md`: This documentation
## Connection Settings
The application connects to MariaDB with these settings:
- **Host**: localhost
- **Database**: cantare_injectie
- **User**: omron
- **Password**: Initial01!
- **Table**: offsystemsCounting
## Troubleshooting
1. **Connection Error 1698**:
- Make sure MariaDB is running: `sudo systemctl start mariadb`
- Verify user exists and has correct password
- Check database exists: `SHOW DATABASES;`
2. **Access Denied**:
- Verify user permissions: `SHOW GRANTS FOR 'omron'@'localhost';`
- Reset password if needed: `ALTER USER 'omron'@'localhost' IDENTIFIED BY 'Initial01!';`
3. **Database/Table doesn't exist**:
- The application will create the table automatically
- Make sure the database `cantare_injectie` exists
## Notes
- IDs must be unique and max 20 characters
- Mass values must be valid decimal numbers
- The app includes comprehensive error handling and user feedback
- All database operations use parameterized queries for security

82
WINDOWS_README.md Normal file
View File

@@ -0,0 +1,82 @@
# Windows Setup Instructions
## Prerequisites
1. Python 3.8 or higher
2. MariaDB or MySQL server (local or remote)
## Installation Steps
### 1. Install Python
- Download from: https://www.python.org/downloads/
- During installation, check "Add Python to PATH"
### 2. Setup the Application
Open Command Prompt in the application folder and run:
```cmd
# Create virtual environment
python -m venv venv
# Activate virtual environment
venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
```
### 3. Database Setup (if using local database)
- Install MariaDB or MySQL
- Run the setup script:
```cmd
mysql -u root -p < setup_user.sql
```
### 4. Run the Application
#### Option A: Using the batch file
Simply double-click `run_app.bat`
#### Option B: Using command line
```cmd
venv\Scripts\activate
python main.py
```
## Configuration
- Use the **Settings** button in the app to configure the database server IP address
- Default connection:
- Host: localhost
- Database: cantare_injectie
- User: omron
- Password: Initial01!
## Troubleshooting
### Missing modules error
```cmd
venv\Scripts\activate
pip install -r requirements.txt
```
### Database connection error
- Check if MariaDB/MySQL service is running
- Verify database credentials
- Use Settings button to update server IP address
### Kivy installation issues
```cmd
pip install --upgrade pip
pip install kivy --pre --extra-index-url https://kivy.org/downloads/simple/
```
## Features
- **Fullscreen mode**: App starts in fullscreen by default
- **Search**: Enter ID and press Enter
- **Add/Update**: Click Add/Update button to enable editing
- **Delete**: Use Delete button in update section
- **Reset**: Clear all fields with Reset Values button
- **Settings**: Configure database server IP address
## Keyboard Shortcuts
- **F11** or **Esc**: Exit fullscreen
- **Enter**: Search for ID (when in ID field)

42
build.bat Normal file
View File

@@ -0,0 +1,42 @@
@echo off
REM Batch file to build Windows executable
echo ========================================
echo Building Windows Executable
echo ========================================
echo.
REM Check if virtual environment exists
if not exist "venv\Scripts\activate.bat" (
echo Creating virtual environment...
python -m venv venv
call venv\Scripts\activate.bat
echo Installing requirements...
pip install -r requirements.txt
) else (
call venv\Scripts\activate.bat
)
REM Install PyInstaller if not present
python -c "import pyinstaller" 2>nul
if errorlevel 1 (
echo Installing PyInstaller...
pip install pyinstaller
)
echo.
echo Building executable with PyInstaller...
echo This may take a few minutes...
echo.
REM Build the executable
python build_windows.py
echo.
echo ========================================
echo Build process completed!
echo.
echo Executable location: dist\DatabaseApp.exe
echo ========================================
echo.
pause

64
build_windows.py Normal file
View File

@@ -0,0 +1,64 @@
"""
Script to build Windows executable using PyInstaller
Run this on a Windows machine or use Wine on Linux
"""
import os
import sys
import subprocess
def build_executable():
"""Build the Windows executable"""
print("Building Windows executable with PyInstaller...")
print("Note: For best results, build on a Windows machine")
print()
# Check if icon file exists
icon_param = []
if os.path.exists('app_icon.ico'):
icon_param = ['--icon=app_icon.ico']
print("Using icon file: app_icon.ico")
else:
print("No icon file found (optional)")
# PyInstaller command - simplified to avoid module collection issues
cmd = [
'pyinstaller',
'--name=DatabaseApp',
'--onefile',
'--windowed',
'--hidden-import=mysql.connector',
'--hidden-import=kivy.core.window.window_sdl2',
'--hidden-import=win32timezone',
'--exclude-module=_tkinter',
'--exclude-module=matplotlib',
'--exclude-module=numpy',
] + icon_param + ['main.py']
try:
print("Running PyInstaller...")
print("Command:", ' '.join(cmd))
print()
subprocess.run(cmd, check=True)
print("\n" + "="*50)
print("Build completed successfully!")
print("Executable location: dist/DatabaseApp.exe")
print("="*50)
print("\nNote: If the executable doesn't work on Windows:")
print("1. Build it directly on a Windows machine")
print("2. Use the DatabaseApp.spec file: pyinstaller DatabaseApp.spec")
except subprocess.CalledProcessError as e:
print(f"\nError building executable: {e}")
print("\nTroubleshooting:")
print("1. Make sure you're building on Windows for best compatibility")
print("2. Try using the spec file: pyinstaller DatabaseApp.spec")
print("3. Check BUILD_WINDOWS_GUIDE.md for more information")
sys.exit(1)
except FileNotFoundError:
print("PyInstaller not found. Please install it first:")
print("pip install pyinstaller")
sys.exit(1)
if __name__ == "__main__":
build_executable()

149
database_manager.py Normal file
View File

@@ -0,0 +1,149 @@
import mysql.connector
from mysql.connector import Error
from typing import List, Tuple, Optional
class DatabaseManager:
"""
Database manager class for handling MariaDB operations.
Connects to MariaDB server with table offsystemsCounting containing id (VARCHAR(20)) and mass (REAL).
"""
def __init__(self):
self.host = "localhost"
self.database = "cantare_injectie"
self.user = "omron"
self.password = "Initial01!"
self.connection = None
self.init_database()
def get_connection(self):
"""Get a database connection."""
try:
if self.connection is None or not self.connection.is_connected():
self.connection = mysql.connector.connect(
host=self.host,
database=self.database,
user=self.user,
password=self.password
)
return self.connection
except Error as e:
print(f"Database connection error: {e}")
return None
def init_database(self):
"""Initialize the database connection and create the table if it doesn't exist."""
try:
conn = self.get_connection()
if conn and conn.is_connected():
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS offsystemsCounting (
id VARCHAR(20) PRIMARY KEY,
mass REAL NOT NULL
)
''')
conn.commit()
print(f"Connected to MariaDB database: {self.database}")
print("Table 'offsystemsCounting' ready")
except Error as e:
print(f"Database initialization error: {e}")
def read_all_data(self) -> List[Tuple[str, float]]:
"""Read all data from the database."""
try:
conn = self.get_connection()
if conn and conn.is_connected():
cursor = conn.cursor()
cursor.execute("SELECT id, mass FROM offsystemsCounting ORDER BY id")
return cursor.fetchall()
except Error as e:
print(f"Error reading data: {e}")
return []
def search_by_id(self, record_id: str) -> Optional[Tuple[str, float]]:
"""Search for a record by ID."""
try:
conn = self.get_connection()
if conn and conn.is_connected():
cursor = conn.cursor()
cursor.execute("SELECT id, mass FROM offsystemsCounting WHERE id = %s", (record_id,))
return cursor.fetchone()
except Error as e:
print(f"Error searching data: {e}")
return None
def add_or_update_record(self, record_id: str, mass: float) -> bool:
"""Add a new record or update existing one if ID already exists."""
try:
conn = self.get_connection()
if conn and conn.is_connected():
cursor = conn.cursor()
# Check if record exists
existing = self.search_by_id(record_id)
if existing:
# Update existing record
cursor.execute(
"UPDATE offsystemsCounting SET mass = %s WHERE id = %s",
(mass, record_id)
)
print(f"Updated record: {record_id} = {mass}")
else:
# Insert new record
cursor.execute(
"INSERT INTO offsystemsCounting (id, mass) VALUES (%s, %s)",
(record_id, mass)
)
print(f"Added new record: {record_id} = {mass}")
conn.commit()
return True
except Error as e:
print(f"Error adding/updating record: {e}")
return False
def delete_record(self, record_id: str) -> bool:
"""Delete a record by ID."""
try:
conn = self.get_connection()
if conn and conn.is_connected():
cursor = conn.cursor()
cursor.execute("DELETE FROM offsystemsCounting WHERE id = %s", (record_id,))
if cursor.rowcount > 0:
conn.commit()
print(f"Deleted record: {record_id}")
return True
else:
print(f"No record found with ID: {record_id}")
return False
except Error as e:
print(f"Error deleting record: {e}")
return False
def get_record_count(self) -> int:
"""Get the total number of records in the database."""
try:
conn = self.get_connection()
if conn and conn.is_connected():
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) FROM offsystemsCounting")
return cursor.fetchone()[0]
except Error as e:
print(f"Error getting record count: {e}")
return 0
def close_connection(self):
"""Close the database connection."""
try:
if self.connection and self.connection.is_connected():
self.connection.close()
print("MariaDB connection closed")
except Error as e:
print(f"Error closing connection: {e}")
def __del__(self):
"""Destructor to ensure connection is closed."""
self.close_connection()

138
import_csv.py Executable file
View File

@@ -0,0 +1,138 @@
#!/usr/bin/env python3
"""
CSV Import Script for MariaDB Database
This script imports data from sept.csv into the offsystemsCounting table
"""
import csv
from database_manager import DatabaseManager
def import_csv_data(csv_file_path):
"""Import data from CSV file into the database."""
print("CSV Import Script for MariaDB Database")
print("=" * 40)
# Initialize database connection
db = DatabaseManager()
# Check if we can connect
conn = db.get_connection()
if not conn or not conn.is_connected():
print("❌ Cannot connect to MariaDB database.")
print("Please make sure:")
print("1. MariaDB server is running")
print("2. Database 'cantare_injectie' exists")
print("3. User 'omron' has proper permissions")
return False
print("✅ Successfully connected to MariaDB database")
# Read and import CSV data
try:
with open(csv_file_path, 'r', newline='', encoding='utf-8') as csvfile:
reader = csv.DictReader(csvfile)
# Check if the required columns exist
if 'id' not in reader.fieldnames or 'mass' not in reader.fieldnames:
print("❌ CSV file must contain 'id' and 'mass' columns")
return False
print(f"📁 Reading data from: {csv_file_path}")
print(f"📋 Columns found: {reader.fieldnames}")
# Import data
imported_count = 0
updated_count = 0
error_count = 0
for row_num, row in enumerate(reader, start=2): # Start at 2 because line 1 is header
try:
record_id = row['id'].strip()
mass_str = row['mass'].strip()
# Validate data
if not record_id:
print(f"⚠️ Row {row_num}: Empty ID, skipping")
error_count += 1
continue
if len(record_id) > 20:
print(f"⚠️ Row {row_num}: ID '{record_id}' too long (max 20 chars), skipping")
error_count += 1
continue
try:
mass = float(mass_str)
except ValueError:
print(f"⚠️ Row {row_num}: Invalid mass value '{mass_str}', skipping")
error_count += 1
continue
# Check if record exists
existing = db.search_by_id(record_id)
# Add or update record
success = db.add_or_update_record(record_id, mass)
if success:
if existing:
updated_count += 1
if updated_count <= 5: # Show first 5 updates
print(f"🔄 Updated: {record_id} = {mass}")
else:
imported_count += 1
if imported_count <= 5: # Show first 5 imports
print(f" Added: {record_id} = {mass}")
else:
error_count += 1
print(f"❌ Failed to import row {row_num}: {record_id}")
# Progress indicator
if (imported_count + updated_count + error_count) % 100 == 0:
print(f"📊 Progress: {imported_count + updated_count + error_count} records processed...")
except Exception as e:
error_count += 1
print(f"❌ Error processing row {row_num}: {e}")
# Final summary
print("\n" + "=" * 40)
print("📊 IMPORT SUMMARY")
print("=" * 40)
print(f"✅ New records added: {imported_count}")
print(f"🔄 Records updated: {updated_count}")
print(f"❌ Errors: {error_count}")
print(f"📈 Total processed: {imported_count + updated_count + error_count}")
# Verify final count in database
total_records = db.get_record_count()
print(f"📋 Total records in database: {total_records}")
if imported_count > 0 or updated_count > 0:
print("\n✅ CSV import completed successfully!")
return True
else:
print("\n⚠️ No records were imported.")
return False
except FileNotFoundError:
print(f"❌ CSV file not found: {csv_file_path}")
return False
except Exception as e:
print(f"❌ Error reading CSV file: {e}")
return False
finally:
# Close database connection
db.close_connection()
if __name__ == "__main__":
csv_file = "sept.csv"
success = import_csv_data(csv_file)
if success:
print("\n🚀 You can now run the Kivy app to view the imported data:")
print(" python main.py")
print(" or")
print(" ./run_app.sh")
else:
print("\n❌ Import failed. Please check the errors above.")

361
main.py Normal file
View File

@@ -0,0 +1,361 @@
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
from kivy.uix.popup import Popup
from kivy.clock import Clock
from kivy.core.window import Window
from database_manager import DatabaseManager
class DatabaseApp(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.db_manager = DatabaseManager()
def build(self):
# Set window to fullscreen
Window.fullscreen = 'auto'
# Main layout with better spacing for fullscreen
main_layout = BoxLayout(orientation='vertical', padding=40, spacing=20)
# Top spacer for vertical centering
main_layout.add_widget(Label(size_hint_y=0.15))
# Content container - centered
content_layout = BoxLayout(orientation='vertical', spacing=30, size_hint_y=None)
content_layout.bind(minimum_height=content_layout.setter('height'))
# Title
title = Label(text='Database Search & Update', font_size=28, bold=True, size_hint_y=None, height=50)
content_layout.add_widget(title)
# Search section
search_layout = GridLayout(cols=2, size_hint_y=None, height=100, spacing=15, row_force_default=True, row_default_height=45)
search_layout.add_widget(Label(text='ID:', size_hint_x=0.25, font_size=20, bold=True))
self.id_input = TextInput(
multiline=False,
size_hint_x=0.75,
hint_text='Enter ID and press Enter to search (max 20 chars)',
readonly=False,
font_size=20,
padding=[10, 10]
)
self.id_input.bind(on_text_validate=self.search_record)
search_layout.add_widget(self.id_input)
search_layout.add_widget(Label(text='Mass:', size_hint_x=0.25, font_size=20, bold=True))
self.mass_input = TextInput(
multiline=False,
size_hint_x=0.75,
hint_text='Mass value (read-only)',
readonly=True,
font_size=20,
padding=[10, 10]
)
search_layout.add_widget(self.mass_input)
content_layout.add_widget(search_layout)
# Button section - larger buttons (3 columns now)
button_layout = GridLayout(cols=3, size_hint_y=None, height=70, spacing=15)
add_update_btn = Button(text='Add/Update', font_size=22, bold=True)
add_update_btn.bind(on_press=self.show_update_frame)
button_layout.add_widget(add_update_btn)
reset_btn = Button(text='Reset Values', font_size=22, bold=True)
reset_btn.bind(on_press=self.reset_values)
button_layout.add_widget(reset_btn)
settings_btn = Button(text='Settings', font_size=22, bold=True)
settings_btn.bind(on_press=self.show_settings)
button_layout.add_widget(settings_btn)
content_layout.add_widget(button_layout)
# Extra spacing between buttons and update frame
content_layout.add_widget(Label(size_hint_y=None, height=40))
# Update frame (initially disabled)
self.update_frame = BoxLayout(orientation='vertical', padding=15, spacing=15, size_hint_y=None, height=200)
self.update_frame_label = Label(text='Update Values', size_hint_y=None, height=40, font_size=22, bold=True)
self.update_frame.add_widget(self.update_frame_label)
update_inputs = GridLayout(cols=2, spacing=15, size_hint_y=None, height=100, row_force_default=True, row_default_height=45)
update_inputs.add_widget(Label(text='ID:', size_hint_x=0.25, font_size=20, bold=True))
self.update_id_input = TextInput(multiline=False, size_hint_x=0.75, readonly=True, font_size=20, padding=[10, 10])
update_inputs.add_widget(self.update_id_input)
update_inputs.add_widget(Label(text='Mass:', size_hint_x=0.25, font_size=20, bold=True))
self.update_mass_input = TextInput(multiline=False, size_hint_x=0.75, readonly=True, font_size=20, padding=[10, 10])
update_inputs.add_widget(self.update_mass_input)
self.update_frame.add_widget(update_inputs)
# Add update and delete buttons in same row
update_buttons = GridLayout(cols=2, size_hint_y=None, height=60, spacing=15)
self.update_confirm_btn = Button(text='Confirm Add/Update', disabled=True, font_size=22, bold=True)
self.update_confirm_btn.bind(on_press=self.add_update_record)
update_buttons.add_widget(self.update_confirm_btn)
self.delete_btn = Button(text='Delete', disabled=True, font_size=22, bold=True)
self.delete_btn.bind(on_press=self.delete_record)
update_buttons.add_widget(self.delete_btn)
self.update_frame.add_widget(update_buttons)
content_layout.add_widget(self.update_frame)
# Initially disable update frame
self.set_update_frame_enabled(False)
# Status label - larger and more prominent
self.status_label = Label(
text='Ready',
size_hint_y=None,
height=50,
color=(0, 0.8, 0, 1),
font_size=22,
bold=True
)
content_layout.add_widget(self.status_label)
main_layout.add_widget(content_layout)
# Bottom spacer for vertical centering
main_layout.add_widget(Label(size_hint_y=0.15))
# Removed database contents frame
# Load initial data
Clock.schedule_once(self.refresh_data, 0.1)
return main_layout
def set_update_frame_enabled(self, enabled):
self.update_id_input.readonly = not enabled
self.update_mass_input.readonly = not enabled
self.update_confirm_btn.disabled = not enabled
self.delete_btn.disabled = not enabled
def show_update_frame(self, instance):
# If no value in search, copy from search fields
record_id = self.id_input.text.strip()
mass_text = self.mass_input.text.strip()
self.set_update_frame_enabled(True)
# If mass field is empty, just clear update frame
if not record_id:
self.update_id_input.text = ''
self.update_mass_input.text = ''
return
self.update_id_input.text = record_id
self.update_mass_input.text = mass_text
def search_record(self, instance):
record_id = self.id_input.text.strip()
if not record_id:
self.show_status("Please enter an ID to search", error=True)
return
if len(record_id) > 20:
self.show_status("ID must be 20 characters or less", error=True)
return
# Show searching status
self.show_status("Searching...", error=False)
try:
record = self.db_manager.search_by_id(record_id)
if record:
self.mass_input.text = str(record[1]) # Set the mass field
self.show_status(f"Found: {record[0]} = {record[1]}")
self.highlight_record(record_id)
else:
self.show_status(f"ID '{record_id}' not found in database", error=True)
self.mass_input.text = ""
except Exception as e:
self.show_status(f"Search error: {str(e)}", error=True)
def add_update_record(self, instance):
"""Add or update a record from the update frame."""
record_id = self.update_id_input.text.strip()
mass_text = self.update_mass_input.text.strip()
if not record_id or not mass_text:
self.show_status("Please enter both ID and mass in update frame", error=True)
return
if len(record_id) > 20:
self.show_status("ID must be 20 characters or less", error=True)
return
try:
mass = float(mass_text)
except ValueError:
self.show_status("Mass must be a valid number", error=True)
return
success = self.db_manager.add_or_update_record(record_id, mass)
if success:
existing = self.db_manager.search_by_id(record_id)
if existing:
self.show_status(f"Successfully added/updated: {record_id} = {mass}")
self.refresh_data(None)
# Clear update frame after successful operation
self.update_id_input.text = ""
self.update_mass_input.text = ""
self.set_update_frame_enabled(False)
else:
self.show_status("Operation completed but record not found", error=True)
else:
self.show_status("Failed to add/update record", error=True)
def delete_record(self, instance):
"""Delete a record using the update frame fields."""
record_id = self.update_id_input.text.strip()
if not record_id:
self.show_status("Please enter an ID in the update fields to delete", error=True)
return
# Confirm deletion
self.show_confirmation_popup(
f"Are you sure you want to delete ID '{record_id}'?",
lambda: self.confirm_delete(record_id)
)
def confirm_delete(self, record_id):
"""Confirm and execute deletion."""
success = self.db_manager.delete_record(record_id)
if success:
self.show_status(f"Successfully deleted: {record_id}")
self.refresh_data(None)
self.clear_fields()
# Clear update frame fields
self.update_id_input.text = ""
self.update_mass_input.text = ""
self.set_update_frame_enabled(False)
else:
self.show_status(f"Failed to delete ID '{record_id}' (not found)", error=True)
def clear_fields(self):
"""Clear the ID and mass fields."""
self.id_input.text = ""
self.mass_input.text = ""
def reset_values(self, instance):
"""Reset/clear the first ID and mass fields and set focus on ID field."""
self.id_input.text = ""
self.mass_input.text = ""
self.id_input.focus = True
self.show_status("Fields cleared", error=False)
def refresh_data(self, instance):
"""Refresh the data display."""
try:
records = self.db_manager.read_all_data()
count = len(records)
self.show_status(f"Data refreshed - {count} records found")
except Exception as e:
self.show_status(f"Error refreshing data: {str(e)}", error=True)
def highlight_record(self, record_id):
"""Highlight a specific record in the display."""
# Since we removed the data display, just show a status message
pass
def show_status(self, message, error=False):
"""Show status message."""
self.status_label.text = message
if error:
self.status_label.color = (1, 0.2, 0.2, 1) # Red for errors
else:
self.status_label.color = (0, 0.8, 0, 1) # Green for success
# Clear status after 5 seconds
Clock.schedule_once(lambda dt: self.clear_status(), 5)
def clear_status(self):
"""Clear the status message."""
self.status_label.text = "Ready"
self.status_label.color = (0, 0.8, 0, 1)
def show_settings(self, instance):
"""Show settings popup for database configuration."""
content = BoxLayout(orientation='vertical', spacing=15, padding=20)
# Title
title_label = Label(text='Database Server Settings', font_size=24, bold=True, size_hint_y=None, height=40)
content.add_widget(title_label)
# IP address input
ip_layout = BoxLayout(orientation='horizontal', size_hint_y=None, height=60, spacing=10)
ip_layout.add_widget(Label(text='Server IP Address:', font_size=20, bold=True, size_hint_x=0.4))
ip_input = TextInput(
text=self.db_manager.host,
multiline=False,
font_size=20,
size_hint_x=0.6,
padding=[10, 10]
)
ip_layout.add_widget(ip_input)
content.add_widget(ip_layout)
# Info label
info_label = Label(
text='Enter the IP address or hostname of the database server.\nOther connection settings remain unchanged.',
font_size=16,
size_hint_y=None,
height=60
)
content.add_widget(info_label)
# Buttons
buttons = BoxLayout(size_hint_y=None, height=60, spacing=15)
save_btn = Button(text='Save', font_size=20, bold=True)
cancel_btn = Button(text='Cancel', font_size=20, bold=True)
buttons.add_widget(save_btn)
buttons.add_widget(cancel_btn)
content.add_widget(buttons)
popup = Popup(
title='Settings',
content=content,
size_hint=(0.6, 0.5)
)
def save_settings():
new_host = ip_input.text.strip()
if new_host:
self.db_manager.host = new_host
# Reconnect with new settings
if self.db_manager.connection:
try:
self.db_manager.connection.close()
except:
pass
self.db_manager.connection = None
self.show_status(f"Database server updated to: {new_host}")
popup.dismiss()
else:
self.show_status("Please enter a valid IP address", error=True)
save_btn.bind(on_press=lambda x: save_settings())
cancel_btn.bind(on_press=popup.dismiss)
popup.open()
def show_confirmation_popup(self, message, confirm_callback):
"""Show a confirmation popup."""
content = BoxLayout(orientation='vertical', spacing=10, padding=10)
label = Label(text=message, text_size=(300, None), halign='center')
content.add_widget(label)
buttons = BoxLayout(size_hint_y=None, height=50, spacing=10)
yes_btn = Button(text='Yes')
no_btn = Button(text='No')
buttons.add_widget(yes_btn)
buttons.add_widget(no_btn)
content.add_widget(buttons)
popup = Popup(
title='Confirm Action',
content=content,
size_hint=(0.8, 0.4)
)
yes_btn.bind(on_press=lambda x: (confirm_callback(), popup.dismiss()))
no_btn.bind(on_press=popup.dismiss)
popup.open()
if __name__ == '__main__':
DatabaseApp().run()

4
requirements.txt Normal file
View File

@@ -0,0 +1,4 @@
kivy>=2.1.0
kivymd>=1.0.0
mysql-connector-python>=8.0.0
pyinstaller>=5.0.0

20
run_app.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/bin/bash
# Startup script for the Database Manager Kivy App
echo "Starting Database Manager App..."
echo "================================"
# Check if virtual environment exists
if [ ! -d "venv" ]; then
echo "Virtual environment not found. Creating one..."
python3 -m venv venv
echo "Installing dependencies..."
source venv/bin/activate
pip install -r requirements.txt
else
source venv/bin/activate
fi
echo "Launching application..."
python main.py

10079
sept.csv Normal file

File diff suppressed because it is too large Load Diff

82
setup_database.sh Executable file
View File

@@ -0,0 +1,82 @@
#!/bin/bash
# MariaDB Database Setup Script
# This script helps set up the MariaDB database for the Kivy app
echo "MariaDB Database Setup for Kivy App"
echo "=================================="
echo
# Check if MariaDB is installed
if ! command -v mysql &> /dev/null; then
echo "❌ MariaDB is not installed."
echo "Please install MariaDB first:"
echo " sudo apt update"
echo " sudo apt install mariadb-server"
exit 1
fi
# Check if MariaDB service is running
if ! systemctl is-active --quiet mariadb; then
echo "⚠️ MariaDB service is not running."
echo "Starting MariaDB service..."
sudo systemctl start mariadb
if systemctl is-active --quiet mariadb; then
echo "✅ MariaDB service started successfully."
else
echo "❌ Failed to start MariaDB service."
exit 1
fi
else
echo "✅ MariaDB service is running."
fi
echo
echo "Setting up database and user..."
echo "You will be prompted for the MySQL root password."
echo
# Create the setup SQL script
cat > /tmp/setup_db.sql << EOF
CREATE DATABASE IF NOT EXISTS cantare_injectie;
CREATE USER IF NOT EXISTS 'omron'@'localhost' IDENTIFIED BY 'Initial01!';
GRANT ALL PRIVILEGES ON cantare_injectie.* TO 'omron'@'localhost';
FLUSH PRIVILEGES;
USE cantare_injectie;
CREATE TABLE IF NOT EXISTS offsystemsCountin (
id VARCHAR(20) PRIMARY KEY,
mass REAL NOT NULL
);
-- Show the created database and table
SHOW DATABASES LIKE 'cantare_injectie';
DESCRIBE offsystemsCounting;
EOF
# Execute the SQL script
mysql -u root -p < /tmp/setup_db.sql
if [ $? -eq 0 ]; then
echo
echo "✅ Database setup completed successfully!"
echo
echo "Database details:"
echo " Host: localhost"
echo " Database: cantare_injectie"
echo " User: omron"
echo " Password: Initial01!"
echo " Table: offsystemsCounting"
echo
echo "You can now run the Kivy application:"
echo " python main.py"
echo
else
echo
echo "❌ Database setup failed."
echo "Please check the error messages above."
fi
# Clean up
rm -f /tmp/setup_db.sql

8
setup_user.sql Normal file
View File

@@ -0,0 +1,8 @@
-- Create user and grant privileges for MariaDB
CREATE USER IF NOT EXISTS 'omron'@'localhost' IDENTIFIED BY 'Initial01!';
GRANT ALL PRIVILEGES ON cantare_injectie.* TO 'omron'@'localhost';
FLUSH PRIVILEGES;
-- Show the user and their privileges
SELECT User, Host FROM mysql.user WHERE User='omron';
SHOW GRANTS FOR 'omron'@'localhost';

101
test_database.py Normal file
View File

@@ -0,0 +1,101 @@
#!/usr/bin/env python3
"""
Test script for the MariaDB Database Manager
This script tests the basic functionality without the GUI
"""
from database_manager import DatabaseManager
def test_database_operations():
print("Testing MariaDB Database Manager...")
print("-" * 40)
# Initialize database
db = DatabaseManager()
# Test 1: Add some initial data
print("1. Adding initial test data...")
test_data = [
("SYS001", 125.5),
("SYS002", 89.7),
("SYS003", 234.1)
]
for record_id, mass in test_data:
success = db.add_or_update_record(record_id, mass)
print(f" Added {record_id}: {'' if success else ''}")
print()
# Test 2: Read all data
print("2. Reading all data...")
records = db.read_all_data()
for record in records:
print(f" ID: {record[0]}, Mass: {record[1]}")
print(f" Total records: {len(records)}")
print()
# Test 3: Search for existing record
print("3. Searching for existing record 'SYS001'...")
result = db.search_by_id("SYS001")
if result:
print(f" Found: {result[0]} = {result[1]}")
else:
print(" Not found")
print()
# Test 4: Search for non-existing record
print("4. Searching for non-existing record 'SYS999'...")
result = db.search_by_id("SYS999")
if result:
print(f" Found: {result[0]} = {result[1]}")
else:
print(" Not found - this is expected!")
print()
# Test 5: Add the missing record
print("5. Adding the missing record 'SYS999'...")
success = db.add_or_update_record("SYS999", 456.8)
print(f" Added SYS999: {'' if success else ''}")
# Verify it was added
result = db.search_by_id("SYS999")
if result:
print(f" Verification: Found {result[0]} = {result[1]}")
print()
# Test 6: Update existing record
print("6. Updating existing record 'SYS001'...")
success = db.add_or_update_record("SYS001", 150.0)
print(f" Updated SYS001: {'' if success else ''}")
result = db.search_by_id("SYS001")
if result:
print(f" New value: {result[0]} = {result[1]}")
print()
# Test 7: Final count
print("7. Final database state...")
records = db.read_all_data()
print(f" Total records: {len(records)}")
for record in records:
print(f" {record[0]} = {record[1]}")
print()
# Test 8: Delete a record
print("8. Testing delete functionality...")
success = db.delete_record("SYS999")
print(f" Deleted SYS999: {'' if success else ''}")
final_records = db.read_all_data()
print(f" Final record count: {len(final_records)}")
print()
# Close connection
db.close_connection()
print("✓ All tests completed successfully!")
print("Note: Test records remain in the MariaDB database.")
if __name__ == "__main__":
test_database_operations()