feat: Add installation scripts for Windows and Unix-based systems
- Created `install_and_run.bat` for Windows installation and setup. - Created `install_and_run.sh` for Unix-based systems installation and setup. - Removed `main.py` as it is no longer needed. - Updated `requirements.txt` to specify package versions and added PyQt5. - Deleted `start.bat` as it is redundant. - Added unit tests for core functionality and scraping modes. - Implemented input validation utilities in `utils/validators.py`. - Added support for dual scraping modes in the scraper.
This commit is contained in:
221
gui_main.py
Normal file
221
gui_main.py
Normal file
@@ -0,0 +1,221 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
EBoek.info Scraper GUI Application
|
||||
|
||||
Main entry point for the PyQt5 GUI version of the EBoek.info scraper.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import traceback
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
|
||||
# Suppress urllib3 OpenSSL warnings on macOS
|
||||
warnings.filterwarnings("ignore", message="urllib3 v2 only supports OpenSSL 1.1.1+")
|
||||
warnings.filterwarnings("ignore", category=UserWarning, module="urllib3")
|
||||
|
||||
# Ensure we can import PyQt5
|
||||
try:
|
||||
from PyQt5.QtWidgets import QApplication, QMessageBox
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QIcon
|
||||
except ImportError as e:
|
||||
print("Error: PyQt5 is not installed.")
|
||||
print("Please run: pip install PyQt5")
|
||||
print(f"Import error: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
# Add the project root to Python path
|
||||
project_root = Path(__file__).parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
# Import our GUI components
|
||||
try:
|
||||
from gui.main_window import MainWindow
|
||||
from core.credentials import CredentialManager
|
||||
except ImportError as e:
|
||||
print(f"Error importing application modules: {e}")
|
||||
print("Please ensure all required files are present.")
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class EBoekScraperApp(QApplication):
|
||||
"""
|
||||
Main application class for the EBoek.info Scraper GUI.
|
||||
"""
|
||||
|
||||
def __init__(self, argv):
|
||||
super().__init__(argv)
|
||||
|
||||
self.setApplicationName("EBoek.info Scraper")
|
||||
self.setApplicationVersion("2.0")
|
||||
self.setOrganizationName("EBoek Scraper")
|
||||
|
||||
# Set application icon if available
|
||||
self.set_application_icon()
|
||||
|
||||
# Handle exceptions
|
||||
sys.excepthook = self.handle_exception
|
||||
|
||||
self.main_window = None
|
||||
|
||||
def set_application_icon(self):
|
||||
"""Set the application icon if available."""
|
||||
# You can add an icon file here if desired
|
||||
# icon_path = project_root / "resources" / "icon.png"
|
||||
# if icon_path.exists():
|
||||
# self.setWindowIcon(QIcon(str(icon_path)))
|
||||
pass
|
||||
|
||||
def handle_exception(self, exc_type, exc_value, exc_traceback):
|
||||
"""Handle uncaught exceptions."""
|
||||
if issubclass(exc_type, KeyboardInterrupt):
|
||||
# Allow Ctrl+C to exit gracefully
|
||||
sys.__excepthook__(exc_type, exc_value, exc_traceback)
|
||||
return
|
||||
|
||||
# Log the exception
|
||||
error_msg = "".join(traceback.format_exception(exc_type, exc_value, exc_traceback))
|
||||
print("Uncaught exception:")
|
||||
print(error_msg)
|
||||
|
||||
# Show error dialog to user
|
||||
if self.main_window:
|
||||
try:
|
||||
QMessageBox.critical(
|
||||
self.main_window,
|
||||
"Unexpected Error",
|
||||
f"An unexpected error occurred:\n\n{str(exc_value)}\n\n"
|
||||
f"The application may need to be restarted.\n\n"
|
||||
f"Error type: {exc_type.__name__}"
|
||||
)
|
||||
except:
|
||||
# If we can't show the dialog, just print
|
||||
print("Could not display error dialog")
|
||||
|
||||
def initialize(self):
|
||||
"""Initialize the application."""
|
||||
try:
|
||||
# Check system requirements
|
||||
self.check_requirements()
|
||||
|
||||
# Create and show main window
|
||||
self.main_window = MainWindow()
|
||||
self.main_window.show()
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error initializing application: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
# Try to show error dialog
|
||||
try:
|
||||
msg = QMessageBox()
|
||||
msg.setIcon(QMessageBox.Critical)
|
||||
msg.setWindowTitle("Initialization Error")
|
||||
msg.setText(f"Could not initialize the application:\n\n{str(e)}")
|
||||
msg.setDetailedText(traceback.format_exc())
|
||||
msg.exec_()
|
||||
except:
|
||||
print("Could not display initialization error dialog")
|
||||
|
||||
return False
|
||||
|
||||
def check_requirements(self):
|
||||
"""Check system requirements and dependencies."""
|
||||
errors = []
|
||||
|
||||
# Check Python version
|
||||
if sys.version_info < (3, 6):
|
||||
errors.append(f"Python 3.6+ required (found {sys.version_info.major}.{sys.version_info.minor})")
|
||||
|
||||
# Check required modules
|
||||
required_modules = ['selenium', 'urllib3']
|
||||
missing_modules = []
|
||||
|
||||
for module in required_modules:
|
||||
try:
|
||||
__import__(module)
|
||||
except ImportError:
|
||||
missing_modules.append(module)
|
||||
|
||||
if missing_modules:
|
||||
errors.append(f"Missing required modules: {', '.join(missing_modules)}")
|
||||
|
||||
# Check Chrome/Chromium availability (basic check)
|
||||
chrome_paths = [
|
||||
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", # macOS
|
||||
"/usr/bin/google-chrome", # Linux
|
||||
"/usr/bin/chromium-browser", # Linux (Chromium)
|
||||
"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe", # Windows
|
||||
"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe", # Windows 32-bit
|
||||
]
|
||||
|
||||
chrome_found = any(Path(path).exists() for path in chrome_paths)
|
||||
if not chrome_found:
|
||||
# This is a warning, not an error
|
||||
print("Warning: Chrome browser not detected in standard locations.")
|
||||
print("Make sure Google Chrome is installed for the scraper to work.")
|
||||
|
||||
if errors:
|
||||
error_text = "System requirement errors:\n\n" + "\n".join(f"• {error}" for error in errors)
|
||||
error_text += "\n\nPlease install missing requirements and try again."
|
||||
raise RuntimeError(error_text)
|
||||
|
||||
|
||||
def show_startup_error(title, message):
|
||||
"""Show a startup error dialog without a main window."""
|
||||
app = QApplication.instance()
|
||||
if not app:
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
msg = QMessageBox()
|
||||
msg.setIcon(QMessageBox.Critical)
|
||||
msg.setWindowTitle(title)
|
||||
msg.setText(message)
|
||||
msg.exec_()
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point for the GUI application."""
|
||||
# Set up high DPI support
|
||||
if hasattr(Qt, 'AA_EnableHighDpiScaling'):
|
||||
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
|
||||
|
||||
if hasattr(Qt, 'AA_UseHighDpiPixmaps'):
|
||||
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
|
||||
|
||||
# Create the application
|
||||
try:
|
||||
app = EBoekScraperApp(sys.argv)
|
||||
|
||||
# Initialize and run
|
||||
if app.initialize():
|
||||
# Run the application
|
||||
sys.exit(app.exec_())
|
||||
else:
|
||||
print("Application initialization failed")
|
||||
sys.exit(1)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Fatal error starting application: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
# Try to show error dialog
|
||||
try:
|
||||
show_startup_error(
|
||||
"Startup Error",
|
||||
f"Could not start the EBoek.info Scraper:\n\n{str(e)}\n\n"
|
||||
f"Please check the installation and try again."
|
||||
)
|
||||
except:
|
||||
print("Could not display startup error dialog")
|
||||
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user