Source code for jdxi_editor.main

"""
Main entry point for the JD-Xi Editor application.

This module sets up the application environment, including logging, MIDI message listening,
and application window initialization. It also manages the application's lifecycle,
from initialization to event loop execution.

Functions:
    midi_callback(msg): Callback function for handling incoming MIDI messages.

    listen_midi(port_name, callback): Listens for MIDI messages on the specified port
    and triggers the provided callback function.

    setup_logging(): Configures logging for the application, including console and file
     logging with rotation.

    main(): Main entry point to initialize and run the JD-Xi Editor application,
    set up the window, and handle MIDI message listening.

"""

import cProfile
import io
import logging
import os
import platform
import pstats
import sys
from pathlib import Path

from PySide6.QtCore import QSettings
from PySide6.QtGui import QColor, QIcon, QPixmap, Qt
from PySide6.QtWidgets import (
    QApplication,
    QFrame,
    QHBoxLayout,
    QLabel,
    QProgressBar,
    QSplashScreen,
    QVBoxLayout,
)

from decologr import setup_logging
from jdxi_editor.jdxi.style import JDXiStyle
from jdxi_editor.log.message import log_message
from jdxi_editor.project import __organization_name__, __program__, __version__
from jdxi_editor.resources import resource_path
from jdxi_editor.ui.widgets.display.digital import DigitalTitle
from jdxi_editor.ui.windows.jdxi.dimensions import JDXiDimensions
from jdxi_editor.ui.windows.jdxi.instrument import JDXiInstrument

os.environ["QT_LOGGING_RULES"] = "qt.qpa.fonts=false"


[docs] def main() -> None: """Main entry point for the JD-Xi Editor application.""" try: # Set up logging first settings = QSettings(__organization_name__, __program__) log_level = int(str(settings.value("log_level", logging.DEBUG))) logger = setup_logging(use_rich=True, project_name="jdxi_editor") # Create application app = QApplication(sys.argv) # Set application metadata app.setApplicationName(__program__) app.setApplicationVersion(__version__) app.setOrganizationName("mabsoft") app.setOrganizationDomain("com.mabsoft") app.setStyleSheet( """ QLabel { color: red; font-weight: bold; } """ ) logger.debug("Application initialized") # Set application icon icon_locations = [ Path(__file__).parent / "resources" / "jdxi_icon.png", # Package location Path(__file__).parent.parent / "resources" / "jdxi_icon.png", # Development location ] icon_found = False for icon_path in icon_locations: if icon_path.exists(): app.setWindowIcon(QIcon(str(icon_path))) log_message(f"Loaded icon from {icon_path}") icon_found = True break if not icon_found: logging.warning( f"Icon not found in any of: {[str(p) for p in icon_locations]}" ) # Create address fallback icon only for windows, not macOS if platform.system() == "Windows": icon = QIcon() pixmap = QPixmap(128, 128) pixmap.fill(QColor("#2897B7")) # Use the app's theme color icon.addPixmap(pixmap) app.setWindowIcon(icon) log_message("Using fallback icon") splash, progress_bar, status_label = setup_splash_screen(app) # Update splash screen with initial progress status_label.setText("Initializing MIDI subsystem…") progress_bar.setValue(10) app.processEvents() # Create window and pass splash screen components window = JDXiInstrument( splash=splash, progress_bar=progress_bar, status_label=status_label ) # Finalize splash screen splash.finish(window) window.show() # Start event loop return app.exec() except Exception as ex: logging.exception(f"Fatal error {ex} occurred") raise
[docs] def setup_splash_screen( app: QApplication, ) -> tuple[QSplashScreen, QProgressBar, QLabel]: """Setup and display a professional application splash screen with rotating status text. Returns: tuple: (splash_screen, progress_bar, status_label) for updating progress """ splash = QSplashScreen() # Need to use the screen center to display the splash screen # In Qt 6, QApplication.desktop() was removed, use QScreen instead screen = app.primaryScreen() if screen: screen_geometry = screen.availableGeometry() screen_center = screen_geometry.center() splash.move( int(screen_center.x() - JDXiDimensions.SPLASH_WIDTH / 2), int(screen_center.y() - JDXiDimensions.SPLASH_HEIGHT / 2), ) splash.setWindowFlags( Qt.SplashScreen | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint ) splash.setFixedSize(JDXiDimensions.SPLASH_WIDTH, JDXiDimensions.SPLASH_HEIGHT) splash.setStyleSheet( """ QSplashScreen { background-color: #111111; border: 1px solid #2c2c2c; } QLabel#TitleLabel { color: #f3f3f3; font-size: 28px; font-weight: 600; letter-spacing: 1px; } QLabel#SubtitleLabel { color: #bbbbbb; font-size: 14px; } QLabel#StatusLabel { color: #88ccff; font-size: 12px; font-style: italic; } QLabel#CreditLabel { color: #888888; font-size: 12px; } QFrame#Card { background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #1c1c1c, stop:1 #0f0f0f); border-radius: 14px; border: 1px solid #2a2a2a; } QProgressBar { background: #2a2a2a; border-radius: 6px; height: 14px; text-align: center; color: #cccccc; font-size: 10px; } QProgressBar::chunk { border-radius: 6px; background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #7ac1ff, stop:1 #3b8fe8); } """ ) root = QVBoxLayout(splash) root.setContentsMargins(28, 28, 28, 28) root.setSpacing(16) # --- Card container --- card = QFrame() card.setObjectName("Card") card_layout = QVBoxLayout(card) card_layout.setContentsMargins(32, 32, 32, 32) card_layout.setSpacing(18) # Title title = DigitalTitle( __program__, digital_font_family=JDXiStyle.FONT_FAMILY_MONOSPACE, show_upper_text=False, ) card_layout.addWidget(title) title.setStyleSheet(JDXiStyle.INSTRUMENT_TITLE_LABEL) # Image image_path = resource_path(os.path.join("resources", "jdxi_cartoon_600.png")) pixmap = QPixmap(image_path).scaled( 360, 220, Qt.KeepAspectRatio, Qt.SmoothTransformation ) logo = QLabel() logo.setPixmap(pixmap) logo.setAlignment(Qt.AlignCenter) card_layout.addWidget(logo) # Subtitle subtitle = QLabel("An editor & toolkit for the Roland JD-Xi instrument") card_layout.addWidget(subtitle) subtitle.setStyleSheet(JDXiStyle.INSTRUMENT_SUBTITLE_LABEL) # Progress bar progress_bar = QProgressBar() progress_bar.setRange(0, 100) progress_bar.setValue(0) progress_bar.setFixedWidth(420) progress_bar.setStyleSheet(JDXiStyle.PROGRESS_BAR) progress_row = QHBoxLayout() progress_row.addStretch() progress_row.addWidget(progress_bar) progress_row.addStretch() card_layout.addLayout(progress_row) # Rotating status label status_label = DigitalTitle( "Starting...", digital_font_family=JDXiStyle.FONT_FAMILY_MONOSPACE ) status_label.setStyleSheet(JDXiStyle.INSTRUMENT_SUBTITLE_LABEL) card_layout.addWidget(status_label) # Footer credits credits = QLabel( f"{__program__} • Version {__version__}\n" "Developed by mabsoft.com — Inspired by modern instrument editors" ) credits.setObjectName("CreditLabel") credits.setAlignment(Qt.AlignCenter) root.addWidget(card) root.addWidget(credits) splash.show() splash.raise_() splash.activateWindow() # Return splash screen components for updating progress return splash, progress_bar, status_label
if __name__ == "__main__": try:
[docs] profiling = False
if profiling: profiler = cProfile.Profile() profiler.enable() exit_code = main() if profiling: profiler.disable() s = io.StringIO() sortby = "cumtime" # or 'tottime' ps = pstats.Stats(profiler, stream=s).sort_stats(sortby) ps.print_stats(50) # Top 50 entries print(s.getvalue()) sys.exit(exit_code) except Exception as ex: print(f"Application crashed: {str(ex)}") # Fallback if logging fails logging.exception("Application crashed") sys.exit(1)