Skip to content

Commit

Permalink
Refactor code to conform to PEP 8 style guide
Browse files Browse the repository at this point in the history
  • Loading branch information
Odizinne committed Jul 27, 2024
1 parent ed5ebe7 commit 47b9a8c
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 130 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
build/
__pycache__/
.venv/
.venv/
.idea/
268 changes: 143 additions & 125 deletions src/BigPictureTV.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,142 @@
import pygetwindow as gw
from enum import Enum
import darkdetect
from PyQt6.QtWidgets import QApplication, QSystemTrayIcon, QMenu, QMainWindow, QDialog
from PyQt6.QtWidgets import QApplication, QSystemTrayIcon, QMenu, QMainWindow
from PyQt6.QtGui import QIcon, QAction
from PyQt6.QtCore import QTimer, QSharedMemory
from design import Ui_MainWindow
from steam_language_reader import get_big_picture_window_title

SETTINGS_FILE = os.path.join(os.environ['APPDATA'], "BigPictureTV", "settings.json")
ICONS_FOLDER = 'icons' if getattr(sys, 'frozen', False) else os.path.join(os.path.dirname(__file__), 'icons')
MULTIMONITORTOOL_PATH = 'dependencies/multimonitortool-x64/MultiMonitorTool.exe' if getattr(sys, 'frozen', False) else os.path.join(os.path.dirname(__file__), 'dependencies/multimonitortool-x64/MultiMonitorTool.exe')
MULTIMONITORTOOL_PATH = 'dependencies/multimonitortool-x64/MultiMonitorTool.exe'


class Mode(Enum):
DESKTOP = 1
GAMEMODE = 2


def generate_monitor_list():
output_file = os.path.join(os.environ['APPDATA'], "BigPictureTV", "monitors.txt")
os.makedirs(os.path.dirname(output_file), exist_ok=True)

try:
subprocess.run([MULTIMONITORTOOL_PATH, "/stext", output_file], check=True)
except subprocess.CalledProcessError as e:
print(f"An error occurred while generating the monitor list: {e}")


def parse_monitors():
monitor_file = os.path.join(os.environ['APPDATA'], "BigPictureTV", "monitors.txt")
monitors = []

with open(monitor_file, 'r', encoding='utf-16') as f:
content = f.read()

entries = content.split('==================================================')

for entry in entries:
match_name = re.search(r'Monitor Name\s*:\s*(.+)', entry)
match_id = re.search(r'Name\s*:\s*(.+)', entry)
if match_name and match_id:
monitor_name = match_name.group(1).strip()
monitor_id = match_id.group(1).strip()
monitors.append((monitor_name, monitor_id))

return monitors


def get_mode_file_path():
app_data_folder = os.path.join(os.environ['APPDATA'], 'BigPictureTV')
if not os.path.exists(app_data_folder):
os.makedirs(app_data_folder)
return os.path.join(app_data_folder, 'current_mode.txt')


def read_current_mode():
return Mode.GAMEMODE if os.path.exists(get_mode_file_path()) else Mode.DESKTOP


def get_audio_devices():
cmd = "powershell Get-AudioDevice -list"
output = subprocess.check_output(cmd, shell=True, text=True)
devices = re.findall(r'Index\s+:\s+(\d+)\s+.*?Name\s+:\s+(.*?)\s+ID\s+:\s+{(.*?)}', output, re.DOTALL)
return devices


def set_audio_device(device_name, devices):
device_words = device_name.lower().split()
for index, name, _ in devices:
if all(word in name.lower() for word in device_words):
cmd = f"powershell set-audiodevice -index {index}"
result = subprocess.run(cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
return result.returncode == 0
return False


def switch_audio(audio_output):
devices = get_audio_devices()
success = set_audio_device(audio_output, devices)
retries = 0
while not success and retries < 10:
print("Failed to switch audio, retrying...")
time.sleep(1)
success = set_audio_device(audio_output, devices)
retries += 1
if not success:
print("Failed to switch audio after 10 attempts.")


def read_stream_status():
file_path = os.path.join(os.environ['APPDATA'], "sunshine-status", "status.txt")
return os.path.exists(file_path)


def is_bigpicture_running():
big_picture_title = get_big_picture_window_title().lower()
big_picture_words = big_picture_title.split()
current_window_titles = [title.lower() for title in gw.getAllTitles()]

for window_title in current_window_titles:
if all(word in window_title for word in big_picture_words):
return True

return False


def write_current_mode(current_mode):
file_path = get_mode_file_path()
if current_mode == Mode.GAMEMODE:
open(file_path, 'w').close()
elif current_mode == Mode.DESKTOP and os.path.exists(file_path):
os.remove(file_path)


def manage_startup_shortcut(state):
target_path = os.path.join(os.getcwd(), 'BigPictureTV.exe')
startup_folder = winshell.startup()
shortcut_path = os.path.join(startup_folder, 'BigPictureTV.lnk')
if state:
winshell.CreateShortcut(
Path=shortcut_path,
Target=target_path,
Icon=(target_path, 0),
Description="Launch BigPictureTV",
StartIn=os.path.dirname(target_path)
)
elif os.path.exists(shortcut_path):
os.remove(shortcut_path)


def check_startup_shortcut():
return os.path.exists(os.path.join(winshell.startup(), 'BigPictureTV.lnk'))


def handle_startup_checkbox_state_changed(state):
manage_startup_shortcut(state)


class BigPictureTV(QMainWindow):
def __init__(self):
super().__init__()
Expand All @@ -39,12 +161,12 @@ def __init__(self):
self.timer = QTimer()
self.gamemode_screen = "/external"
self.desktop_screen = "/internal"
self.generate_monitor_list()
generate_monitor_list()
self.populate_monitor_comboboxes()
self.load_settings()
self.initialize_ui()
self.is_audio_device_cmdlets_installed()
self.current_mode = self.read_current_mode()
self.current_mode = read_current_mode()
self.switch_mode(self.current_mode or Mode.DESKTOP)
self.tray_icon = self.create_tray_icon()
self.timer.timeout.connect(self.update_mode)
Expand All @@ -54,48 +176,20 @@ def __init__(self):
self.first_run = False

def initialize_ui(self):
self.ui.disableAudioCheckbox.stateChanged.connect(self.on_disableAudioCheckbox_stateChanged)
self.ui.startupCheckBox.stateChanged.connect(self.on_startupCheckBox_stateChanged)
self.ui.disableAudioCheckbox.stateChanged.connect(self.handle_disableaudio_checkbox_state_changed)
self.ui.startupCheckBox.stateChanged.connect(handle_startup_checkbox_state_changed)
self.ui.gamemodeEntry.textChanged.connect(self.save_settings)
self.ui.desktopEntry.textChanged.connect(self.save_settings)
self.ui.checkRateSpinBox.valueChanged.connect(self.save_settings)
self.ui.startupCheckBox.setChecked(self.check_startup_shortcut())
self.ui.startupCheckBox.setChecked(check_startup_shortcut())
self.ui.desktopVideoBox.currentIndexChanged.connect(self.save_settings)
self.ui.gamemodeVideoBox.currentIndexChanged.connect(self.save_settings)
self.ui.displayswitchBox.stateChanged.connect(self.on_displayswitchBox_stateChanged)
self.ui.displayswitchBox.stateChanged.connect(self.handle_displayswitch_box_state_hanged)

self.apply_settings()

def generate_monitor_list(self):
output_file = os.path.join(os.environ['APPDATA'], "BigPictureTV", "monitors.txt")
os.makedirs(os.path.dirname(output_file), exist_ok=True)

try:
subprocess.run([MULTIMONITORTOOL_PATH, "/stext", output_file], check=True)
except subprocess.CalledProcessError as e:
print(f"An error occurred while generating the monitor list: {e}")

def parse_monitors(self):
monitor_file = os.path.join(os.environ['APPDATA'], "BigPictureTV", "monitors.txt")
monitors = []

with open(monitor_file, 'r', encoding='utf-16') as f:
content = f.read()

entries = content.split('==================================================')

for entry in entries:
match_name = re.search(r'Monitor Name\s*:\s*(.+)', entry)
match_id = re.search(r'Name\s*:\s*(.+)', entry)
if match_name and match_id:
monitor_name = match_name.group(1).strip()
monitor_id = match_id.group(1).strip()
monitors.append((monitor_name, monitor_id))

return monitors

def populate_monitor_comboboxes(self):
monitors = self.parse_monitors()
monitors = parse_monitors()

self.ui.gamemodeVideoBox.clear()
self.ui.desktopVideoBox.clear()
Expand All @@ -104,18 +198,15 @@ def populate_monitor_comboboxes(self):
self.ui.gamemodeVideoBox.addItem(monitor_name, monitor_id)
self.ui.desktopVideoBox.addItem(monitor_name, monitor_id)

def on_displayswitchBox_stateChanged(self):
def handle_displayswitch_box_state_hanged(self):
self.use_displayswitch = self.ui.displayswitchBox.isChecked()
self.toggle_video_settings(not self.use_displayswitch)
self.save_settings()

def on_disableAudioCheckbox_stateChanged(self, state):
def handle_disableaudio_checkbox_state_changed(self, state):
self.toggle_audio_settings(not state)
self.save_settings()

def on_startupCheckBox_stateChanged(self, state):
self.manage_startup_shortcut(state)

def toggle_audio_settings(self, enabled):
self.ui.desktopEntry.setEnabled(enabled)
self.ui.desktopLabel.setEnabled(enabled)
Expand Down Expand Up @@ -187,36 +278,29 @@ def save_settings(self):
with open(SETTINGS_FILE, 'w') as f:
json.dump(self.settings, f, indent=4)


def read_current_mode(self):
return Mode.GAMEMODE if os.path.exists(self.get_mode_file_path()) else Mode.DESKTOP

def get_mode_file_path(self):
app_data_folder = os.path.join(os.environ['APPDATA'], 'BigPictureTV')
if not os.path.exists(app_data_folder):
os.makedirs(app_data_folder)
return os.path.join(app_data_folder, 'current_mode.txt')

def switch_mode(self, mode):
if mode == self.current_mode:
return
self.current_mode = mode
self.switch_screen(self.gamemode_screen if mode == Mode.GAMEMODE else self.desktop_screen)
if not self.ui.disableAudioCheckbox.isChecked():
self.switch_audio(self.settings.get('GAMEMODE_AUDIO') if mode == Mode.GAMEMODE else self.settings.get('DESKTOP_AUDIO'))
self.write_current_mode(mode)
switch_audio(
self.settings.get('GAMEMODE_AUDIO') if mode == Mode.GAMEMODE else self.settings.get('DESKTOP_AUDIO'))
write_current_mode(mode)
if self.tray_icon:
self.update_tray_icon_menu()
self.update_tray_icon()

def switch_screen(self, mode):
if self.use_displayswitch:
print("Using displayswitch.exe to switch screens")
mode == "/external" if Mode.GAMEMODE else "/internal"
if mode == self.gamemode_screen:
mode = "/external"
else:
mode = "/internal"
subprocess.run(["displayswitch.exe", mode], check=True)
return

selected_monitor_id = None
if mode == self.gamemode_screen:
selected_monitor_id = self.ui.gamemodeVideoBox.currentData()
else:
Expand All @@ -225,7 +309,7 @@ def switch_screen(self, mode):
enable_command = [MULTIMONITORTOOL_PATH, "/enable", selected_monitor_id]
subprocess.run(enable_command, check=True)

monitors = self.parse_monitors()
monitors = parse_monitors()
for _, monitor_id in monitors:
if monitor_id != selected_monitor_id:
disable_command = [MULTIMONITORTOOL_PATH, "/disable", monitor_id]
Expand All @@ -241,41 +325,10 @@ def is_audio_device_cmdlets_installed(self):
self.toggle_audio_settings(False)
return False

def get_audio_devices(self):
cmd = "powershell Get-AudioDevice -list"
output = subprocess.check_output(cmd, shell=True, text=True)
devices = re.findall(r'Index\s+:\s+(\d+)\s+.*?Name\s+:\s+(.*?)\s+ID\s+:\s+{(.*?)}', output, re.DOTALL)
return devices

def set_audio_device(self, device_name, devices):
device_words = device_name.lower().split()
for index, name, _ in devices:
if all(word in name.lower() for word in device_words):
cmd = f"powershell set-audiodevice -index {index}"
result = subprocess.run(cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
return result.returncode == 0
return False

def switch_audio(self, audio_output):
devices = self.get_audio_devices()
success = self.set_audio_device(audio_output, devices)
retries = 0
while not success and retries < 10:
print("Failed to switch audio, retrying...")
time.sleep(1)
success = self.set_audio_device(audio_output, devices)
retries += 1
if not success:
print("Failed to switch audio after 10 attempts.")

def read_stream_status(self):
file_path = os.path.join(os.environ['APPDATA'], "sunshine-status", "status.txt")
return os.path.exists(file_path)

def update_mode(self):
if self.is_bigpicture_running() and self.current_mode != Mode.GAMEMODE and not self.read_stream_status():
if is_bigpicture_running() and self.current_mode != Mode.GAMEMODE and not read_stream_status():
self.switch_mode(Mode.GAMEMODE)
elif not self.is_bigpicture_running() and self.current_mode != Mode.DESKTOP:
elif not is_bigpicture_running() and self.current_mode != Mode.DESKTOP:
self.switch_mode(Mode.DESKTOP)

def update_mode_timer_interval(self, check_rate):
Expand Down Expand Up @@ -321,24 +374,6 @@ def update_tray_icon(self):
else:
self.tray_icon.setIcon(QIcon(os.path.join(ICONS_FOLDER, f'icon_desktop_{theme}.png')))

def is_bigpicture_running(self):
big_picture_title = get_big_picture_window_title().lower()
big_picture_words = big_picture_title.split()
current_window_titles = [title.lower() for title in gw.getAllTitles()]

for window_title in current_window_titles:
if all(word in window_title for word in big_picture_words):
return True

return False

def write_current_mode(self, current_mode):
file_path = self.get_mode_file_path()
if current_mode == Mode.GAMEMODE:
open(file_path, 'w').close()
elif current_mode == Mode.DESKTOP and os.path.exists(file_path):
os.remove(file_path)

def toggle_detection(self):
self.paused = not self.paused
if self.paused:
Expand All @@ -353,23 +388,6 @@ def closeEvent(self, event):
event.ignore()
self.hide()

def manage_startup_shortcut(self, state):
target_path = os.path.join(os.getcwd(), 'BigPictureTV.exe')
startup_folder = winshell.startup()
shortcut_path = os.path.join(startup_folder, 'BigPictureTV.lnk')
if state:
winshell.CreateShortcut(
Path=shortcut_path,
Target=target_path,
Icon=(target_path, 0),
Description="Launch BigPictureTV",
StartIn=os.path.dirname(target_path)
)
elif os.path.exists(shortcut_path):
os.remove(shortcut_path)

def check_startup_shortcut(self):
return os.path.exists(os.path.join(winshell.startup(), 'BigPictureTV.lnk'))

if __name__ == '__main__':
shared_memory = QSharedMemory('BigPictureTVSharedMemory')
Expand Down
Loading

0 comments on commit 47b9a8c

Please sign in to comment.