Add steamvr settings handling and downloading/deletion of ge proton
This commit is contained in:
parent
d54253d5f6
commit
a0b2db7bde
2 changed files with 292 additions and 40 deletions
329
main.py
329
main.py
|
@ -1,20 +1,58 @@
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import tarfile
|
||||||
|
import tempfile
|
||||||
import traceback
|
import traceback
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
import dearpygui.dearpygui as dpg
|
import dearpygui.dearpygui as dpg
|
||||||
|
import requests
|
||||||
|
|
||||||
|
DPG_TAG_GE_PROTON_AVAILABLE = '__ge_proton_available'
|
||||||
|
DPG_TAG_GE_PROTON_INSTALLED = '__ge_proton_installed'
|
||||||
|
DPG_TAG_LOADING_POPUP = '__loading_popup'
|
||||||
|
DPG_TAG_LOADING_POPUP_TITLE = '__loading_popup_title'
|
||||||
|
DPG_TAG_LOADING_POPUP_MESSAGE = '__loading_popup_message'
|
||||||
|
DPG_TAG_ALERT_POPUP = '__alert_popup'
|
||||||
|
DPG_TAG_ALERT_POPUP_TITLE = '__alert_popup_title'
|
||||||
|
DPG_TAG_ALERT_POPUP_MESSAGE = '__alert_popup_message'
|
||||||
|
DPG_TAG_ALERT_POPUP_CONFIRM = '__alert_popup_confirm'
|
||||||
|
DPG_TAG_ALERT_POPUP_DISMISS = '__alert_popup_dismiss'
|
||||||
|
DPG_TAG_CURRENT_PAGE = '__current_page'
|
||||||
|
|
||||||
|
GPU_POWER_PROFILE_MODE_FILE = '/sys/class/drm/card0/device/pp_power_profile_mode'
|
||||||
|
GPU_PERFORMANCE_LEVEL_FILE = '/sys/class/drm/card0/device/power_dpm_force_performance_level'
|
||||||
|
|
||||||
|
STEAM_VR_SETTINGS_FILE = '{base_dir}/config/steamvr.vrsettings'
|
||||||
|
|
||||||
|
GE_PROTON_GITHUB = 'GloriousEggroll/proton-ge-custom'
|
||||||
|
GE_PROTON_INSTALL_DIR = '{base_dir}/compatibilitytools.d'
|
||||||
|
|
||||||
gpu_options = []
|
gpu_options = []
|
||||||
steam_vr_options = []
|
steam_vr_options = []
|
||||||
ge_proton_options = []
|
|
||||||
|
page_links = {
|
||||||
|
'first': '',
|
||||||
|
'prev': '',
|
||||||
|
'next': '',
|
||||||
|
'last': ''
|
||||||
|
}
|
||||||
|
max_page = '0'
|
||||||
|
curr_page = '0'
|
||||||
|
|
||||||
|
|
||||||
# region general stuff
|
# region general stuff
|
||||||
|
|
||||||
def _log(sender, app_data, user_data):
|
def _loaded(option, value=None):
|
||||||
print(f'sender: {sender}, \t app_data: {app_data}, \t user_data: {user_data}')
|
print(f'Loaded {option}' + (f': {value}' if value is not None else ''))
|
||||||
|
|
||||||
|
|
||||||
|
def _store(option, value):
|
||||||
|
print(f'Store {option}: {value}')
|
||||||
|
|
||||||
|
|
||||||
def write_to_file(file, value):
|
def write_to_file(file, value):
|
||||||
|
@ -38,6 +76,28 @@ def update_options(options):
|
||||||
for (item, value_getter) in options:
|
for (item, value_getter) in options:
|
||||||
dpg.set_value(item, value_getter())
|
dpg.set_value(item, value_getter())
|
||||||
|
|
||||||
|
|
||||||
|
def _loading_popup(show=True, title='', message=''):
|
||||||
|
dpg.set_value(DPG_TAG_LOADING_POPUP_TITLE, title)
|
||||||
|
dpg.set_value(DPG_TAG_LOADING_POPUP_MESSAGE, message)
|
||||||
|
dpg.configure_item(DPG_TAG_LOADING_POPUP, show=show)
|
||||||
|
|
||||||
|
|
||||||
|
def _alert_popup(
|
||||||
|
show=True,
|
||||||
|
title='',
|
||||||
|
message='',
|
||||||
|
confirm='OK',
|
||||||
|
dismiss='Cancel',
|
||||||
|
on_confirm: Callable = None,
|
||||||
|
on_dismiss: Callable = None
|
||||||
|
):
|
||||||
|
dpg.set_value(DPG_TAG_ALERT_POPUP_TITLE, title)
|
||||||
|
dpg.set_value(DPG_TAG_ALERT_POPUP_MESSAGE, message)
|
||||||
|
dpg.configure_item(DPG_TAG_ALERT_POPUP_CONFIRM, label=confirm, callback=on_confirm, show=on_confirm is not None)
|
||||||
|
dpg.configure_item(DPG_TAG_ALERT_POPUP_DISMISS, label=dismiss, callback=on_dismiss, show=on_dismiss is not None)
|
||||||
|
dpg.configure_item(DPG_TAG_ALERT_POPUP, show=show)
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,7 +112,6 @@ def get_steam_dir():
|
||||||
for path in possible_paths:
|
for path in possible_paths:
|
||||||
candidate = (Path.home() / Path(path)).resolve()
|
candidate = (Path.home() / Path(path)).resolve()
|
||||||
if candidate.exists():
|
if candidate.exists():
|
||||||
print(str(candidate))
|
|
||||||
return str(candidate)
|
return str(candidate)
|
||||||
return 'No Steam installation found!'
|
return 'No Steam installation found!'
|
||||||
|
|
||||||
|
@ -63,7 +122,7 @@ def get_steam_dir():
|
||||||
|
|
||||||
def _raw_gpu_profiles():
|
def _raw_gpu_profiles():
|
||||||
rex = re.compile('^ *(\\d) +(\\w+)([ *]):$', flags=re.MULTILINE)
|
rex = re.compile('^ *(\\d) +(\\w+)([ *]):$', flags=re.MULTILINE)
|
||||||
with open('/sys/class/drm/card0/device/pp_power_profile_mode', 'r') as f:
|
with open(GPU_POWER_PROFILE_MODE_FILE, 'r') as f:
|
||||||
matches = re.findall(rex, f.read())
|
matches = re.findall(rex, f.read())
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
|
@ -74,15 +133,15 @@ def get_available_gpu_profiles():
|
||||||
|
|
||||||
def get_selected_gpu_profile():
|
def get_selected_gpu_profile():
|
||||||
active_profile = next(filter(lambda profile: profile[2] == '*', _raw_gpu_profiles()), None)
|
active_profile = next(filter(lambda profile: profile[2] == '*', _raw_gpu_profiles()), None)
|
||||||
|
_loaded('GPU Profile', active_profile)
|
||||||
return active_profile[1] if active_profile is not None else None
|
return active_profile[1] if active_profile is not None else None
|
||||||
|
|
||||||
|
|
||||||
def select_gpu_profile(sender, app_data, user_data):
|
def select_gpu_profile(_sender, app_data, _user_data):
|
||||||
_log(sender, app_data, user_data)
|
|
||||||
selected_profile = next(filter(lambda profile: profile[1] == app_data, _raw_gpu_profiles()), None)
|
selected_profile = next(filter(lambda profile: profile[1] == app_data, _raw_gpu_profiles()), None)
|
||||||
print(f'selecting power profile {selected_profile}')
|
_store('GPU Profile', selected_profile)
|
||||||
if selected_profile is not None:
|
if selected_profile is not None:
|
||||||
write_to_file('/sys/class/drm/card0/device/pp_power_profile_mode', selected_profile[0])
|
write_to_file(GPU_POWER_PROFILE_MODE_FILE, selected_profile[0])
|
||||||
update_options(gpu_options)
|
update_options(gpu_options)
|
||||||
|
|
||||||
|
|
||||||
|
@ -91,16 +150,16 @@ def get_available_gpu_levels():
|
||||||
|
|
||||||
|
|
||||||
def get_selected_gpu_level():
|
def get_selected_gpu_level():
|
||||||
with open('/sys/class/drm/card0/device/power_dpm_force_performance_level', 'r') as f:
|
with open(GPU_PERFORMANCE_LEVEL_FILE, 'r') as f:
|
||||||
level = f.readline()
|
level = f.readline().strip()
|
||||||
|
_loaded('GPU Level', level)
|
||||||
return level
|
return level
|
||||||
|
|
||||||
|
|
||||||
def select_gpu_level(sender, level, user_data):
|
def select_gpu_level(_sender, level, _user_data):
|
||||||
_log(sender, level, user_data)
|
_store('GPU Level', level)
|
||||||
print(f'selecting power profile {level}')
|
|
||||||
if level is not None:
|
if level is not None:
|
||||||
write_to_file('/sys/class/drm/card0/device/power_dpm_force_performance_level', level)
|
write_to_file(GPU_PERFORMANCE_LEVEL_FILE, level)
|
||||||
update_options(gpu_options)
|
update_options(gpu_options)
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
@ -109,31 +168,145 @@ def select_gpu_level(sender, level, user_data):
|
||||||
# region steam vr
|
# region steam vr
|
||||||
|
|
||||||
def is_vulkan_async_enabled():
|
def is_vulkan_async_enabled():
|
||||||
return True
|
with open(STEAM_VR_SETTINGS_FILE.format(base_dir=get_steam_dir()), 'r') as f:
|
||||||
|
data_content = json.load(f)
|
||||||
|
enabled = data_content['steamvr']['enableLinuxVulkanAsync']
|
||||||
|
_loaded('SteamVR enableLinuxVulkanAsync', enabled)
|
||||||
|
return enabled
|
||||||
|
|
||||||
|
|
||||||
def enable_vulkan_async(sender, enabled, user_data):
|
def enable_vulkan_async(_sender, enabled, _user_data):
|
||||||
_log(sender, enabled, user_data)
|
_store('SteamVR enableLinuxVulkanAsync', enabled)
|
||||||
|
file_path = STEAM_VR_SETTINGS_FILE.format(base_dir=get_steam_dir())
|
||||||
|
with open(file_path, 'r') as f:
|
||||||
|
data_content = json.load(f)
|
||||||
|
data_content['steamvr']['enableLinuxVulkanAsync'] = enabled
|
||||||
|
with open(file_path, 'w') as f:
|
||||||
|
json.dump(data_content, f, indent=3, separators=(',', ' : '))
|
||||||
|
update_options(steam_vr_options)
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
|
|
||||||
# region ge proton
|
# region ge proton
|
||||||
|
|
||||||
|
def update_installed_ge_proton():
|
||||||
|
dpg.configure_item(DPG_TAG_GE_PROTON_INSTALLED, items=get_installed_ge_protons())
|
||||||
|
|
||||||
|
|
||||||
def get_installed_ge_protons():
|
def get_installed_ge_protons():
|
||||||
return tuple(sorted(['7-55', '8-2', '8-9', '9-7'], reverse=True))
|
path = GE_PROTON_INSTALL_DIR.format(base_dir=get_steam_dir())
|
||||||
|
installed = os.listdir(path)
|
||||||
|
sorted_versions = tuple(sorted(installed, reverse=True))
|
||||||
|
_loaded('GE Proton installed', sorted_versions)
|
||||||
|
return sorted_versions
|
||||||
|
|
||||||
|
|
||||||
def get_available_ge_protons():
|
def delete_installed_ge_proton():
|
||||||
return tuple(sorted(['6-42', '7-55', '7-99', '7-6', '7-5', '8-2', '8-6', '8-9', '9-7'], reverse=True))
|
version = dpg.get_value(DPG_TAG_GE_PROTON_INSTALLED)
|
||||||
|
install_path = f'{GE_PROTON_INSTALL_DIR.format(base_dir=get_steam_dir())}/{version}'
|
||||||
|
_alert_popup(
|
||||||
|
title=f'Delete {version}?',
|
||||||
|
message=f'Will remove\n{install_path}',
|
||||||
|
confirm='Delete',
|
||||||
|
on_confirm=lambda: _confirmed_delete_installed_ge_proton(install_path),
|
||||||
|
on_dismiss=lambda: _alert_popup(show=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def install_ge_proton(sender, app_data, user_data):
|
def _confirmed_delete_installed_ge_proton(version):
|
||||||
_log(sender, app_data, user_data)
|
_alert_popup(show=False)
|
||||||
|
_loading_popup(title='Deleting...', message=version)
|
||||||
|
print(f'Deleting {version}')
|
||||||
|
shutil.rmtree(version)
|
||||||
|
update_installed_ge_proton()
|
||||||
|
print('Done deleting')
|
||||||
|
_loading_popup(show=False)
|
||||||
|
|
||||||
|
|
||||||
def delete_ge_proton(sender, app_data, user_data):
|
def update_available_ge_protons():
|
||||||
_log(sender, app_data, user_data)
|
global curr_page
|
||||||
|
global max_page
|
||||||
|
curr_page = 1
|
||||||
|
max_page = 1
|
||||||
|
dpg.configure_item(DPG_TAG_GE_PROTON_AVAILABLE, items=get_available_ge_protons())
|
||||||
|
|
||||||
|
|
||||||
|
def _update_page_links(links):
|
||||||
|
global curr_page
|
||||||
|
global max_page
|
||||||
|
matches = re.findall('<(\\S+)>; rel=\\"(\\w+)\\"', links)
|
||||||
|
for link in page_links:
|
||||||
|
page_links[link] = ''
|
||||||
|
for match in matches:
|
||||||
|
page_links[match[1]] = match[0]
|
||||||
|
if page_links['last'] != '':
|
||||||
|
max_page = page_links['last'].split('=')[1]
|
||||||
|
dpg.set_value(DPG_TAG_CURRENT_PAGE, f'{curr_page}/{max_page}')
|
||||||
|
|
||||||
|
|
||||||
|
def switch_page(sender, app_data, user_data):
|
||||||
|
global curr_page
|
||||||
|
print(f'Switching page {user_data}')
|
||||||
|
link = page_links[user_data]
|
||||||
|
if link != '':
|
||||||
|
curr_page = link.split('=')[1]
|
||||||
|
dpg.configure_item(DPG_TAG_GE_PROTON_AVAILABLE, items=get_available_ge_protons(link))
|
||||||
|
|
||||||
|
|
||||||
|
def get_available_ge_protons(link=None):
|
||||||
|
url = link or f'https://api.github.com/repos/{GE_PROTON_GITHUB}/releases'
|
||||||
|
response = requests.get(url)
|
||||||
|
_update_page_links(response.headers['Link'])
|
||||||
|
releases = response.json()
|
||||||
|
release_names = list(map(lambda release: release['tag_name'], releases))
|
||||||
|
sorted_releases = tuple(sorted(release_names, reverse=True))
|
||||||
|
_loaded('GE Proton releases', sorted_releases)
|
||||||
|
return sorted_releases
|
||||||
|
|
||||||
|
|
||||||
|
def install_ge_proton():
|
||||||
|
version = dpg.get_value(DPG_TAG_GE_PROTON_AVAILABLE)
|
||||||
|
url = f'https://github.com/GloriousEggroll/proton-ge-custom/releases/download/{version}/{version}.tar.gz'
|
||||||
|
tar_file_path = f'{tempfile.gettempdir()}/{version}.tar.gz'
|
||||||
|
target_extract_path = GE_PROTON_INSTALL_DIR.format(base_dir=get_steam_dir())
|
||||||
|
|
||||||
|
if version is None or version == '':
|
||||||
|
_alert_popup(
|
||||||
|
title=f'No version selected!',
|
||||||
|
message='Reload the release list and select a version to install.',
|
||||||
|
on_confirm=lambda: _alert_popup(show=False)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if version in get_installed_ge_protons():
|
||||||
|
_alert_popup(
|
||||||
|
title=f'Version {version} already installed!',
|
||||||
|
message='To download again, delete it first',
|
||||||
|
on_confirm=lambda: _alert_popup(show=False)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f'Loading {url} to {tar_file_path}')
|
||||||
|
title = f'Installing {version}'
|
||||||
|
_loading_popup(title=title, message='Downloading...')
|
||||||
|
response = requests.get(url, stream=True)
|
||||||
|
if response.status_code == 200:
|
||||||
|
with open(tar_file_path, 'wb') as temp_file:
|
||||||
|
temp_file.write(response.raw.read())
|
||||||
|
print('Download complete, start extracting...')
|
||||||
|
_loading_popup(title=title, message='Extracting...')
|
||||||
|
with tarfile.open(tar_file_path) as tar_file:
|
||||||
|
tar_file.extractall(target_extract_path)
|
||||||
|
print('Done installing')
|
||||||
|
else:
|
||||||
|
print(response.status_code)
|
||||||
|
print(response.text)
|
||||||
|
if Path(tar_file_path).exists():
|
||||||
|
os.remove(tar_file_path)
|
||||||
|
_loading_popup(show=False)
|
||||||
|
update_installed_ge_proton()
|
||||||
|
dpg.set_value(DPG_TAG_GE_PROTON_INSTALLED, version)
|
||||||
|
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
|
@ -144,14 +317,55 @@ def show_window():
|
||||||
dpg.create_context()
|
dpg.create_context()
|
||||||
dpg.create_viewport(title='VRUtil', width=650, height=500)
|
dpg.create_viewport(title='VRUtil', width=650, height=500)
|
||||||
|
|
||||||
|
with dpg.window(
|
||||||
|
tag=DPG_TAG_LOADING_POPUP,
|
||||||
|
min_size=(200, 10),
|
||||||
|
pos=(225, 175),
|
||||||
|
modal=True,
|
||||||
|
show=False,
|
||||||
|
no_title_bar=True,
|
||||||
|
no_resize=True,
|
||||||
|
no_move=True
|
||||||
|
):
|
||||||
|
with dpg.group(horizontal=True):
|
||||||
|
dpg.add_loading_indicator()
|
||||||
|
with dpg.group():
|
||||||
|
dpg.add_text('title', tag=DPG_TAG_LOADING_POPUP_TITLE)
|
||||||
|
dpg.add_text('message', tag=DPG_TAG_LOADING_POPUP_MESSAGE)
|
||||||
|
|
||||||
|
with dpg.window(
|
||||||
|
tag=DPG_TAG_ALERT_POPUP,
|
||||||
|
min_size=(200, 30),
|
||||||
|
pos=(225, 175),
|
||||||
|
modal=True,
|
||||||
|
show=False,
|
||||||
|
no_title_bar=True,
|
||||||
|
no_resize=True,
|
||||||
|
no_move=True
|
||||||
|
):
|
||||||
|
with dpg.group(horizontal=True):
|
||||||
|
dpg.add_loading_indicator(
|
||||||
|
circle_count=3,
|
||||||
|
color=(240, 140, 0, 255),
|
||||||
|
secondary_color=(180, 100, 0, 255)
|
||||||
|
)
|
||||||
|
with dpg.group():
|
||||||
|
dpg.add_text('title', tag=DPG_TAG_ALERT_POPUP_TITLE)
|
||||||
|
dpg.add_text('message', tag=DPG_TAG_ALERT_POPUP_MESSAGE)
|
||||||
|
dpg.add_spacer(height=10)
|
||||||
|
with dpg.group(horizontal=True):
|
||||||
|
dpg.add_button(tag=DPG_TAG_ALERT_POPUP_DISMISS, label='Cancel')
|
||||||
|
dpg.add_button(tag=DPG_TAG_ALERT_POPUP_CONFIRM, label='OK')
|
||||||
|
|
||||||
with dpg.window(tag='__main_window'):
|
with dpg.window(tag='__main_window'):
|
||||||
|
|
||||||
dpg.add_text('Current Steam dir:')
|
dpg.add_text('Current Steam dir:')
|
||||||
dpg.add_text(get_steam_dir())
|
dpg.add_text(get_steam_dir())
|
||||||
|
|
||||||
|
dpg.add_spacer(height=10)
|
||||||
|
|
||||||
# region GPU
|
# region GPU
|
||||||
|
|
||||||
dpg.add_spacer(height=10)
|
|
||||||
with dpg.collapsing_header(label='GPU', default_open=True):
|
with dpg.collapsing_header(label='GPU', default_open=True):
|
||||||
|
|
||||||
gpu_options.append((
|
gpu_options.append((
|
||||||
|
@ -175,11 +389,12 @@ def show_window():
|
||||||
get_selected_gpu_level
|
get_selected_gpu_level
|
||||||
))
|
))
|
||||||
|
|
||||||
|
dpg.add_spacer(height=10)
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
# region STEAM VR
|
# region STEAM VR
|
||||||
|
|
||||||
dpg.add_spacer(height=10)
|
|
||||||
with dpg.collapsing_header(label='Steam VR', default_open=True):
|
with dpg.collapsing_header(label='Steam VR', default_open=True):
|
||||||
|
|
||||||
steam_vr_options.append((
|
steam_vr_options.append((
|
||||||
|
@ -191,11 +406,12 @@ def show_window():
|
||||||
is_vulkan_async_enabled
|
is_vulkan_async_enabled
|
||||||
))
|
))
|
||||||
|
|
||||||
|
dpg.add_spacer(height=10)
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
# region GE PROTON
|
# region GE PROTON
|
||||||
|
|
||||||
dpg.add_spacer(height=10)
|
|
||||||
with dpg.collapsing_header(label='GE Proton', default_open=True):
|
with dpg.collapsing_header(label='GE Proton', default_open=True):
|
||||||
|
|
||||||
with dpg.group(horizontal=True):
|
with dpg.group(horizontal=True):
|
||||||
|
@ -204,11 +420,13 @@ def show_window():
|
||||||
|
|
||||||
with dpg.group(horizontal=False):
|
with dpg.group(horizontal=False):
|
||||||
dpg.add_text('Installed versions:')
|
dpg.add_text('Installed versions:')
|
||||||
dpg.add_button(label='Delete', callback=_log)
|
dpg.add_button(label='Delete', callback=delete_installed_ge_proton)
|
||||||
ge_proton_options.append((
|
dpg.add_listbox(
|
||||||
dpg.add_listbox(items=get_installed_ge_protons(), width=250, num_items=8, callback=_log),
|
tag=DPG_TAG_GE_PROTON_INSTALLED,
|
||||||
get_installed_ge_protons
|
items=get_installed_ge_protons(),
|
||||||
))
|
width=250,
|
||||||
|
num_items=8
|
||||||
|
)
|
||||||
|
|
||||||
# AVAILABLE
|
# AVAILABLE
|
||||||
|
|
||||||
|
@ -216,13 +434,46 @@ def show_window():
|
||||||
dpg.add_text('Available versions:')
|
dpg.add_text('Available versions:')
|
||||||
|
|
||||||
with dpg.group(horizontal=True):
|
with dpg.group(horizontal=True):
|
||||||
dpg.add_button(label='Reload', callback=_log)
|
dpg.add_button(label='Install', callback=install_ge_proton)
|
||||||
dpg.add_button(label='Install', callback=_log)
|
dpg.add_button(label='Reload', callback=update_available_ge_protons)
|
||||||
|
dpg.add_button(
|
||||||
|
label='First',
|
||||||
|
arrow=True,
|
||||||
|
direction=dpg.mvDir_Left,
|
||||||
|
user_data='first',
|
||||||
|
callback=switch_page
|
||||||
|
)
|
||||||
|
dpg.add_button(
|
||||||
|
label='Prev',
|
||||||
|
arrow=True,
|
||||||
|
direction=dpg.mvDir_Left,
|
||||||
|
user_data='prev',
|
||||||
|
callback=switch_page
|
||||||
|
)
|
||||||
|
dpg.add_text('0/0', tag=DPG_TAG_CURRENT_PAGE)
|
||||||
|
dpg.add_button(
|
||||||
|
label='Next',
|
||||||
|
arrow=True,
|
||||||
|
direction=dpg.mvDir_Right,
|
||||||
|
user_data='next',
|
||||||
|
callback=switch_page
|
||||||
|
)
|
||||||
|
dpg.add_button(
|
||||||
|
label='Last',
|
||||||
|
arrow=True,
|
||||||
|
direction=dpg.mvDir_Right,
|
||||||
|
user_data='last',
|
||||||
|
callback=switch_page
|
||||||
|
)
|
||||||
|
|
||||||
ge_proton_options.append((
|
dpg.add_listbox(
|
||||||
dpg.add_listbox(items=get_available_ge_protons(), width=250, num_items=8, callback=_log),
|
tag=DPG_TAG_GE_PROTON_AVAILABLE,
|
||||||
get_available_ge_protons
|
items=(),
|
||||||
))
|
width=250,
|
||||||
|
num_items=8
|
||||||
|
),
|
||||||
|
|
||||||
|
dpg.add_spacer(height=10)
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
dearpygui
|
dearpygui~=1.11.1
|
||||||
|
requests~=2.32.3
|
||||||
|
|
Loading…
Reference in a new issue