# main.py import sys import json import os from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QWidget, QVBoxLayout, QLabel, QSpinBox, QLineEdit, QGridLayout from PyQt5.QtCore import Qt from PyQt5.QtGui import QImage, QPixmap, qRgb from dmx import DMXRecorder, DMXPlayer class MainWindow(QMainWindow): def __init__(self): super().__init__() self.settings = self.load_settings() self.initUI() self.isRecording = False self.isPlaying = False self.recorder = None self.player = None def initUI(self): self.setWindowTitle("DMX Recorder and Player") self.resize(300, 500) # Use dark theme self.setStyleSheet(''' QWidget { background-color: #222222; color: #FFFFFF; } QSlider::groove:horizontal { border: 1px solid #444444; height: 8px; background: #555555; } QSlider::handle:horizontal { background: #888888; border: 1px solid #444444; width: 18px; margin: -8px 0; } QLineEdit { background-color: #333333; border: 1px solid #555555; color: #FFFFFF; padding: 4px; } QPushButton { background-color: #444444; border: 1px solid #666666; padding: 6px; } QPushButton:hover { background-color: #555555; } QLabel { color: #FFFFFF; } ''') self.central_widget = QWidget() self.setCentralWidget(self.central_widget) self.layout = QVBoxLayout() self.central_widget.setLayout(self.layout) self.record_button = QPushButton("Record") self.record_button.clicked.connect(self.toggle_recording) self.play_button = QPushButton("Play") self.play_button.clicked.connect(self.toggle_playback) # Input fields for settings self.universes_label = QLabel("Universes to Playback:") self.universes_spinbox = QSpinBox() self.universes_spinbox.setMinimum(1) self.universes_spinbox.setMaximum(4) # Maximum universes recorded self.universes_spinbox.setValue(self.settings.get('universes', 4)) # Port number self.port_label = QLabel("Port Number:") self.port_input = QLineEdit(str(self.settings.get('port', 6454))) # Transmit Address (Playback) self.transmit_label = QLabel("Transmit Address:") self.transmit_input = QLineEdit(self.settings.get('transmit_address', '255.255.255.255')) # Listen Address (Recording) self.listen_label = QLabel("Listen Address:") self.listen_input = QLineEdit(self.settings.get('listen_address', '0.0.0.0')) self.video_label = QLabel() self.video_label.setAlignment(Qt.AlignCenter) self.layout.addWidget(self.video_label) # Add settings input fields to the layout settings_layout = QGridLayout() settings_layout.addWidget(self.universes_label, 0, 0) settings_layout.addWidget(self.universes_spinbox, 0, 1) settings_layout.addWidget(self.port_label, 1, 0) settings_layout.addWidget(self.port_input, 1, 1) settings_layout.addWidget(self.transmit_label, 2, 0) settings_layout.addWidget(self.transmit_input, 2, 1) settings_layout.addWidget(self.listen_label, 3, 0) settings_layout.addWidget(self.listen_input, 3, 1) self.layout.addLayout(settings_layout) self.layout.addWidget(self.record_button) self.layout.addWidget(self.play_button) def toggle_recording(self): if not self.isRecording: if self.isPlaying: # Can't record while playing return # Get settings port = int(self.port_input.text()) listen_address = self.listen_input.text() # Start recording self.recorder = DMXRecorder(listen_address=listen_address, port=port) self.recorder.frame_ready.connect(self.update_frame) self.recorder.start_recording() self.record_button.setText("Stop") self.isRecording = True else: # Stop recording if self.recorder: self.recorder.frame_ready.disconnect(self.update_frame) self.recorder.stop_recording() self.recorder = None self.record_button.setText("Record") self.isRecording = False def toggle_playback(self): if not self.isPlaying: if self.isRecording: # Can't play while recording return # Get settings num_universes_to_playback = self.universes_spinbox.value() port = int(self.port_input.text()) transmit_address = self.transmit_input.text() # Start playback self.player = DMXPlayer(num_universes=num_universes_to_playback, transmit_address=transmit_address, port=port) self.player.frame_ready.connect(self.update_frame) self.player.playback_finished.connect(self.on_playback_finished) self.player.start_playback() self.play_button.setText("Stop") self.isPlaying = True else: # Stop playback if self.player: self.player.stop_playback() self.player = None self.play_button.setText("Play") self.isPlaying = False def on_playback_finished(self): if self.isPlaying: self.play_button.setText("Play") self.isPlaying = False if self.player: self.player.frame_ready.disconnect(self.update_frame) self.player = None def update_frame(self, frame_array): # Convert the numpy array to QImage and display height, width = frame_array.shape # Convert the numpy array to QImage q_img = QImage(frame_array.data, width, height, width, QImage.Format_Indexed8) # Create a grayscale color table color_table = [qRgb(i, i, i) for i in range(256)] q_img.setColorTable(color_table) # Scale image to fit the label pixmap = QPixmap.fromImage(q_img).scaled(self.video_label.width(), self.video_label.height(), Qt.KeepAspectRatio) # Set the pixmap to the label self.video_label.setPixmap(pixmap) def closeEvent(self, event): # Save settings to JSON file self.save_settings() super().closeEvent(event) def load_settings(self): # Load settings from settings.json if it exists settings_file = 'settings.json' if os.path.exists(settings_file): with open(settings_file, 'r') as f: return json.load(f) else: # Return default settings return {} def save_settings(self): settings = { 'universes': self.universes_spinbox.value(), 'port': int(self.port_input.text()), 'transmit_address': self.transmit_input.text(), 'listen_address': self.listen_input.text(), } with open('settings.json', 'w') as f: json.dump(settings, f) if __name__ == '__main__': app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())