Compare commits
28 Commits
33c9c3d099
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 58082ed171 | |||
| f09c365384 | |||
| b204ce38fc | |||
| 1cf4482914 | |||
| b9025fcabe | |||
|
|
fa5c846ebb | ||
| 6bcfc3102b | |||
| 92714bcb51 | |||
|
|
396bf19214 | ||
|
|
37c7a5bd7a | ||
|
|
69e0f7f8b1 | ||
|
|
44771dcd11 | ||
|
|
ca42c8e3c7 | ||
|
|
9f45a02f13 | ||
|
|
6f07bb9cba | ||
|
|
1536f26fee | ||
|
|
e838d25c44 | ||
|
|
8e3893e85c | ||
|
|
f2a2ca7d97 | ||
|
|
8301fc8ef4 | ||
|
|
82949fb9bf | ||
|
|
9bfc40d733 | ||
|
|
e7aac91d51 | ||
|
|
795dd5cac2 | ||
|
|
ba54bbfa75 | ||
|
|
5e1cdfb9e5 | ||
|
|
8619debd71 | ||
|
|
184178275b |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,4 @@
|
|||||||
label/
|
label/
|
||||||
|
build/
|
||||||
|
logs/
|
||||||
|
pdf_backup/
|
||||||
|
|||||||
178
BUILD_ON_WINDOWS.md
Normal file
178
BUILD_ON_WINDOWS.md
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
# Building LabelPrinter.exe on Windows
|
||||||
|
|
||||||
|
This guide explains how to build a standalone `LabelPrinter.exe` single-file executable on a Windows machine.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
1. **Python 3.10, 3.11, 3.12, or 3.13** - Download from https://www.python.org/
|
||||||
|
- ⚠️ **IMPORTANT**: Check "Add Python to PATH" during installation
|
||||||
|
- ⚠️ **Note**: Python 3.14+ may have compatibility issues with Kivy 2.2.1
|
||||||
|
- Verify: Open Command Prompt and type `python --version`
|
||||||
|
|
||||||
|
2. **Git** (optional, for cloning the repository)
|
||||||
|
|
||||||
|
3. **Internet connection** - To download dependencies
|
||||||
|
|
||||||
|
## Quick Start (Using Provided Scripts)
|
||||||
|
|
||||||
|
### Option 1: Batch Script (Recommended for CMD users)
|
||||||
|
|
||||||
|
1. Open **Command Prompt** (cmd.exe)
|
||||||
|
2. Navigate to the project folder:
|
||||||
|
```
|
||||||
|
cd C:\path\to\label_print
|
||||||
|
```
|
||||||
|
3. Run the build script:
|
||||||
|
```
|
||||||
|
build_windows.bat
|
||||||
|
```
|
||||||
|
4. Wait 5-15 minutes for the build to complete
|
||||||
|
5. The executable will be in: `dist\LabelPrinter.exe`
|
||||||
|
|
||||||
|
### Option 2: PowerShell Script
|
||||||
|
|
||||||
|
1. Open **PowerShell** (as Administrator recommended)
|
||||||
|
2. Navigate to the project folder:
|
||||||
|
```
|
||||||
|
cd C:\path\to\label_print
|
||||||
|
```
|
||||||
|
3. Allow script execution (if needed):
|
||||||
|
```
|
||||||
|
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||||
|
```
|
||||||
|
4. Run the build script:
|
||||||
|
```
|
||||||
|
.\build_windows.ps1
|
||||||
|
```
|
||||||
|
5. Wait 5-15 minutes for the build to complete
|
||||||
|
6. The executable will be in: `dist\LabelPrinter.exe`
|
||||||
|
|
||||||
|
## Manual Build Steps
|
||||||
|
|
||||||
|
If you prefer to run commands manually:
|
||||||
|
|
||||||
|
### Step 1: Prepare Python Environment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Upgrade pip, setuptools, and wheel
|
||||||
|
python -m pip install --upgrade pip setuptools wheel
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Install Dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install required Python packages
|
||||||
|
pip install python-barcode pillow reportlab kivy==2.2.1 pyinstaller==6.1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Build the Executable
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create single-file executable
|
||||||
|
pyinstaller label_printer_gui.py ^
|
||||||
|
--onefile ^
|
||||||
|
--windowed ^
|
||||||
|
--name=LabelPrinter ^
|
||||||
|
--distpath=./dist ^
|
||||||
|
--workpath=./build ^
|
||||||
|
--hidden-import=kivy ^
|
||||||
|
--hidden-import=PIL ^
|
||||||
|
--hidden-import=barcode ^
|
||||||
|
--hidden-import=reportlab ^
|
||||||
|
--hidden-import=print_label ^
|
||||||
|
--hidden-import=print_label_pdf ^
|
||||||
|
-y
|
||||||
|
```
|
||||||
|
|
||||||
|
The build process will take 5-15 minutes depending on your PC speed.
|
||||||
|
|
||||||
|
### Step 4: Test the Executable
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run the built executable
|
||||||
|
dist\LabelPrinter.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
After successful build, you'll have:
|
||||||
|
|
||||||
|
```
|
||||||
|
dist/
|
||||||
|
└── LabelPrinter.exe ← Single executable file
|
||||||
|
```
|
||||||
|
|
||||||
|
## Distributing the Executable
|
||||||
|
|
||||||
|
You can:
|
||||||
|
1. **Copy `LabelPrinter.exe`** to any Windows PC (no Python needed!)
|
||||||
|
2. **Share via USB** or file transfer
|
||||||
|
3. **Create an installer** using NSIS or InnoSetup (optional)
|
||||||
|
4. **Upload to GitHub Releases** for public distribution
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Error: "Python is not recognized"
|
||||||
|
- Reinstall Python and check "Add Python to PATH"
|
||||||
|
- Restart Command Prompt after reinstalling Python
|
||||||
|
|
||||||
|
### Error: "pip command not found"
|
||||||
|
- Use `python -m pip` instead of `pip`
|
||||||
|
|
||||||
|
### Build takes too long (>30 minutes)
|
||||||
|
- **Normal for first build** - Kivy framework is large
|
||||||
|
- Subsequent builds will be faster due to caching
|
||||||
|
- Close other applications to free up RAM
|
||||||
|
|
||||||
|
### Error: "No module named 'kivy'"
|
||||||
|
- Make sure dependencies installed correctly: `pip install kivy==2.2.1`
|
||||||
|
- Check internet connection
|
||||||
|
|
||||||
|
### Python 3.14 Compatibility Issues
|
||||||
|
If you have Python 3.14 installed and get errors like:
|
||||||
|
- `ModuleNotFoundError: No module named 'kivy'`
|
||||||
|
- `ImportError: DLL load failed`
|
||||||
|
- PyInstaller compatibility errors
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
- Install Python 3.11, 3.12, or 3.13 instead
|
||||||
|
- Download from: https://www.python.org/downloads/
|
||||||
|
- Uninstall Python 3.14 first
|
||||||
|
- Then use one of the recommended versions
|
||||||
|
- Make sure all files are in the project folder:
|
||||||
|
- `label_printer_gui.py`
|
||||||
|
- `print_label.py`
|
||||||
|
- `print_label_pdf.py`
|
||||||
|
- Antivirus might be blocking it - check security software
|
||||||
|
|
||||||
|
## Build Time Reference
|
||||||
|
|
||||||
|
- **First build**: 10-15 minutes (downloading dependencies)
|
||||||
|
- **Subsequent builds**: 5-10 minutes (cached dependencies)
|
||||||
|
|
||||||
|
## Advanced Options
|
||||||
|
|
||||||
|
### Reduce Build Time
|
||||||
|
```bash
|
||||||
|
pyinstaller label_printer_gui.py --onefile --windowed --name=LabelPrinter -y
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add Icon to Executable
|
||||||
|
```bash
|
||||||
|
pyinstaller label_printer_gui.py --onefile --windowed --name=LabelPrinter --icon=path\to\icon.ico -y
|
||||||
|
```
|
||||||
|
|
||||||
|
### Faster: Use --onedir (Directory) instead of --onefile
|
||||||
|
```bash
|
||||||
|
pyinstaller label_printer_gui.py --onedir --windowed --name=LabelPrinter -y
|
||||||
|
# Builds in ~3-5 minutes, but creates a folder instead of single file
|
||||||
|
```
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
If you encounter issues:
|
||||||
|
1. Check the error message carefully
|
||||||
|
2. Make sure Python 3.10+ is installed
|
||||||
|
3. Verify all dependencies: `pip list`
|
||||||
|
4. Check internet connection
|
||||||
|
5. Try again with a fresh Command Prompt window
|
||||||
88
DEPLOYMENT.md
Normal file
88
DEPLOYMENT.md
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# Label Printer - Portable Deployment Guide
|
||||||
|
|
||||||
|
## Deployment Structure
|
||||||
|
|
||||||
|
The app is now **fully self-contained** with SumatraPDF embedded inside:
|
||||||
|
|
||||||
|
```
|
||||||
|
LabelPrinter/
|
||||||
|
├── LabelPrinter.exe # Main application (includes SumatraPDF inside)
|
||||||
|
├── pdf_backup/ # Auto-created: PDF backups
|
||||||
|
└── logs/ # Auto-created: Print logs
|
||||||
|
```
|
||||||
|
|
||||||
|
**No visible folders!** SumatraPDF is bundled inside LabelPrinter.exe and extracted to a temporary location at runtime.
|
||||||
|
|
||||||
|
## Setup Instructions
|
||||||
|
|
||||||
|
### 1. Download SumatraPDF (For Building Only)
|
||||||
|
|
||||||
|
**This step is only needed when building the app.** SumatraPDF will be embedded inside the executable.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# PowerShell command to download SumatraPDF
|
||||||
|
powershell -ExecutionPolicy Bypass -File setup_sumatra.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
This downloads SumatraPDF portable (~5 MB) to the `SumatraPDF` folder.
|
||||||
|
|
||||||
|
### 2. Build the Application
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\build_windows.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
The build script will:
|
||||||
|
- Check for SumatraPDF
|
||||||
|
- Bundle it inside the executable
|
||||||
|
- Create `dist\LabelPrinter.exe` (~80 MB including all dependencies)
|
||||||
|
|
||||||
|
### 3. Deploy
|
||||||
|
|
||||||
|
**Simply copy `LabelPrinter.exe` to any Windows machine!**
|
||||||
|
|
||||||
|
```
|
||||||
|
📁 Deployment (any folder)
|
||||||
|
└── LabelPrinter.exe ← Just this one file!
|
||||||
|
```
|
||||||
|
|
||||||
|
- No installation needed
|
||||||
|
- No additional files or folders
|
||||||
|
- Double-click to run
|
||||||
|
- Works on any Windows 10/11 machine
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- ✅ **Single Executable** - Everything bundled in one .exe file (~80 MB)
|
||||||
|
- ✅ **Fully Portable** - No installation needed, no external dependencies
|
||||||
|
- ✅ **Silent Printing** - No PDF viewer windows pop up
|
||||||
|
- ✅ **Network Printers** - Supports printers from print servers (e.g. `\\server\printer`)
|
||||||
|
- ✅ **PDF Backup** - All labels saved to `pdf_backup/` folder
|
||||||
|
- ✅ **Print Logging** - CSV logs in `logs/` folder
|
||||||
|
- ✅ **SumatraPDF Hidden** - Embedded inside, not visible to users
|
||||||
|
|
||||||
|
## Printer Name Display
|
||||||
|
|
||||||
|
Network printer names (e.g. `\\filesibiusb05\ZDesigner_ZQ630`) are automatically shortened to 20 characters in the dropdown for better display. The full printer name is used for actual printing.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Printing Not Working
|
||||||
|
|
||||||
|
1. **Check Printer Connection**: Verify printer is online and accessible
|
||||||
|
2. **Check PDF Backup**: Labels are always saved to `pdf_backup/` folder even if printing fails
|
||||||
|
3. **Check Logs**: View print logs in `logs/` folder for error messages
|
||||||
|
4. **Rebuild App**: If you built the app yourself, ensure `setup_sumatra.ps1` was run first to download SumatraPDF before building
|
||||||
|
|
||||||
|
### Network Printers Not Showing
|
||||||
|
|
||||||
|
- Network printers must be installed/connected on the machine before running the app
|
||||||
|
- For print server printers like `\\filesibiusb05\printer`, ensure the share is accessible
|
||||||
|
- Run as administrator if printer enumeration fails
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- First run may be slower (Kivy initialization)
|
||||||
|
- PDF backups are auto-deleted after 5 days
|
||||||
|
- Log files are auto-deleted after 5 days
|
||||||
|
- Supports Python 3.10-3.13 (Python 3.14+ may have issues with Kivy)
|
||||||
38
LabelPrinter.spec
Normal file
38
LabelPrinter.spec
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
a = Analysis(
|
||||||
|
['label_printer_gui.py'],
|
||||||
|
pathex=[],
|
||||||
|
binaries=[('SumatraPDF\\SumatraPDF.exe', '.')],
|
||||||
|
datas=[],
|
||||||
|
hiddenimports=['kivy', 'PIL', 'barcode', 'reportlab', 'print_label', 'print_label_pdf'],
|
||||||
|
hookspath=[],
|
||||||
|
hooksconfig={},
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
noarchive=False,
|
||||||
|
optimize=0,
|
||||||
|
)
|
||||||
|
pyz = PYZ(a.pure)
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.datas,
|
||||||
|
[],
|
||||||
|
name='LabelPrinter',
|
||||||
|
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,
|
||||||
|
)
|
||||||
227
PYINSTALLER_GUIDE.md
Normal file
227
PYINSTALLER_GUIDE.md
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
# Building a Standalone EXE with PyInstaller
|
||||||
|
|
||||||
|
This guide explains how to create a standalone Windows executable (`.exe`) file that doesn't require Python to be installed.
|
||||||
|
|
||||||
|
## Quick Start (Windows)
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
- Python 3.11+ installed
|
||||||
|
- Virtual environment activated
|
||||||
|
- All dependencies installed
|
||||||
|
|
||||||
|
### One-Command Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Activate virtual environment
|
||||||
|
venv\Scripts\activate
|
||||||
|
|
||||||
|
# Build the executable
|
||||||
|
python build_exe.py
|
||||||
|
```
|
||||||
|
|
||||||
|
That's it! Your executable will be in `dist/LabelPrinter.exe`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What Happens During Build
|
||||||
|
|
||||||
|
1. **Analyzes your code** - Finds all imported modules
|
||||||
|
2. **Collects dependencies** - Bundles Kivy, PIL, barcode, reportlab, etc.
|
||||||
|
3. **Creates executable** - Packages everything into one `.exe` file
|
||||||
|
4. **Output**: `dist/LabelPrinter.exe` (~150-200 MB)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Detailed Build Instructions
|
||||||
|
|
||||||
|
### Step 1: Install PyInstaller
|
||||||
|
```bash
|
||||||
|
venv\Scripts\activate
|
||||||
|
pip install pyinstaller
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Build Using Script
|
||||||
|
```bash
|
||||||
|
python build_exe.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Alternative Manual Build
|
||||||
|
If the script doesn't work, use this command directly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pyinstaller ^
|
||||||
|
--onefile ^
|
||||||
|
--windowed ^
|
||||||
|
--name=LabelPrinter ^
|
||||||
|
--hidden-import=kivy ^
|
||||||
|
--hidden-import=kivy.core.window ^
|
||||||
|
--hidden-import=kivy.core.text ^
|
||||||
|
--hidden-import=kivy.core.image ^
|
||||||
|
--hidden-import=kivy.uix.boxlayout ^
|
||||||
|
--hidden-import=kivy.uix.gridlayout ^
|
||||||
|
--hidden-import=kivy.uix.label ^
|
||||||
|
--hidden-import=kivy.uix.textinput ^
|
||||||
|
--hidden-import=kivy.uix.button ^
|
||||||
|
--hidden-import=kivy.uix.spinner ^
|
||||||
|
--hidden-import=kivy.uix.scrollview ^
|
||||||
|
--hidden-import=kivy.uix.popup ^
|
||||||
|
--hidden-import=kivy.clock ^
|
||||||
|
--hidden-import=kivy.graphics ^
|
||||||
|
--hidden-import=PIL ^
|
||||||
|
--hidden-import=barcode ^
|
||||||
|
--hidden-import=reportlab ^
|
||||||
|
--hidden-import=print_label ^
|
||||||
|
--hidden-import=print_label_pdf ^
|
||||||
|
--collect-all=kivy ^
|
||||||
|
--collect-all=PIL ^
|
||||||
|
label_printer_gui.py
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Output Files
|
||||||
|
|
||||||
|
After building, you'll have:
|
||||||
|
|
||||||
|
```
|
||||||
|
Label-design/
|
||||||
|
├── dist/
|
||||||
|
│ └── LabelPrinter.exe ← Your standalone executable (150-200 MB)
|
||||||
|
├── build/ ← Temporary build files (can delete)
|
||||||
|
└── label_printer.spec ← PyInstaller spec file
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Running the Executable
|
||||||
|
|
||||||
|
### On Your Computer
|
||||||
|
1. Double-click `dist/LabelPrinter.exe`
|
||||||
|
2. App starts immediately (first run takes ~5 seconds)
|
||||||
|
3. Works like the Python version
|
||||||
|
|
||||||
|
### Sharing with Others
|
||||||
|
1. Copy `dist/LabelPrinter.exe` to a folder
|
||||||
|
2. Create a shortcut to it on the desktop
|
||||||
|
3. Share the folder or executable
|
||||||
|
4. **No Python installation needed on their computer!**
|
||||||
|
|
||||||
|
### Creating a Shortcut
|
||||||
|
1. Right-click `LabelPrinter.exe`
|
||||||
|
2. Send to → Desktop (create shortcut)
|
||||||
|
3. Double-click the shortcut to run
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "Failed to build the executable"
|
||||||
|
|
||||||
|
**Solution 1**: Check Python version
|
||||||
|
```bash
|
||||||
|
python --version # Should be 3.11+
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution 2**: Update PyInstaller
|
||||||
|
```bash
|
||||||
|
pip install --upgrade pyinstaller
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution 3**: Install missing dependencies
|
||||||
|
```bash
|
||||||
|
pip install -r requirements_windows.txt
|
||||||
|
pip install pyinstaller
|
||||||
|
```
|
||||||
|
|
||||||
|
### "DLL load failed" when running exe
|
||||||
|
|
||||||
|
This usually means a library isn't bundled correctly.
|
||||||
|
|
||||||
|
**Solution**: Rebuild with verbose output
|
||||||
|
```bash
|
||||||
|
pyinstaller --debug=imports label_printer_gui.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Executable is very large (200+ MB)
|
||||||
|
|
||||||
|
This is normal for Kivy applications. The size includes:
|
||||||
|
- Python runtime (~50 MB)
|
||||||
|
- Kivy framework (~30 MB)
|
||||||
|
- Dependencies (PIL, barcode, reportlab, etc.) (~20 MB)
|
||||||
|
- Your code (~1 KB)
|
||||||
|
|
||||||
|
You can reduce size slightly with:
|
||||||
|
```bash
|
||||||
|
--exclude-module=matplotlib
|
||||||
|
--exclude-module=numpy
|
||||||
|
--exclude-module=scipy
|
||||||
|
```
|
||||||
|
|
||||||
|
### Slow to start (5-10 seconds)
|
||||||
|
|
||||||
|
Normal for Kivy apps. The first startup initializes:
|
||||||
|
- Python runtime
|
||||||
|
- Kivy graphics system
|
||||||
|
- Font rendering
|
||||||
|
- Window initialization
|
||||||
|
|
||||||
|
Subsequent runs are faster (~3 seconds).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Advanced Options
|
||||||
|
|
||||||
|
### Add an Icon
|
||||||
|
1. Create a 256x256 PNG icon: `app_icon.png`
|
||||||
|
2. Convert to ICO: Use an online tool or ImageMagick
|
||||||
|
3. Build with icon:
|
||||||
|
```bash
|
||||||
|
pyinstaller --icon=app_icon.ico label_printer_gui.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Two-File Distribution
|
||||||
|
Instead of `--onefile`, use separate files for faster startup:
|
||||||
|
```bash
|
||||||
|
pyinstaller label_printer_gui.py
|
||||||
|
```
|
||||||
|
Creates `dist/` folder with all files (faster to run, easier to debug).
|
||||||
|
|
||||||
|
### Console Output
|
||||||
|
To see error messages, remove `--windowed`:
|
||||||
|
```bash
|
||||||
|
pyinstaller --onefile --name=LabelPrinter label_printer_gui.py
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Build Options Reference
|
||||||
|
|
||||||
|
| Option | Purpose |
|
||||||
|
|--------|---------|
|
||||||
|
| `--onefile` | Single executable (recommended) |
|
||||||
|
| `--windowed` | No console window |
|
||||||
|
| `--icon=file.ico` | Custom icon |
|
||||||
|
| `--hidden-import=module` | Include module that's not imported directly |
|
||||||
|
| `--collect-all=module` | Include all module data |
|
||||||
|
| `--distpath=folder` | Output directory |
|
||||||
|
| `--name=AppName` | Executable name |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Final Steps
|
||||||
|
|
||||||
|
1. **Test the executable**: Run `LabelPrinter.exe` and test all features
|
||||||
|
2. **Verify PDF backup**: Check `pdf_backup/` folder is created
|
||||||
|
3. **Test printing**: Print a label to ensure PDF output works
|
||||||
|
4. **Share**: Distribute the `.exe` file to users
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Questions?
|
||||||
|
|
||||||
|
- Check the error message in the console
|
||||||
|
- Try rebuilding with `python build_exe.py`
|
||||||
|
- Ensure all dependencies are installed: `pip install -r requirements_windows.txt`
|
||||||
|
- Check that Python 3.11+ is installed: `python --version`
|
||||||
|
|
||||||
|
Good luck! 🚀
|
||||||
234
README.md
Normal file
234
README.md
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
# Label Printer GUI
|
||||||
|
|
||||||
|
A cross-platform barcode label printing application with a modern GUI. Create, generate, and print labels with automatic Code128 barcode encoding.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
✨ **Core Features**
|
||||||
|
- 🎨 Beautiful Kivy GUI interface
|
||||||
|
- 📊 Automatic Code128 barcode generation
|
||||||
|
- 📄 High-quality PDF label generation
|
||||||
|
- 💾 Automatic PDF backup system
|
||||||
|
- ✅ Input validation with 25-character limit
|
||||||
|
- 🔢 Number-only filter for quantity field
|
||||||
|
|
||||||
|
🖨️ **Printer Support**
|
||||||
|
- Windows printer detection and printing
|
||||||
|
- Linux CUPS printer support
|
||||||
|
- macOS printing support
|
||||||
|
- PDF fallback (works everywhere)
|
||||||
|
|
||||||
|
🚀 **Distribution**
|
||||||
|
- PyInstaller support for standalone Windows .exe
|
||||||
|
- No Python installation needed
|
||||||
|
- Cross-platform source code (Windows, Linux, macOS)
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Option 1: Python Source (Recommended for Development)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Clone/Download the project
|
||||||
|
cd Label-design
|
||||||
|
|
||||||
|
# 2. Create virtual environment
|
||||||
|
python -m venv venv
|
||||||
|
source venv/bin/activate # On Windows: venv\Scripts\activate
|
||||||
|
|
||||||
|
# 3. Install dependencies
|
||||||
|
pip install -r requirements_gui.txt
|
||||||
|
|
||||||
|
# 4. Run the app
|
||||||
|
python label_printer_gui.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: Windows Standalone Executable
|
||||||
|
|
||||||
|
1. Download `LabelPrinter.exe` from releases
|
||||||
|
2. Double-click to run (no Python needed!)
|
||||||
|
3. First run takes ~5 seconds to initialize
|
||||||
|
|
||||||
|
## Building Your Own Executable
|
||||||
|
|
||||||
|
### Windows Build Steps
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Activate virtual environment
|
||||||
|
venv\Scripts\activate
|
||||||
|
|
||||||
|
# 2. Install PyInstaller
|
||||||
|
pip install pyinstaller
|
||||||
|
|
||||||
|
# 3. Build executable
|
||||||
|
python build_exe.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Your executable will be in `dist/LabelPrinter.exe` (~200 MB)
|
||||||
|
|
||||||
|
### Manual Build Command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pyinstaller --onefile --windowed --name=LabelPrinter ^
|
||||||
|
--hidden-import=kivy ^
|
||||||
|
--hidden-import=kivy.core.window ^
|
||||||
|
--hidden-import=kivy.core.text ^
|
||||||
|
--hidden-import=kivy.core.image ^
|
||||||
|
--hidden-import=kivy.uix.boxlayout ^
|
||||||
|
--hidden-import=kivy.uix.gridlayout ^
|
||||||
|
--hidden-import=kivy.uix.label ^
|
||||||
|
--hidden-import=kivy.uix.textinput ^
|
||||||
|
--hidden-import=kivy.uix.button ^
|
||||||
|
--hidden-import=kivy.uix.spinner ^
|
||||||
|
--hidden-import=kivy.uix.scrollview ^
|
||||||
|
--hidden-import=kivy.uix.popup ^
|
||||||
|
--hidden-import=kivy.clock ^
|
||||||
|
--hidden-import=kivy.graphics ^
|
||||||
|
--hidden-import=PIL ^
|
||||||
|
--hidden-import=barcode ^
|
||||||
|
--hidden-import=reportlab ^
|
||||||
|
--hidden-import=print_label ^
|
||||||
|
--hidden-import=print_label_pdf ^
|
||||||
|
label_printer_gui.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
Label-design/
|
||||||
|
├── label_printer_gui.py # Main GUI application
|
||||||
|
├── print_label.py # Printing functionality
|
||||||
|
├── print_label_pdf.py # PDF generation
|
||||||
|
├── build_exe.py # PyInstaller build script
|
||||||
|
├── requirements_gui.txt # GUI dependencies
|
||||||
|
├── pdf_backup/ # Generated label PDFs
|
||||||
|
├── dist/ # Built executables
|
||||||
|
├── documentation/ # Docs and guides
|
||||||
|
│ ├── WINDOWS_SETUP.md
|
||||||
|
│ ├── PYINSTALLER_GUIDE.md
|
||||||
|
│ └── [other docs]
|
||||||
|
└── venv/ # Python virtual environment
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
**Required (Core):**
|
||||||
|
- `python-barcode` - Barcode generation
|
||||||
|
- `pillow` - Image processing
|
||||||
|
- `reportlab` - PDF generation
|
||||||
|
|
||||||
|
**GUI:**
|
||||||
|
- `kivy` - Cross-platform GUI framework
|
||||||
|
|
||||||
|
**Optional (Printing):**
|
||||||
|
- `pycups` - Linux CUPS support
|
||||||
|
- `pywin32` - Windows printer support
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Basic Workflow
|
||||||
|
|
||||||
|
1. **Enter Data:**
|
||||||
|
- **SAP-Nr**: Article code (up to 25 chars)
|
||||||
|
- **Cantitate**: Quantity (numbers only)
|
||||||
|
- **ID rola**: Reel/Cable ID (up to 25 chars)
|
||||||
|
|
||||||
|
2. **Select Printer:**
|
||||||
|
- Choose from detected printers
|
||||||
|
- Or select "PDF" for PDF output
|
||||||
|
|
||||||
|
3. **Print:**
|
||||||
|
- Click "PRINT LABEL"
|
||||||
|
- PDF is auto-saved to `pdf_backup/` folder
|
||||||
|
- Label sent to printer
|
||||||
|
|
||||||
|
### PDF Backup
|
||||||
|
|
||||||
|
All generated labels are automatically saved with timestamps:
|
||||||
|
```
|
||||||
|
pdf_backup/
|
||||||
|
├── final_label_20260205_120530.pdf
|
||||||
|
├── final_label_20260205_120542.pdf
|
||||||
|
└── final_label_20260205_120555.pdf
|
||||||
|
```
|
||||||
|
|
||||||
|
## Guides
|
||||||
|
|
||||||
|
- **[WINDOWS_SETUP.md](documentation/WINDOWS_SETUP.md)** - Windows installation guide
|
||||||
|
- **[PYINSTALLER_GUIDE.md](documentation/PYINSTALLER_GUIDE.md)** - Building executables
|
||||||
|
- **[documentation/](documentation/)** - All documentation
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "No Printers Found"
|
||||||
|
This is normal. Select "PDF" option - labels will be saved to `pdf_backup/` folder.
|
||||||
|
|
||||||
|
### "GUI Won't Start"
|
||||||
|
Ensure all dependencies are installed:
|
||||||
|
```bash
|
||||||
|
pip install -r requirements_gui.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Windows Executable Issues
|
||||||
|
- Update PyInstaller: `pip install --upgrade pyinstaller`
|
||||||
|
- Rebuild: `python build_exe.py`
|
||||||
|
- Check dependencies: `pip list`
|
||||||
|
|
||||||
|
### Kivy Graphics Issues
|
||||||
|
On Linux, you may need SDL2 dependencies:
|
||||||
|
```bash
|
||||||
|
sudo apt-get install libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Platform Support
|
||||||
|
|
||||||
|
| Platform | Source | Executable | Status |
|
||||||
|
|----------|--------|-----------|--------|
|
||||||
|
| Windows | ✅ Yes | ✅ Yes | ✅ Fully Supported |
|
||||||
|
| Linux | ✅ Yes | ❌ No | ✅ Fully Supported |
|
||||||
|
| macOS | ✅ Yes | ⚠️ Possible | ⚠️ Untested |
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
### Barcode Format
|
||||||
|
- **Type**: Code128
|
||||||
|
- **Max Length**: 25 characters
|
||||||
|
- **DPI**: 300 (print quality)
|
||||||
|
|
||||||
|
### PDF Specifications
|
||||||
|
- **Page Size**: 11.5 x 8 cm (landscape)
|
||||||
|
- **Quality**: High-resolution barcodes
|
||||||
|
- **Font**: Helvetica with automatic sizing
|
||||||
|
|
||||||
|
### GUI Specifications
|
||||||
|
- **Framework**: Kivy 2.3+
|
||||||
|
- **Size**: 420 x 700 pixels (mobile-optimized)
|
||||||
|
- **Color**: White text on dark background
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Feel free to fork, modify, and improve!
|
||||||
|
|
||||||
|
Suggested improvements:
|
||||||
|
- [ ] Custom barcode formats (QR, Code39, etc.)
|
||||||
|
- [ ] Batch label printing
|
||||||
|
- [ ] Label preview before printing
|
||||||
|
- [ ] Printer-specific settings
|
||||||
|
- [ ] Multi-language support
|
||||||
|
- [ ] Database integration
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Open source - modify and use freely
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
For issues, questions, or suggestions:
|
||||||
|
1. Check the documentation in `documentation/` folder
|
||||||
|
2. Review the code comments
|
||||||
|
3. Test with the source code first before building exe
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Status**: Production Ready ✅
|
||||||
|
|
||||||
|
Last Updated: February 2026
|
||||||
BIN
SumatraPDF/SumatraPDF.exe
Normal file
BIN
SumatraPDF/SumatraPDF.exe
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
83
build_exe.py
Normal file
83
build_exe.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
"""
|
||||||
|
PyInstaller build script for Label Printer GUI
|
||||||
|
Run this to create a standalone Windows executable
|
||||||
|
|
||||||
|
IMPORTANT: This script MUST be run on Windows to generate a Windows .exe file.
|
||||||
|
If run on Linux/macOS, it will create a Linux/macOS binary that won't work on Windows.
|
||||||
|
|
||||||
|
To build for Windows:
|
||||||
|
1. Copy this project to a Windows machine
|
||||||
|
2. Install dependencies: pip install -r requirements_windows.txt
|
||||||
|
3. Run this script: python build_exe.py
|
||||||
|
4. The Windows .exe will be created in the dist/ folder
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
# Get the current directory
|
||||||
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
# PyInstaller arguments
|
||||||
|
args = [
|
||||||
|
'label_printer_gui.py',
|
||||||
|
'--onefile', # Create a single executable
|
||||||
|
'--windowed', # Don't show console window
|
||||||
|
'--name=LabelPrinter', # Executable name
|
||||||
|
'--distpath=./dist', # Output directory
|
||||||
|
'--workpath=./build', # Work directory (was --buildpath)
|
||||||
|
'--hidden-import=kivy',
|
||||||
|
'--hidden-import=kivy.core.window',
|
||||||
|
'--hidden-import=kivy.core.text',
|
||||||
|
'--hidden-import=kivy.core.image',
|
||||||
|
'--hidden-import=kivy.uix.boxlayout',
|
||||||
|
'--hidden-import=kivy.uix.gridlayout',
|
||||||
|
'--hidden-import=kivy.uix.label',
|
||||||
|
'--hidden-import=kivy.uix.textinput',
|
||||||
|
'--hidden-import=kivy.uix.button',
|
||||||
|
'--hidden-import=kivy.uix.spinner',
|
||||||
|
'--hidden-import=kivy.uix.scrollview',
|
||||||
|
'--hidden-import=kivy.uix.popup',
|
||||||
|
'--hidden-import=kivy.clock',
|
||||||
|
'--hidden-import=kivy.graphics',
|
||||||
|
'--hidden-import=PIL',
|
||||||
|
'--hidden-import=barcode',
|
||||||
|
'--hidden-import=reportlab',
|
||||||
|
'--hidden-import=print_label',
|
||||||
|
'--hidden-import=print_label_pdf',
|
||||||
|
]
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print("=" * 60)
|
||||||
|
print("Label Printer GUI - PyInstaller Build")
|
||||||
|
print("=" * 60)
|
||||||
|
print("\nBuilding standalone executable...")
|
||||||
|
print("This may take a few minutes...\n")
|
||||||
|
|
||||||
|
# Change to script directory
|
||||||
|
os.chdir(script_dir)
|
||||||
|
|
||||||
|
# Run PyInstaller directly with subprocess for better error reporting
|
||||||
|
try:
|
||||||
|
result = subprocess.run(['pyinstaller'] + args, check=True)
|
||||||
|
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("Build Complete!")
|
||||||
|
print("=" * 60)
|
||||||
|
print("\nExecutable location: ./dist/LabelPrinter.exe")
|
||||||
|
print("\nYou can now:")
|
||||||
|
print("1. Double-click LabelPrinter.exe to run")
|
||||||
|
print("2. Share the exe with others")
|
||||||
|
print("3. Create a shortcut on desktop")
|
||||||
|
print("\nNote: First run may take a moment as Kivy initializes")
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("Build Failed!")
|
||||||
|
print("=" * 60)
|
||||||
|
print(f"\nError code: {e.returncode}")
|
||||||
|
print("\nPlease check the error messages above for details.")
|
||||||
|
sys.exit(1)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\nFatal error: {e}")
|
||||||
|
sys.exit(1)
|
||||||
99
build_windows.bat
Normal file
99
build_windows.bat
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
@echo off
|
||||||
|
REM Label Printer - Windows Build Script (Single File EXE)
|
||||||
|
REM This script builds a standalone LabelPrinter.exe on Windows
|
||||||
|
REM Requirements: Python 3.10-3.13 installed and in PATH
|
||||||
|
REM Note: Python 3.14+ may have compatibility issues
|
||||||
|
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ========================================================
|
||||||
|
echo Label Printer - Windows Build Script
|
||||||
|
echo Creates: LabelPrinter.exe (Single File)
|
||||||
|
echo ========================================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM Check if Python is installed
|
||||||
|
python --version >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo ERROR: Python is not installed or not in PATH
|
||||||
|
echo Please install Python 3.10-3.13 from https://www.python.org/
|
||||||
|
echo Make sure to check "Add Python to PATH" during installation
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [1/5] Checking Python installation...
|
||||||
|
python --version
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM Upgrade pip
|
||||||
|
echo [2/5] Upgrading pip, setuptools, and wheel...
|
||||||
|
python -m pip install --upgrade pip setuptools wheel
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo ERROR: Failed to upgrade pip
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM Install dependencies
|
||||||
|
echo [3/5] Installing dependencies...
|
||||||
|
echo Installing: python-barcode, pillow, reportlab, kivy, pyinstaller, pywin32, wmi...
|
||||||
|
pip install python-barcode pillow reportlab kivy==2.2.1 pyinstaller==6.1.0 pywin32 wmi
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo ERROR: Failed to install dependencies
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM Clean old build
|
||||||
|
echo [4/5] Cleaning old build artifacts...
|
||||||
|
if exist "dist" rmdir /s /q dist
|
||||||
|
if exist "build" rmdir /s /q build
|
||||||
|
if exist "*.spec" del *.spec
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM Build with PyInstaller
|
||||||
|
echo [5/5] Building executable with PyInstaller...
|
||||||
|
echo This may take 5-15 minutes, please wait...
|
||||||
|
echo.
|
||||||
|
|
||||||
|
pyinstaller label_printer_gui.py ^
|
||||||
|
--onefile ^
|
||||||
|
--windowed ^
|
||||||
|
--name=LabelPrinter ^
|
||||||
|
--distpath=./dist ^
|
||||||
|
--workpath=./build ^
|
||||||
|
--hidden-import=kivy ^
|
||||||
|
--hidden-import=PIL ^
|
||||||
|
--hidden-import=barcode ^
|
||||||
|
--hidden-import=reportlab ^
|
||||||
|
--hidden-import=print_label ^
|
||||||
|
--hidden-import=print_label_pdf ^
|
||||||
|
-y
|
||||||
|
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo.
|
||||||
|
echo ERROR: Build failed!
|
||||||
|
echo Please check the error messages above.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ========================================================
|
||||||
|
echo BUILD SUCCESSFUL!
|
||||||
|
echo ========================================================
|
||||||
|
echo.
|
||||||
|
echo Executable Location: dist\LabelPrinter.exe
|
||||||
|
echo.
|
||||||
|
echo Next steps:
|
||||||
|
echo 1. Navigate to the dist folder
|
||||||
|
echo 2. Double-click LabelPrinter.exe to run
|
||||||
|
echo 3. You can copy LabelPrinter.exe to other machines
|
||||||
|
echo.
|
||||||
|
echo Note: First run may take a moment as Kivy initializes
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
130
build_windows.ps1
Normal file
130
build_windows.ps1
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
# Label Printer - Windows Build Script (Single File EXE)
|
||||||
|
# This script builds a standalone LabelPrinter.exe on Windows
|
||||||
|
# Requirements: Python 3.10-3.13 installed and in PATH
|
||||||
|
# Note: Python 3.14+ may have compatibility issues
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "========================================================"
|
||||||
|
Write-Host " Label Printer - Windows Build Script"
|
||||||
|
Write-Host " Creates: LabelPrinter.exe (Single File)"
|
||||||
|
Write-Host "========================================================"
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# Check if Python is installed
|
||||||
|
try {
|
||||||
|
$pythonVersion = python --version 2>&1
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "Python not found"
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Host "ERROR: Python is not installed or not in PATH" -ForegroundColor Red
|
||||||
|
Write-Host "Please install Python 3.10-3.13 from https://www.python.org/"
|
||||||
|
Write-Host "Make sure to check 'Add Python to PATH' during installation"
|
||||||
|
Read-Host "Press Enter to exit"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "[1/5] Checking Python installation..." -ForegroundColor Cyan
|
||||||
|
Write-Host $pythonVersion
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
Write-Host "[2/5] Upgrading pip, setuptools, and wheel..." -ForegroundColor Cyan
|
||||||
|
python -m pip install --upgrade pip setuptools wheel
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Host "ERROR: Failed to upgrade pip" -ForegroundColor Red
|
||||||
|
Read-Host "Press Enter to exit"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
Write-Host "[3/5] Installing dependencies..." -ForegroundColor Cyan
|
||||||
|
Write-Host "Installing: python-barcode, pillow, reportlab, kivy, pyinstaller, pywin32, wmi..."
|
||||||
|
pip install python-barcode pillow reportlab kivy pyinstaller pywin32 wmi
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Host "ERROR: Failed to install dependencies" -ForegroundColor Red
|
||||||
|
Read-Host "Press Enter to exit"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
Write-Host "[4/6] Checking for SumatraPDF..." -ForegroundColor Cyan
|
||||||
|
$sumatraPath = "SumatraPDF\SumatraPDF.exe"
|
||||||
|
if (-not (Test-Path $sumatraPath)) {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "WARNING: SumatraPDF not found!" -ForegroundColor Yellow
|
||||||
|
Write-Host "SumatraPDF is required for silent PDF printing." -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Run the setup script first:" -ForegroundColor Yellow
|
||||||
|
Write-Host " powershell -ExecutionPolicy Bypass -File setup_sumatra.ps1" -ForegroundColor Cyan
|
||||||
|
Write-Host ""
|
||||||
|
$response = Read-Host "Continue building without SumatraPDF? (y/n)"
|
||||||
|
if ($response -ne "y") {
|
||||||
|
Write-Host "Build cancelled."
|
||||||
|
Read-Host "Press Enter to exit"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Building without SumatraPDF (PDF printing will not work)..." -ForegroundColor Yellow
|
||||||
|
$addBinaryArg = @()
|
||||||
|
} else {
|
||||||
|
Write-Host "Found: $sumatraPath" -ForegroundColor Green
|
||||||
|
# Add SumatraPDF as bundled binary (will be embedded inside the exe)
|
||||||
|
$addBinaryArg = @("--add-binary", "$sumatraPath;.")
|
||||||
|
}
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
Write-Host "[5/6] Cleaning old build artifacts..." -ForegroundColor Cyan
|
||||||
|
if (Test-Path "dist") { Remove-Item -Recurse -Force "dist" }
|
||||||
|
if (Test-Path "build") { Remove-Item -Recurse -Force "build" }
|
||||||
|
Remove-Item -Force "*.spec" -ErrorAction SilentlyContinue
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
Write-Host "[6/6] Building executable with PyInstaller..." -ForegroundColor Cyan
|
||||||
|
Write-Host "This may take 5-15 minutes, please wait..."
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
$pyinstallerArgs = @(
|
||||||
|
"label_printer_gui.py",
|
||||||
|
"--onefile",
|
||||||
|
"--windowed",
|
||||||
|
"--name=LabelPrinter",
|
||||||
|
"--distpath=./dist",
|
||||||
|
"--workpath=./build",
|
||||||
|
"--hidden-import=kivy",
|
||||||
|
"--hidden-import=PIL",
|
||||||
|
"--hidden-import=barcode",
|
||||||
|
"--hidden-import=reportlab",
|
||||||
|
"--hidden-import=print_label",
|
||||||
|
"--hidden-import=print_label_pdf",
|
||||||
|
"-y"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add SumatraPDF binary if available (bundles inside the exe)
|
||||||
|
if ($addBinaryArg) {
|
||||||
|
$pyinstallerArgs += $addBinaryArg
|
||||||
|
}
|
||||||
|
|
||||||
|
pyinstaller @pyinstallerArgs
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "ERROR: Build failed!" -ForegroundColor Red
|
||||||
|
Write-Host "Please check the error messages above." -ForegroundColor Red
|
||||||
|
Read-Host "Press Enter to exit"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "========================================================"
|
||||||
|
Write-Host " BUILD SUCCESSFUL!" -ForegroundColor Green
|
||||||
|
Write-Host "========================================================"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Executable Location: dist\LabelPrinter.exe" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Next steps:"
|
||||||
|
Write-Host " 1. Navigate to the dist folder"
|
||||||
|
Write-Host " 2. Double-click LabelPrinter.exe to run"
|
||||||
|
Write-Host " 3. You can copy LabelPrinter.exe to other machines"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Note: First run may take a moment as Kivy initializes"
|
||||||
|
Write-Host ""
|
||||||
|
Read-Host "Press Enter to exit"
|
||||||
BIN
dist/LabelPrinter.exe
vendored
Normal file
BIN
dist/LabelPrinter.exe
vendored
Normal file
Binary file not shown.
114
documentation/WINDOWS_SETUP.md
Normal file
114
documentation/WINDOWS_SETUP.md
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
# Label Printer GUI - Windows Setup Guide
|
||||||
|
|
||||||
|
## Installation Steps
|
||||||
|
|
||||||
|
### 1. Install Python
|
||||||
|
- Download Python 3.11+ from [python.org](https://www.python.org/downloads/)
|
||||||
|
- **Important**: Check "Add Python to PATH" during installation
|
||||||
|
|
||||||
|
### 2. Create Virtual Environment
|
||||||
|
```bash
|
||||||
|
python -m venv venv
|
||||||
|
venv\Scripts\activate
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Install Dependencies
|
||||||
|
```bash
|
||||||
|
pip install -r requirements_windows.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Optional: Windows Printer Support (pywin32)
|
||||||
|
After installing requirements, run:
|
||||||
|
```bash
|
||||||
|
python -m pip install --upgrade pywin32
|
||||||
|
python Scripts/pywin32_postinstall.py -install
|
||||||
|
```
|
||||||
|
|
||||||
|
This enables native Windows printer detection.
|
||||||
|
|
||||||
|
## Running the App
|
||||||
|
|
||||||
|
### From Command Prompt
|
||||||
|
```bash
|
||||||
|
venv\Scripts\activate
|
||||||
|
python label_printer_gui.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create Shortcut (Optional)
|
||||||
|
Create a batch file `run_app.bat`:
|
||||||
|
```batch
|
||||||
|
@echo off
|
||||||
|
call venv\Scripts\activate.bat
|
||||||
|
python label_printer_gui.py
|
||||||
|
pause
|
||||||
|
```
|
||||||
|
|
||||||
|
Then double-click the batch file to run the app.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
✅ **Cross-Platform GUI** - Works on Windows, Linux, and macOS
|
||||||
|
✅ **Barcode Generation** - Automatic Code128 barcode creation
|
||||||
|
✅ **PDF Output** - High-quality PDF labels stored in `pdf_backup/` folder
|
||||||
|
✅ **Printer Support** - Automatic printer detection (Windows, Linux, macOS)
|
||||||
|
✅ **Input Validation** - 25-character limit with real-time validation
|
||||||
|
✅ **PDF Backup** - All generated labels automatically saved
|
||||||
|
|
||||||
|
## Printer Setup
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
1. Go to Settings → Devices → Printers & Scanners
|
||||||
|
2. Add your label printer
|
||||||
|
3. Run the app - printer will be auto-detected
|
||||||
|
4. Select printer from dropdown
|
||||||
|
|
||||||
|
### Alternative (No Printer)
|
||||||
|
- Select "PDF" option
|
||||||
|
- Labels will be saved to `pdf_backup/` folder
|
||||||
|
- Open and print from any PDF viewer
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "No Printers Found"
|
||||||
|
- This is normal - select "PDF" option
|
||||||
|
- You can print PDFs manually from the backup folder
|
||||||
|
- Or install your printer driver
|
||||||
|
|
||||||
|
### Windows Defender Warning
|
||||||
|
- Click "More info" → "Run anyway"
|
||||||
|
- This is safe - the app is open-source
|
||||||
|
|
||||||
|
### Missing Dependencies
|
||||||
|
```bash
|
||||||
|
pip install --upgrade pip
|
||||||
|
pip install -r requirements_windows.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Port Already in Use
|
||||||
|
If you get an error about ports, restart your computer or:
|
||||||
|
```bash
|
||||||
|
python -m pip uninstall -y pywin32
|
||||||
|
python -m pip install pywin32
|
||||||
|
```
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
Label-design/
|
||||||
|
├── label_printer_gui.py # Main GUI application
|
||||||
|
├── print_label.py # Print functionality
|
||||||
|
├── print_label_pdf.py # PDF generation
|
||||||
|
├── requirements_windows.txt # Windows dependencies
|
||||||
|
├── pdf_backup/ # Stored PDF labels
|
||||||
|
├── venv/ # Virtual environment
|
||||||
|
└── documentation/ # Documentation files
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tips
|
||||||
|
|
||||||
|
- **Character Limit**: Each field supports up to 25 characters (barcode limit)
|
||||||
|
- **Quantity Field**: Only numbers allowed
|
||||||
|
- **PDF Backup**: All labels automatically saved with timestamp
|
||||||
|
- **Cross-Platform**: Same code runs on Windows, Linux, and macOS
|
||||||
|
|
||||||
|
For more information, see the documentation folder.
|
||||||
5
documentation/requirements_windows.txt
Normal file
5
documentation/requirements_windows.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
python-barcode
|
||||||
|
pillow
|
||||||
|
reportlab
|
||||||
|
kivy
|
||||||
|
pywin32
|
||||||
@@ -18,7 +18,11 @@ from kivy.graphics import Color, Rectangle
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
from print_label import print_label_standalone
|
import platform
|
||||||
|
import time
|
||||||
|
import datetime
|
||||||
|
import glob
|
||||||
|
from print_label import print_label_standalone, get_available_printers
|
||||||
from kivy.clock import Clock
|
from kivy.clock import Clock
|
||||||
|
|
||||||
# Set window size - portrait/phone dimensions (375x667 like iPhone)
|
# Set window size - portrait/phone dimensions (375x667 like iPhone)
|
||||||
@@ -34,18 +38,175 @@ class LabelPrinterApp(App):
|
|||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.available_printers = self.get_available_printers()
|
# Build printer display names and mapping to full names
|
||||||
|
full_printers = get_available_printers()
|
||||||
|
self.printer_display_map = {} # display_name -> full_name
|
||||||
|
self.available_printers = []
|
||||||
|
for full_name in full_printers:
|
||||||
|
display_name = self._shorten_printer_name(full_name)
|
||||||
|
# Ensure unique display names
|
||||||
|
if display_name in self.printer_display_map:
|
||||||
|
display_name = full_name[:20]
|
||||||
|
self.printer_display_map[display_name] = full_name
|
||||||
|
self.available_printers.append(display_name)
|
||||||
|
# Clean old PDF backup files on startup
|
||||||
|
self.cleanup_old_pdfs()
|
||||||
|
# Clean old log files on startup
|
||||||
|
self.cleanup_old_logs()
|
||||||
|
|
||||||
def get_available_printers(self):
|
def _shorten_printer_name(self, name, max_len=20):
|
||||||
"""Get list of available printers from CUPS"""
|
"""Shorten printer name for display (max 20 chars).
|
||||||
|
For network printers like \\\\server\\printer, show just the printer part."""
|
||||||
|
if name.startswith('\\\\'):
|
||||||
|
# Network printer: \\server\printer -> extract printer name
|
||||||
|
parts = name.strip('\\').split('\\')
|
||||||
|
if len(parts) >= 2:
|
||||||
|
short = parts[-1] # Just the printer name
|
||||||
|
else:
|
||||||
|
short = name
|
||||||
|
else:
|
||||||
|
short = name
|
||||||
|
# Truncate to max_len
|
||||||
|
if len(short) > max_len:
|
||||||
|
short = short[:max_len]
|
||||||
|
return short
|
||||||
|
|
||||||
|
def _get_full_printer_name(self, display_name):
|
||||||
|
"""Resolve display name back to full printer name for printing."""
|
||||||
|
return self.printer_display_map.get(display_name, display_name)
|
||||||
|
|
||||||
|
def cleanup_old_pdfs(self, days=5):
|
||||||
|
"""
|
||||||
|
Delete PDF files older than specified days from pdf_backup folder.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
days (int): Delete files older than this many days (default: 5)
|
||||||
|
"""
|
||||||
|
pdf_backup_dir = 'pdf_backup'
|
||||||
|
|
||||||
|
# Create folder if it doesn't exist
|
||||||
|
if not os.path.exists(pdf_backup_dir):
|
||||||
|
os.makedirs(pdf_backup_dir, exist_ok=True)
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import cups
|
current_time = time.time()
|
||||||
conn = cups.Connection()
|
cutoff_time = current_time - (days * 24 * 3600) # Convert days to seconds
|
||||||
printers = conn.getPrinters()
|
|
||||||
return list(printers.keys()) if printers else ["PDF"]
|
for filename in os.listdir(pdf_backup_dir):
|
||||||
|
file_path = os.path.join(pdf_backup_dir, filename)
|
||||||
|
|
||||||
|
# Only process PDF files
|
||||||
|
if not filename.endswith('.pdf'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check if file is older than cutoff
|
||||||
|
if os.path.isfile(file_path):
|
||||||
|
file_mtime = os.path.getmtime(file_path)
|
||||||
|
if file_mtime < cutoff_time:
|
||||||
|
try:
|
||||||
|
os.remove(file_path)
|
||||||
|
print(f"Deleted old PDF: {filename}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to delete {filename}: {e}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error getting printers: {e}")
|
print(f"Error during PDF cleanup: {e}")
|
||||||
return ["PDF"]
|
|
||||||
|
def cleanup_old_logs(self, days=5):
|
||||||
|
"""
|
||||||
|
Delete log files older than specified days from logs folder.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
days (int): Delete files older than this many days (default: 5)
|
||||||
|
"""
|
||||||
|
logs_dir = 'logs'
|
||||||
|
|
||||||
|
# Create folder if it doesn't exist
|
||||||
|
if not os.path.exists(logs_dir):
|
||||||
|
os.makedirs(logs_dir, exist_ok=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
current_time = time.time()
|
||||||
|
cutoff_time = current_time - (days * 24 * 3600) # Convert days to seconds
|
||||||
|
|
||||||
|
for filename in os.listdir(logs_dir):
|
||||||
|
file_path = os.path.join(logs_dir, filename)
|
||||||
|
|
||||||
|
# Process both .log and .csv files
|
||||||
|
if not (filename.endswith('.log') or filename.endswith('.csv')):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check if file is older than cutoff
|
||||||
|
if os.path.isfile(file_path):
|
||||||
|
file_mtime = os.path.getmtime(file_path)
|
||||||
|
if file_mtime < cutoff_time:
|
||||||
|
try:
|
||||||
|
os.remove(file_path)
|
||||||
|
print(f"Deleted old log: {filename}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to delete {filename}: {e}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error during log cleanup: {e}")
|
||||||
|
|
||||||
|
def log_print_action(self, sap_nr, quantity, cable_id, printer, pdf_filename, success):
|
||||||
|
"""
|
||||||
|
Log the print action to a CSV log file (table format).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
sap_nr (str): SAP article number
|
||||||
|
quantity (str): Quantity value
|
||||||
|
cable_id (str): Cable ID
|
||||||
|
printer (str): Printer name
|
||||||
|
pdf_filename (str): Path to the generated PDF file
|
||||||
|
success (bool): Whether the print was successful
|
||||||
|
"""
|
||||||
|
logs_dir = 'logs'
|
||||||
|
|
||||||
|
# Create logs folder if it doesn't exist
|
||||||
|
os.makedirs(logs_dir, exist_ok=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Create log filename with date
|
||||||
|
log_date = datetime.datetime.now().strftime("%Y%m%d")
|
||||||
|
log_filename = os.path.join(logs_dir, f"print_log_{log_date}.csv")
|
||||||
|
|
||||||
|
# Create log entry values
|
||||||
|
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
status = "SUCCESS" if success else "FAILED"
|
||||||
|
|
||||||
|
# Escape CSV values (handle commas and quotes)
|
||||||
|
def escape_csv(value):
|
||||||
|
"""Escape CSV special characters"""
|
||||||
|
value = str(value)
|
||||||
|
if ',' in value or '"' in value or '\n' in value:
|
||||||
|
value = '"' + value.replace('"', '""') + '"'
|
||||||
|
return value
|
||||||
|
|
||||||
|
# Create CSV line
|
||||||
|
log_line = (
|
||||||
|
f"{timestamp},{status},"
|
||||||
|
f"{escape_csv(sap_nr)},"
|
||||||
|
f"{escape_csv(quantity)},"
|
||||||
|
f"{escape_csv(cable_id)},"
|
||||||
|
f"{escape_csv(printer)},"
|
||||||
|
f"{escape_csv(pdf_filename)}\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if file exists to add header on first entry
|
||||||
|
file_exists = os.path.isfile(log_filename)
|
||||||
|
|
||||||
|
# Write to log file
|
||||||
|
with open(log_filename, 'a', encoding='utf-8') as f:
|
||||||
|
# Add header if file is new
|
||||||
|
if not file_exists:
|
||||||
|
f.write("Timestamp,Status,SAP-Nr,Quantity,Cable ID,Printer,PDF File\n")
|
||||||
|
# Append log entry
|
||||||
|
f.write(log_line)
|
||||||
|
|
||||||
|
print(f"Log entry saved to: {log_filename}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error saving log entry: {e}")
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
"""Build the simplified single-column UI"""
|
"""Build the simplified single-column UI"""
|
||||||
@@ -144,7 +305,8 @@ class LabelPrinterApp(App):
|
|||||||
values=self.available_printers,
|
values=self.available_printers,
|
||||||
size_hint_y=None,
|
size_hint_y=None,
|
||||||
height=45,
|
height=45,
|
||||||
font_size='12sp'
|
font_size='12sp',
|
||||||
|
sync_height=True,
|
||||||
)
|
)
|
||||||
self.printer_spinner = printer_spinner
|
self.printer_spinner = printer_spinner
|
||||||
form_layout.add_widget(printer_spinner)
|
form_layout.add_widget(printer_spinner)
|
||||||
@@ -186,7 +348,8 @@ class LabelPrinterApp(App):
|
|||||||
sap_nr = self.sap_input.text.strip()
|
sap_nr = self.sap_input.text.strip()
|
||||||
quantity = self.qty_input.text.strip()
|
quantity = self.qty_input.text.strip()
|
||||||
cable_id = self.cable_id_input.text.strip()
|
cable_id = self.cable_id_input.text.strip()
|
||||||
printer = self.printer_spinner.text
|
# Resolve display name to full printer name
|
||||||
|
printer = self._get_full_printer_name(self.printer_spinner.text)
|
||||||
|
|
||||||
# Validate input
|
# Validate input
|
||||||
if not sap_nr and not quantity and not cable_id:
|
if not sap_nr and not quantity and not cable_id:
|
||||||
@@ -212,18 +375,37 @@ class LabelPrinterApp(App):
|
|||||||
|
|
||||||
# Print in background thread (using PDF by default)
|
# Print in background thread (using PDF by default)
|
||||||
def print_thread():
|
def print_thread():
|
||||||
|
pdf_filename = None
|
||||||
|
success = False
|
||||||
try:
|
try:
|
||||||
success = print_label_standalone(label_text, printer, preview=0, use_pdf=True)
|
success = print_label_standalone(label_text, printer, preview=0, use_pdf=True)
|
||||||
|
|
||||||
|
# Get the PDF filename that was created
|
||||||
|
# Files are saved to pdf_backup/ with timestamp
|
||||||
|
pdf_files = glob.glob('pdf_backup/final_label_*.pdf')
|
||||||
|
if pdf_files:
|
||||||
|
# Get the most recently created PDF file
|
||||||
|
pdf_filename = max(pdf_files, key=os.path.getctime)
|
||||||
|
|
||||||
if success:
|
if success:
|
||||||
|
# Log the successful print action
|
||||||
|
self.log_print_action(sap_nr, quantity, cable_id, printer, pdf_filename or "unknown", True)
|
||||||
|
|
||||||
# Use Clock.schedule_once to update UI from main thread
|
# Use Clock.schedule_once to update UI from main thread
|
||||||
Clock.schedule_once(lambda dt: popup.dismiss(), 0)
|
Clock.schedule_once(lambda dt: popup.dismiss(), 0)
|
||||||
Clock.schedule_once(lambda dt: self.show_popup("Success", "Label printed successfully!"), 0.1)
|
Clock.schedule_once(lambda dt: self.show_popup("Success", "Label printed successfully!", auto_dismiss=True), 0.1)
|
||||||
# Clear inputs after successful print
|
# Clear inputs after successful print (but keep printer selection)
|
||||||
Clock.schedule_once(lambda dt: self.clear_inputs(), 0.2)
|
Clock.schedule_once(lambda dt: self.clear_inputs(), 0.2)
|
||||||
else:
|
else:
|
||||||
|
# Log the failed print action
|
||||||
|
self.log_print_action(sap_nr, quantity, cable_id, printer, pdf_filename or "unknown", False)
|
||||||
|
|
||||||
Clock.schedule_once(lambda dt: popup.dismiss(), 0)
|
Clock.schedule_once(lambda dt: popup.dismiss(), 0)
|
||||||
Clock.schedule_once(lambda dt: self.show_popup("Error", "Failed to print label"), 0.1)
|
Clock.schedule_once(lambda dt: self.show_popup("Error", "Failed to print label"), 0.1)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
# Log the error
|
||||||
|
self.log_print_action(sap_nr, quantity, cable_id, printer, pdf_filename or "unknown", False)
|
||||||
|
|
||||||
Clock.schedule_once(lambda dt: popup.dismiss(), 0)
|
Clock.schedule_once(lambda dt: popup.dismiss(), 0)
|
||||||
Clock.schedule_once(lambda dt: self.show_popup("Error", f"Print error: {str(e)}"), 0.1)
|
Clock.schedule_once(lambda dt: self.show_popup("Error", f"Print error: {str(e)}"), 0.1)
|
||||||
|
|
||||||
@@ -232,13 +414,20 @@ class LabelPrinterApp(App):
|
|||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
def clear_inputs(self):
|
def clear_inputs(self):
|
||||||
"""Clear all input fields"""
|
"""Clear only the input fields, preserving printer selection"""
|
||||||
self.sap_input.text = ''
|
self.sap_input.text = ''
|
||||||
self.qty_input.text = ''
|
self.qty_input.text = ''
|
||||||
self.cable_id_input.text = ''
|
self.cable_id_input.text = ''
|
||||||
|
# Printer selection is NOT cleared - it persists until user changes it
|
||||||
|
|
||||||
def show_popup(self, title, message):
|
def show_popup(self, title, message, auto_dismiss=False):
|
||||||
"""Show a popup message"""
|
"""Show a popup message
|
||||||
|
|
||||||
|
Args:
|
||||||
|
title (str): Popup title
|
||||||
|
message (str): Popup message
|
||||||
|
auto_dismiss (bool): If True, popup will auto-dismiss after 3 seconds
|
||||||
|
"""
|
||||||
popup = Popup(
|
popup = Popup(
|
||||||
title=title,
|
title=title,
|
||||||
content=BoxLayout(
|
content=BoxLayout(
|
||||||
@@ -256,6 +445,10 @@ class LabelPrinterApp(App):
|
|||||||
popup.content.add_widget(close_button)
|
popup.content.add_widget(close_button)
|
||||||
|
|
||||||
popup.open()
|
popup.open()
|
||||||
|
|
||||||
|
# Auto-dismiss after 3 seconds if requested
|
||||||
|
if auto_dismiss:
|
||||||
|
Clock.schedule_once(lambda dt: popup.dismiss(), 3)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
7
logs/print_log_20260206.log
Normal file
7
logs/print_log_20260206.log
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[2026-02-06 08:30:44] SUCCESS
|
||||||
|
SAP-Nr: jgvkdjrdkh
|
||||||
|
Quantity: 300
|
||||||
|
Cable ID: jdflhfgvkjdzhee6465758382
|
||||||
|
Printer: PDF
|
||||||
|
PDF File: pdf_backup/final_label_20260206_083044.pdf
|
||||||
|
---
|
||||||
103
pdf_backup/final_label_20260206_083044.pdf
Normal file
103
pdf_backup/final_label_20260206_083044.pdf
Normal file
File diff suppressed because one or more lines are too long
309
print_label.py
309
print_label.py
@@ -1,31 +1,92 @@
|
|||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
import barcode
|
import barcode
|
||||||
from barcode.writer import ImageWriter
|
from barcode.writer import ImageWriter
|
||||||
import cups, time, os, datetime
|
import time
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import datetime
|
||||||
|
import platform
|
||||||
|
import subprocess
|
||||||
from print_label_pdf import PDFLabelGenerator
|
from print_label_pdf import PDFLabelGenerator
|
||||||
|
|
||||||
#functie de printare etichete pe un printer specificat cu un preview opțional
|
# Cross-platform printer support
|
||||||
# Aceasta funcție creează o imagine cu un cod de bare și text, apoi o trimite la imprimantă.
|
try:
|
||||||
# Dacă este specificat un preview, afișează o fereastră de previzualizare înainte de a imprima.
|
import cups
|
||||||
# Dimensiunea etichetei este de 9x5 cm la 300 DPI, cu un cadru exterior și două cadre interioare pentru codul de bare și text.
|
CUPS_AVAILABLE = True
|
||||||
# Codul de bare este generat folosind formatul Code128, iar textul este afișat sub codul de bare cu
|
except ImportError:
|
||||||
# o dimensiune de font maximizată pentru a se potrivi în cadrul textului
|
CUPS_AVAILABLE = False
|
||||||
# Imaginile sunt create folosind biblioteca PIL, iar imprimarea se face prin intermediul
|
|
||||||
# bibliotecii CUPS pentru gestionarea imprimantelor.
|
|
||||||
# Această funcție este utilă pentru a crea etichete personalizate cu coduri de bare și text, care pot fi utilizate în diverse aplicații, cum ar fi etichetarea produselor, inventariere sau organizarea documentelor
|
|
||||||
#mod de utilizare in cadrul unui program se copie fisierul print_label.py in directorul de lucru
|
|
||||||
# si se apeleaza functia print_label_standalone cu parametrii corespunzători:
|
|
||||||
# - value: textul de afișat pe etichetă
|
|
||||||
# - printer: numele imprimantei pe care se va face printarea
|
|
||||||
# - preview: 0 pentru a nu afișa previzualizarea, 1-3 pentru o previzualizare de 3 secunde, >3 pentru o previzualizare de 5 secunde
|
|
||||||
|
|
||||||
# se recomanda instalarea si setarea imprimantei in sistemul de operare
|
try:
|
||||||
# pentru a putea fi utilizata de catre biblioteca CUPS
|
import win32api
|
||||||
# se verifica proprietatile imprimantei in cups sa fie setata dimensiunea corecta a etichetei
|
import win32print
|
||||||
# pentru a instala biblioteca barcode se foloseste comanda pip install python-barcode
|
WIN32_AVAILABLE = True
|
||||||
# pentru a instala biblioteca PIL se foloseste comanda pip install pillow
|
except ImportError:
|
||||||
# pentru a instala biblioteca CUPS se foloseste comanda pip install pycups
|
WIN32_AVAILABLE = False
|
||||||
# pentru a instala biblioteca Tkinter se foloseste comanda sudo apt-get install python3-tk
|
|
||||||
|
SYSTEM = platform.system() # 'Linux', 'Windows', 'Darwin'
|
||||||
|
|
||||||
|
|
||||||
|
def get_available_printers():
|
||||||
|
"""
|
||||||
|
Get list of available printers (cross-platform).
|
||||||
|
Includes both local and network printers on Windows.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: List of available printer names, with "PDF" as fallback
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if SYSTEM == "Linux" and CUPS_AVAILABLE:
|
||||||
|
# Linux: Use CUPS
|
||||||
|
conn = cups.Connection()
|
||||||
|
printers = conn.getPrinters()
|
||||||
|
return list(printers.keys()) if printers else ["PDF"]
|
||||||
|
|
||||||
|
elif SYSTEM == "Windows":
|
||||||
|
# Windows: Get local + connected printers (includes print server connections)
|
||||||
|
try:
|
||||||
|
printers = []
|
||||||
|
|
||||||
|
# PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS captures:
|
||||||
|
# - Locally installed printers
|
||||||
|
# - Printers connected from a print server (e.g. \\server\printer)
|
||||||
|
try:
|
||||||
|
flags = win32print.PRINTER_ENUM_LOCAL | win32print.PRINTER_ENUM_CONNECTIONS
|
||||||
|
for printer_info in win32print.EnumPrinters(flags):
|
||||||
|
printer_name = printer_info[2]
|
||||||
|
if printer_name and printer_name not in printers:
|
||||||
|
printers.append(printer_name)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error enumerating printers: {e}")
|
||||||
|
|
||||||
|
# Add PDF as fallback option
|
||||||
|
if "PDF" not in printers:
|
||||||
|
printers.append("PDF")
|
||||||
|
|
||||||
|
return printers if printers else ["PDF"]
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error getting Windows printers: {e}")
|
||||||
|
return ["PDF"]
|
||||||
|
|
||||||
|
elif SYSTEM == "Darwin":
|
||||||
|
# macOS: Use lpstat command
|
||||||
|
try:
|
||||||
|
result = subprocess.run(["lpstat", "-p", "-d"],
|
||||||
|
capture_output=True, text=True)
|
||||||
|
printers = []
|
||||||
|
for line in result.stdout.split('\n'):
|
||||||
|
if line.startswith('printer'):
|
||||||
|
printer_name = line.split()[1]
|
||||||
|
printers.append(printer_name)
|
||||||
|
return printers if printers else ["PDF"]
|
||||||
|
except:
|
||||||
|
return ["PDF"]
|
||||||
|
|
||||||
|
else:
|
||||||
|
return ["PDF"]
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error getting printers: {e}")
|
||||||
|
return ["PDF"]
|
||||||
|
|
||||||
|
|
||||||
def create_label_image(text):
|
def create_label_image(text):
|
||||||
@@ -164,6 +225,194 @@ def create_label_pdf(text):
|
|||||||
return generator.create_label_pdf(sap_nr, cantitate, lot_number, pdf_filename)
|
return generator.create_label_pdf(sap_nr, cantitate, lot_number, pdf_filename)
|
||||||
|
|
||||||
|
|
||||||
|
def print_to_printer(printer_name, file_path):
|
||||||
|
"""
|
||||||
|
Print file to printer (cross-platform).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
printer_name (str): Name of printer or "PDF" for PDF output
|
||||||
|
file_path (str): Path to file to print
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if successful
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if printer_name == "PDF":
|
||||||
|
# PDF output - file is already saved
|
||||||
|
print(f"PDF output: {file_path}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif SYSTEM == "Linux" and CUPS_AVAILABLE:
|
||||||
|
# Linux: Use CUPS
|
||||||
|
conn = cups.Connection()
|
||||||
|
conn.printFile(printer_name, file_path, "Label Print", {})
|
||||||
|
print(f"Label sent to printer: {printer_name}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif SYSTEM == "Windows":
|
||||||
|
# Windows: Print PDF silently without any viewer opening
|
||||||
|
try:
|
||||||
|
if WIN32_AVAILABLE:
|
||||||
|
import win32print
|
||||||
|
import win32api
|
||||||
|
|
||||||
|
if file_path.endswith('.pdf'):
|
||||||
|
# Try silent printing methods (no viewer opens)
|
||||||
|
import os
|
||||||
|
import winreg
|
||||||
|
|
||||||
|
printed = False
|
||||||
|
|
||||||
|
# Method 1: SumatraPDF (bundled inside exe or external)
|
||||||
|
sumatra_paths = []
|
||||||
|
|
||||||
|
# Get the directory where this script/exe is running
|
||||||
|
if getattr(sys, 'frozen', False):
|
||||||
|
# Running as compiled executable
|
||||||
|
# PyInstaller extracts bundled files to sys._MEIPASS temp folder
|
||||||
|
if hasattr(sys, '_MEIPASS'):
|
||||||
|
# Check bundled version first (inside the exe)
|
||||||
|
bundled_sumatra = os.path.join(sys._MEIPASS, 'SumatraPDF.exe')
|
||||||
|
sumatra_paths.append(bundled_sumatra)
|
||||||
|
|
||||||
|
# Also check app directory for external version
|
||||||
|
app_dir = os.path.dirname(sys.executable)
|
||||||
|
sumatra_paths.append(os.path.join(app_dir, "SumatraPDF", "SumatraPDF.exe"))
|
||||||
|
sumatra_paths.append(os.path.join(app_dir, "SumatraPDF.exe"))
|
||||||
|
else:
|
||||||
|
# Running as script - check local folders
|
||||||
|
app_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
sumatra_paths.append(os.path.join(app_dir, "SumatraPDF", "SumatraPDF.exe"))
|
||||||
|
sumatra_paths.append(os.path.join(app_dir, "SumatraPDF.exe"))
|
||||||
|
|
||||||
|
# Then check system installations
|
||||||
|
sumatra_paths.extend([
|
||||||
|
r"C:\Program Files\SumatraPDF\SumatraPDF.exe",
|
||||||
|
r"C:\Program Files (x86)\SumatraPDF\SumatraPDF.exe",
|
||||||
|
])
|
||||||
|
|
||||||
|
for sumatra_path in sumatra_paths:
|
||||||
|
if os.path.exists(sumatra_path):
|
||||||
|
try:
|
||||||
|
subprocess.run([
|
||||||
|
sumatra_path,
|
||||||
|
"-print-to",
|
||||||
|
printer_name,
|
||||||
|
file_path,
|
||||||
|
"-print-settings",
|
||||||
|
"fit,landscape",
|
||||||
|
"-silent",
|
||||||
|
"-exit-when-done"
|
||||||
|
], check=False, creationflags=subprocess.CREATE_NO_WINDOW)
|
||||||
|
print(f"Label sent to printer via SumatraPDF: {printer_name}")
|
||||||
|
printed = True
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
print(f"SumatraPDF error: {e}")
|
||||||
|
|
||||||
|
# Method 2: Adobe Reader silent printing
|
||||||
|
if not printed:
|
||||||
|
adobe_path = None
|
||||||
|
for key_path in [
|
||||||
|
r"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AcroRd32.exe",
|
||||||
|
r"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\Acrobat.exe"
|
||||||
|
]:
|
||||||
|
try:
|
||||||
|
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key_path)
|
||||||
|
adobe_path, _ = winreg.QueryValueEx(key, "")
|
||||||
|
winreg.CloseKey(key)
|
||||||
|
break
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if adobe_path and os.path.exists(adobe_path):
|
||||||
|
try:
|
||||||
|
subprocess.run([
|
||||||
|
adobe_path,
|
||||||
|
"/t", # Print and close
|
||||||
|
file_path,
|
||||||
|
printer_name
|
||||||
|
], check=False, creationflags=subprocess.CREATE_NO_WINDOW)
|
||||||
|
print(f"Label sent to printer via Adobe Reader: {printer_name}")
|
||||||
|
printed = True
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Method 3: GhostScript (if installed)
|
||||||
|
if not printed:
|
||||||
|
gs_paths = [
|
||||||
|
r"C:\Program Files\gs\gs10.02.1\bin\gswin64c.exe",
|
||||||
|
r"C:\Program Files (x86)\gs\gs10.02.1\bin\gswin32c.exe",
|
||||||
|
]
|
||||||
|
# Try to find gswin in PATH
|
||||||
|
try:
|
||||||
|
gs_result = subprocess.run(['where', 'gswin64c'],
|
||||||
|
capture_output=True, text=True, check=False)
|
||||||
|
if gs_result.returncode == 0:
|
||||||
|
gs_paths.insert(0, gs_result.stdout.strip().split('\n')[0])
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
for gs_path in gs_paths:
|
||||||
|
if os.path.exists(gs_path):
|
||||||
|
try:
|
||||||
|
subprocess.run([
|
||||||
|
gs_path,
|
||||||
|
"-dNOPAUSE", "-dBATCH", "-dQUIET",
|
||||||
|
f"-sDEVICE=mswinpr2",
|
||||||
|
f"-sOutputFile=%printer%{printer_name}",
|
||||||
|
file_path
|
||||||
|
], check=False, creationflags=subprocess.CREATE_NO_WINDOW)
|
||||||
|
print(f"Label sent to printer via GhostScript: {printer_name}")
|
||||||
|
printed = True
|
||||||
|
break
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not printed:
|
||||||
|
# Fallback: Let user know and save PDF
|
||||||
|
print("=" * 60)
|
||||||
|
print("NOTICE: Silent PDF printing requires SumatraPDF")
|
||||||
|
print("SumatraPDF not found (should be bundled inside the app)")
|
||||||
|
print("If you built the app yourself, ensure SumatraPDF.exe is downloaded first.")
|
||||||
|
print("Run: setup_sumatra.ps1 before building")
|
||||||
|
print("=" * 60)
|
||||||
|
print(f"PDF saved to: {file_path}")
|
||||||
|
print("The PDF can be printed manually.")
|
||||||
|
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
# Non-PDF files
|
||||||
|
subprocess.run(['notepad', '/p', file_path],
|
||||||
|
check=False,
|
||||||
|
creationflags=subprocess.CREATE_NO_WINDOW)
|
||||||
|
print(f"Label sent to printer: {printer_name}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print("win32print not available, PDF saved as backup only")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Windows print error: {e}")
|
||||||
|
print("PDF backup saved as fallback")
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif SYSTEM == "Darwin":
|
||||||
|
# macOS: Use lp command
|
||||||
|
subprocess.run(["lp", "-d", printer_name, file_path], check=True)
|
||||||
|
print(f"Label sent to printer: {printer_name}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
else:
|
||||||
|
print(f"Unsupported system: {SYSTEM}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Printer error: {str(e)}")
|
||||||
|
print("Label already saved to file as fallback...")
|
||||||
|
print(f"Label file: {file_path}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def print_label_standalone(value, printer, preview=0, use_pdf=True):
|
def print_label_standalone(value, printer, preview=0, use_pdf=True):
|
||||||
"""
|
"""
|
||||||
Print a label with the specified text on the specified printer.
|
Print a label with the specified text on the specified printer.
|
||||||
@@ -226,23 +475,11 @@ def print_label_standalone(value, printer, preview=0, use_pdf=True):
|
|||||||
|
|
||||||
# Print after preview
|
# Print after preview
|
||||||
print("Sending to printer...")
|
print("Sending to printer...")
|
||||||
conn = cups.Connection()
|
return print_to_printer(printer, temp_file)
|
||||||
conn.printFile(printer, temp_file, "Label Print", {})
|
|
||||||
return True
|
|
||||||
else:
|
else:
|
||||||
print("Direct printing without preview...")
|
print("Direct printing without preview...")
|
||||||
# Direct printing without preview (preview = 0)
|
# Direct printing without preview (preview = 0)
|
||||||
try:
|
return print_to_printer(printer, temp_file)
|
||||||
conn = cups.Connection()
|
|
||||||
conn.printFile(printer, temp_file, "Label Print", {})
|
|
||||||
print(f"Label sent to printer: {printer}")
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
# If printing fails, save to file as fallback
|
|
||||||
print(f"Printer error: {str(e)}")
|
|
||||||
print("Label already saved to file as fallback...")
|
|
||||||
print(f"Label file: {temp_file}")
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error printing label: {str(e)}")
|
print(f"Error printing label: {str(e)}")
|
||||||
|
|||||||
7
requirements_windows.txt
Normal file
7
requirements_windows.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
python-barcode
|
||||||
|
pillow
|
||||||
|
kivy>=2.1.0
|
||||||
|
reportlab
|
||||||
|
pyinstaller>=6.0.0
|
||||||
|
pywin32
|
||||||
|
wmi
|
||||||
95
setup_sumatra.ps1
Normal file
95
setup_sumatra.ps1
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
# Download and Setup SumatraPDF Portable for Label Printer
|
||||||
|
# This script downloads SumatraPDF portable and sets up the deployment structure
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "========================================================"
|
||||||
|
Write-Host " Label Printer - SumatraPDF Setup"
|
||||||
|
Write-Host "========================================================"
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# Create SumatraPDF folder if it doesn't exist
|
||||||
|
$sumatraFolder = "SumatraPDF"
|
||||||
|
if (-not (Test-Path $sumatraFolder)) {
|
||||||
|
New-Item -ItemType Directory -Path $sumatraFolder -Force | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if SumatraPDF.exe already exists
|
||||||
|
$sumatraExe = Join-Path $sumatraFolder "SumatraPDF.exe"
|
||||||
|
if (Test-Path $sumatraExe) {
|
||||||
|
Write-Host "[OK] SumatraPDF.exe already exists at: $sumatraExe" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
Read-Host "Press Enter to exit"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "[1/3] Downloading SumatraPDF portable (64-bit, ~5 MB)..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# SumatraPDF download URL (latest stable version)
|
||||||
|
$url = "https://www.sumatrapdfreader.org/dl/rel/3.5.2/SumatraPDF-3.5.2-64.zip"
|
||||||
|
$zipFile = "SumatraPDF-temp.zip"
|
||||||
|
|
||||||
|
try {
|
||||||
|
# Download with progress
|
||||||
|
$progressPreference = 'SilentlyContinue'
|
||||||
|
Invoke-WebRequest -Uri $url -OutFile $zipFile -ErrorAction Stop
|
||||||
|
Write-Host "[OK] Download complete" -ForegroundColor Green
|
||||||
|
} catch {
|
||||||
|
Write-Host "[ERROR] Failed to download SumatraPDF" -ForegroundColor Red
|
||||||
|
Write-Host "Error: $_" -ForegroundColor Red
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Please download manually from:" -ForegroundColor Yellow
|
||||||
|
Write-Host " https://www.sumatrapdfreader.org/download-free-pdf-viewer" -ForegroundColor Yellow
|
||||||
|
Write-Host " Extract SumatraPDF.exe to the 'SumatraPDF' folder" -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
Read-Host "Press Enter to exit"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[2/3] Extracting..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
try {
|
||||||
|
# Extract ZIP file
|
||||||
|
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||||
|
[System.IO.Compression.ZipFile]::ExtractToDirectory($zipFile, $sumatraFolder)
|
||||||
|
Write-Host "[OK] Extraction complete" -ForegroundColor Green
|
||||||
|
} catch {
|
||||||
|
Write-Host "[ERROR] Failed to extract ZIP file" -ForegroundColor Red
|
||||||
|
Write-Host "Error: $_" -ForegroundColor Red
|
||||||
|
Read-Host "Press Enter to exit"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[3/3] Cleaning up..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# Remove temporary ZIP file
|
||||||
|
if (Test-Path $zipFile) {
|
||||||
|
Remove-Item $zipFile -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "[OK] Cleanup complete" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# Verify installation
|
||||||
|
if (Test-Path $sumatraExe) {
|
||||||
|
Write-Host "========================================================"
|
||||||
|
Write-Host " SETUP SUCCESSFUL!" -ForegroundColor Green
|
||||||
|
Write-Host "========================================================"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "SumatraPDF portable is now installed at:" -ForegroundColor Green
|
||||||
|
Write-Host " $sumatraExe" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "The Label Printer app will now be able to print PDFs silently." -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
} else {
|
||||||
|
Write-Host "========================================================"
|
||||||
|
Write-Host " SETUP INCOMPLETE" -ForegroundColor Yellow
|
||||||
|
Write-Host "========================================================"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Could not find SumatraPDF.exe after extraction." -ForegroundColor Yellow
|
||||||
|
Write-Host "Please check the SumatraPDF folder and ensure SumatraPDF.exe is present." -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
}
|
||||||
|
|
||||||
|
Read-Host "Press Enter to exit"
|
||||||
Reference in New Issue
Block a user