552 lines
22 KiB
Python
552 lines
22 KiB
Python
from PyQt5.QtCore import Qt
|
|
from PyQt5.QtCore import Qt
|
|
from PyQt5.QtWidgets import (QMainWindow, QWidget, QToolBar, QAction,
|
|
QSizePolicy, QToolButton, QTreeWidget,
|
|
QListWidget, QListWidgetItem, QTreeWidgetItem, QGroupBox, QLineEdit,
|
|
QLabel, QTextEdit, QPushButton, QFileDialog, QSlider, QHBoxLayout, QVBoxLayout, QFrame, QGridLayout, QComboBox, QFontDialog, QTextBrowser)
|
|
from PyQt5.QtWidgets import QMessageBox
|
|
from PyQt5.QtGui import QIcon, QFont
|
|
from qtwidgets import Toggle
|
|
from striprtf.striprtf import rtf_to_text
|
|
from cloud_backend import LoginDialog, login, upload_file
|
|
import tempfile
|
|
import export_process, import_process, upload_process
|
|
import settings, pathlib, os, sys, icons, easySQL, tables, json, zipfile
|
|
|
|
global status_bar
|
|
global tool_bar
|
|
|
|
class ModificationItem(QTreeWidgetItem):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.id = 0
|
|
|
|
class ColectionItem(QListWidgetItem):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.id = 0
|
|
|
|
class MainGui(QMainWindow):
|
|
def __init__(self) -> None:
|
|
self.app_settings = settings.app_settings
|
|
super().__init__()
|
|
self.setWindowTitle(self.app_settings.get_setting('window_title'))
|
|
self.setMaximumHeight(720)
|
|
self.setMaximumWidth(1280)
|
|
self.resize(self.app_settings.get_setting('window_width'), self.app_settings.get_setting('window_height'))
|
|
self.setWindowIcon(QIcon(icons.window_icon))
|
|
|
|
self.widget = QWidget()
|
|
|
|
self.tool_bar = QToolBar()
|
|
self.addToolBar(self.tool_bar)
|
|
self.tool_bar.setMovable(False)
|
|
self.tool_bar.setFixedHeight(35)
|
|
|
|
self.exit_app = QAction("Save and exit", self)
|
|
self.exit_app.setIcon(QIcon(icons.exit_icon))
|
|
self.exit_app.triggered.connect(self.exit_application)
|
|
self.tool_bar.addAction(self.exit_app)
|
|
|
|
self.tool_bar.addSeparator()
|
|
|
|
# need an import button that imports a selected pcmp from a file dialog
|
|
self.import_tool = QAction("Import PCMP File", self)
|
|
self.import_tool.setToolTip("Imports a PCMP file from system into your Penumbra installation.")
|
|
self.import_tool.setIcon(QIcon(icons.import_icon))
|
|
self.import_tool.triggered.connect(self.import_collection)
|
|
self.tool_bar.addAction(self.import_tool)
|
|
|
|
# need an export button that exports a selected collection in the list
|
|
self.export_tool = QAction("Export Collection to Archive", self)
|
|
self.export_tool.setToolTip("Export collection from Penumbra installation into a share-able PCMP archive")
|
|
self.export_tool.setIcon(QIcon(icons.export_icon))
|
|
self.export_tool.setEnabled(False)
|
|
self.export_tool.triggered.connect(self.export_collection)
|
|
self.tool_bar.addAction(self.export_tool)
|
|
|
|
# need an upload dummy button for future features
|
|
self.upload_tool = QAction("Upload Collection to the set up server.", self)
|
|
self.upload_tool.setToolTip("If a server has been configured alongside a profile on that server, you may upload the collection straight there for others to download without needing to share it.")
|
|
self.upload_tool.setIcon(QIcon(icons.upload_icon))
|
|
self.upload_tool.setEnabled(False)
|
|
self.upload_tool.triggered.connect(self.upload_collection)
|
|
self.tool_bar.addAction(self.upload_tool)
|
|
|
|
self.refresh_tool = QAction("Refresh", self)
|
|
self.refresh_tool.setToolTip("Refreshes the loaded data and selected collection data!")
|
|
self.refresh_tool.setIcon(QIcon(icons.refresh))
|
|
self.refresh_tool.setEnabled(True)
|
|
self.tool_bar.addAction(self.refresh_tool)
|
|
|
|
#add a spacer
|
|
self.spacer = QWidget()
|
|
self.spacer.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
|
self.tool_bar.addWidget(self.spacer)
|
|
|
|
self.mode_label = QLabel()
|
|
self.mode_label.setText('Mode')
|
|
self.tool_bar.addWidget(self.mode_label)
|
|
|
|
self.mode_slider = Toggle()
|
|
self.mode_slider.setToolTip('Sets the mode between basic or advanced details allowing for more manipulation of imports and exports. Only use this if you know what you are doing!')
|
|
self.mode_slider.toggled.connect(self.mode_change)
|
|
self.tool_bar.addWidget(self.mode_slider)
|
|
|
|
|
|
self.settings_button = QAction("Settings", self)
|
|
self.settings_button.setIcon(QIcon(icons.configure))
|
|
self.tool_bar.addAction(self.settings_button)
|
|
self.settings_button.triggered.connect(self.change_font_size)
|
|
|
|
self.help_button = QAction("Help", self)
|
|
self.help_button.setIcon(QIcon(icons.help_icon))
|
|
self.tool_bar.addAction(self.help_button)
|
|
|
|
# need a database connection indicator
|
|
self.database_indicator = QAction("Database Status", self)
|
|
self.database_indicator.setIcon(QIcon(icons.database_bad))
|
|
self.tool_bar.addAction(self.database_indicator)
|
|
# need a sign-in/profile placard that acts as a button to change login info
|
|
self.profile_indicator = QToolButton()
|
|
self.profile_indicator.setText("Profile/Login")
|
|
self.profile_indicator.setToolTip("If a server is configured use this to attempt so sign in to a profile on that server. This profile will be used for when uploading a collection.")
|
|
self.profile_indicator.setIcon(QIcon(icons.profile))
|
|
self.profile_indicator.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
|
|
self.profile_indicator.clicked.connect(self.login_user)
|
|
self.tool_bar.addWidget(self.profile_indicator)
|
|
|
|
# main vertical layout object
|
|
self.horizontal_layout = QHBoxLayout(self)
|
|
|
|
# collection layout V
|
|
self.collection_vertical_layout = QVBoxLayout()
|
|
# self.inner_canvas.setStyleSheet("background-color: blue")
|
|
|
|
self.collection_list = QListWidget()
|
|
self.col_frame = QFrame()
|
|
self.col_frame.setFrameStyle(QFrame.Panel)
|
|
|
|
self.collection_list.setFixedWidth(200)
|
|
self.collection_list.setFixedHeight(300)
|
|
|
|
self.collection_vertical_layout.addWidget(self.collection_list)
|
|
self.collection_vertical_layout.addWidget(self.col_frame)
|
|
|
|
# Close out collection Layout V
|
|
|
|
|
|
self.details_vertical_layout = QVBoxLayout()
|
|
|
|
self.details_group = QGroupBox()
|
|
self.details_group.setTitle('Collection Details')
|
|
|
|
self.details_horizontal_layout = QHBoxLayout()
|
|
|
|
self.horizontal_form_layout = QGridLayout()
|
|
# ---- Form Control Start ---->
|
|
self.name_label = QLabel()
|
|
self.name_label.setText("Collection Name")
|
|
self.name_edit = QLineEdit()
|
|
self.name_edit.setMaximumWidth(200)
|
|
|
|
self.version_label = QLabel()
|
|
self.version_label.setText("Version")
|
|
self.version_edit = QLineEdit()
|
|
self.version_edit.setMaximumWidth(200)
|
|
|
|
self.comment_label = QLabel()
|
|
self.comment_label.setText("Comments")
|
|
|
|
self.comment_edit = QTextEdit()
|
|
self.comment_edit.setMaximumHeight(150)
|
|
self.comment_edit.setMaximumHeight(250)
|
|
|
|
|
|
self.horizontal_form_layout.addWidget(self.name_label, 0, 0)
|
|
self.horizontal_form_layout.addWidget(self.name_edit, 0, 1)
|
|
|
|
self.horizontal_form_layout.addWidget(self.version_label, 1, 0)
|
|
self.horizontal_form_layout.addWidget(self.version_edit, 1, 1)
|
|
|
|
self.horizontal_form_layout.addWidget(self.comment_label, 2, 0)
|
|
self.horizontal_form_layout.addWidget(self.comment_edit, 2, 1)
|
|
|
|
# ---- Form Control End ---->
|
|
|
|
self.details_group.setLayout(self.horizontal_form_layout)
|
|
self.details_group.setFixedHeight(300)
|
|
self.details_group.setMaximumWidth(500)
|
|
|
|
|
|
self.character_links = QGroupBox()
|
|
self.character_links.setTitle("Character Links")
|
|
self.character_links.setFixedHeight(300)
|
|
self.character_links.setFixedWidth(200)
|
|
|
|
self.character_grid_layout = QGridLayout()
|
|
self.links_list = QListWidget()
|
|
self.character_grid_layout.addWidget(self.links_list)
|
|
|
|
self.character_links.setLayout(self.character_grid_layout)
|
|
|
|
self.search_horizontal_layout = QHBoxLayout()
|
|
|
|
self.clear_filter_mods = QPushButton()
|
|
self.clear_filter_mods.setIcon(QIcon(icons.clear_filter))
|
|
self.clear_filter_mods.setMaximumWidth(30)
|
|
self.clear_filter_mods.setMaximumHeight(30)
|
|
|
|
self.mod_list_search = QLineEdit()
|
|
self.mod_list_search.textEdited.connect(self.filter_mods)
|
|
self.mod_list_search.setFixedHeight(30)
|
|
self.mod_list_search.setFixedWidth(200)
|
|
|
|
self.search_horizontal_layout.addWidget(self.clear_filter_mods)
|
|
self.search_horizontal_layout.addWidget(self.mod_list_search)
|
|
|
|
self.search_horizontal_layout.setSpacing(5)
|
|
self.search_horizontal_layout.setAlignment(Qt.AlignLeft)
|
|
|
|
|
|
self.mod_list = QTreeWidget()
|
|
|
|
|
|
self.details_horizontal_layout.addWidget(self.details_group)
|
|
self.details_horizontal_layout.addWidget(self.character_links)
|
|
|
|
self.details_vertical_layout.addLayout(self.details_horizontal_layout)
|
|
self.details_vertical_layout.addLayout(self.search_horizontal_layout)
|
|
self.details_vertical_layout.addWidget(self.mod_list)
|
|
# ---- Details/Modlist Layout V ---- >
|
|
|
|
self.advanced_widget_canvas = QWidget()
|
|
self.advanced_widget_canvas.setLayout(self.details_vertical_layout)
|
|
|
|
self.horizontal_layout.addLayout(self.collection_vertical_layout)
|
|
|
|
|
|
self.basic_vertical_layout = QVBoxLayout()
|
|
self.basic_horizontal_layout = QHBoxLayout()
|
|
|
|
self.basic_vertical_layout.addLayout(self.basic_horizontal_layout)
|
|
|
|
self.basic_group = QGroupBox()
|
|
self.basic_group.setTitle("Basic Mode Instructions")
|
|
self.basic_horizontal_layout.addWidget(self.basic_group)
|
|
|
|
self.group_layout = QGridLayout()
|
|
self.basic_group.setLayout(self.group_layout)
|
|
|
|
self.label1 = QLabel()
|
|
#self.label1.setAcceptRichText(True)
|
|
self.label1.setText("hello World")
|
|
self.label1.setWordWrap(True)
|
|
self.label1.setMinimumWidth(400)
|
|
|
|
|
|
self.group_layout.addWidget(self.label1, 0, 0)
|
|
self.group_layout.setAlignment(Qt.AlignLeft)
|
|
self.group_layout.addWidget(self.name_edit, 1, 0)
|
|
|
|
|
|
self.basic_widget_canvas = QWidget()
|
|
self.basic_widget_canvas.setLayout(self.basic_vertical_layout)
|
|
|
|
# this is where the if split needs to happen
|
|
|
|
basic_mode = True
|
|
advanced_mode = False
|
|
self.horizontal_layout.addWidget(self.advanced_widget_canvas)
|
|
self.horizontal_layout.addWidget(self.basic_widget_canvas)
|
|
|
|
|
|
if advanced_mode:
|
|
self.basic_widget_canvas.setHidden(True)
|
|
self.advanced_widget_canvas.setHidden(False)
|
|
if basic_mode:
|
|
self.basic_widget_canvas.setHidden(False)
|
|
self.advanced_widget_canvas.setHidden(True)
|
|
|
|
|
|
self.widget.setLayout(self.horizontal_layout)
|
|
self.setCentralWidget(self.widget)
|
|
|
|
self.selected_item = None
|
|
self.selected_item_meta = {}
|
|
self.selected_item_id = 0
|
|
self.selected_item_data = {}
|
|
|
|
def change_font_size(self, a):
|
|
(font, ok) = QFontDialog.getFont(QFont("Helvetica [Cronyx]", 10), self)
|
|
if ok:
|
|
# the user clicked OK and font is set to the font the user selected
|
|
self.setFont(font)
|
|
|
|
def __post__init__(self):
|
|
self.collection_list.itemClicked.connect(self.populate_details)
|
|
if not settings.app_settings.get_setting("update_opt_in") or settings.app_settings.lock_client:
|
|
self.profile_indicator.setEnabled(False)
|
|
self.database_indicator.setEnabled(False)
|
|
self.repopulate_collections()
|
|
self.repopulate_modslist()
|
|
|
|
def exit_application(self):
|
|
self.app_settings.save_settings()
|
|
sys.exit()
|
|
|
|
def mode_change(self, a):
|
|
if a:
|
|
self.setWindowTitle(f"{self.app_settings.get_setting('window_title')} (Advanced Mode)")
|
|
self.app_settings.set_setting('advanced_mode', True)
|
|
self.basic_widget_canvas.setHidden(True)
|
|
self.advanced_widget_canvas.setHidden(False)
|
|
else:
|
|
self.setWindowTitle(f"{self.app_settings.get_setting('window_title')} (Basic Mode)")
|
|
self.app_settings.set_setting('advanced_mode', False)
|
|
self.advanced_widget_canvas.setHidden(True)
|
|
self.basic_widget_canvas.setHidden(False)
|
|
|
|
|
|
if not self.app_settings.get_setting('advanced_mode'):
|
|
self.version_edit.setDisabled(True)
|
|
self.comment_edit.setDisabled(True)
|
|
self.links_list.setDisabled(True)
|
|
self.mod_list_search.setDisabled(True)
|
|
self.clear_filter_mods.setDisabled(True)
|
|
|
|
else:
|
|
self.version_edit.setDisabled(False)
|
|
self.comment_edit.setDisabled(False)
|
|
self.links_list.setDisabled(False)
|
|
self.mod_list_search.setDisabled(False)
|
|
self.clear_filter_mods.setDisabled(False)
|
|
|
|
self.repopulate_modslist()
|
|
|
|
def get_export_upload_payload(self) -> dict:
|
|
collection_data = easySQL.fetchone_from_table(tables.collections, filter=('collection_id', self.selected_item_id))
|
|
collection_settings = json.loads(collection_data.settings)
|
|
|
|
# The below process handles the repackaging payload for a collection allowing for advanced changes such
|
|
# as enabling or putting a modification on ignore.
|
|
# TODO: enabled_mods and mods_to_copy could be consolidated into a single dictionary
|
|
enabled_mods = {}
|
|
mods_to_copy = []
|
|
count = self.mod_list.topLevelItemCount()
|
|
for index in range(count):
|
|
item = self.mod_list.topLevelItem(index)
|
|
if item.checkState(0) == 2:
|
|
modification_row = easySQL.fetchone_from_table(tables.modifications, filter=('name', item.text(0)))
|
|
mods_to_copy.append((modification_row.name, modification_row.mod_path, modification_row.total_files))
|
|
try:
|
|
mod_settings = collection_settings[item.text(0)]
|
|
mod_settings['Enabled'] = True
|
|
enabled_mods[item.text(0)] = mod_settings
|
|
except:
|
|
pass
|
|
|
|
payload: {str, any} = {
|
|
'collection_json':{
|
|
'Version': collection_data.version,
|
|
'Id': collection_data.uuid,
|
|
'Name': self.name_edit.text(),
|
|
'Settings': enabled_mods,
|
|
'Inheritance': []
|
|
},
|
|
'character_links': json.loads(collection_data.character_links),
|
|
'meta_data': {
|
|
"collection_name": self.name_edit.text(),
|
|
"collection_uuid": collection_data.uuid,
|
|
"version": self.version_edit.text(),
|
|
"comments": self.comment_edit.toPlainText()
|
|
},
|
|
'mods_to_copy': mods_to_copy
|
|
}
|
|
|
|
return payload
|
|
|
|
def upload_collection(self):
|
|
payload = self.get_export_upload_payload()
|
|
|
|
progress_dialog = upload_process.UploadProgressDialog(self, payload)
|
|
progress_dialog.start_upload()
|
|
|
|
def export_collection(self):
|
|
|
|
payload = self.get_export_upload_payload()
|
|
dialog = QFileDialog(self)
|
|
dialog.setFileMode(QFileDialog.DirectoryOnly)
|
|
if dialog.exec_():
|
|
save_directory = dialog.selectedFiles()[0]
|
|
payload['save_directory'] = save_directory
|
|
|
|
# todo: remove this
|
|
with open('test.json', 'w+') as file:
|
|
json.dump(payload, file, indent=2)
|
|
|
|
progress_dialog = export_process.ExportProgressDialog(self, payload)
|
|
progress_dialog.start_exportation()
|
|
|
|
def import_collection(self):
|
|
dialog = QFileDialog(self)
|
|
dialog.setFileMode(QFileDialog.AnyFile)
|
|
dialog.setNameFilter("Archive (*.pcmp)")
|
|
if dialog.exec_():
|
|
path_to_zip = pathlib.Path(dialog.selectedFiles()[0])
|
|
print(path_to_zip) # do some validation on the path
|
|
|
|
|
|
|
|
with zipfile.ZipFile(path_to_zip) as collection_pcmp:
|
|
|
|
|
|
collection_uuid = ''
|
|
|
|
with collection_pcmp.open("meta.json") as _file:
|
|
meta = json.load(_file)
|
|
collection_uuid = meta["collection_uuid"]
|
|
|
|
with collection_pcmp.open(f"{collection_uuid}.json") as _file:
|
|
collection_data = json.load(_file)
|
|
|
|
if collection_uuid in [row.uuid for row in easySQL.fetchall_from_table(tables.collections)]:
|
|
warning_dialog = QMessageBox(self)
|
|
warning_dialog.setWindowTitle('Collection already exists...')
|
|
warning_dialog.setText(f"It appears that this collection already exists, would you like to overwrite it?")
|
|
warning_dialog.setIcon(QMessageBox.Question)
|
|
warning_dialog.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
|
|
button = warning_dialog.exec()
|
|
|
|
if button == QMessageBox.No:
|
|
return
|
|
|
|
|
|
payload = {
|
|
'collection_json': collection_data,
|
|
'meta_data': meta,
|
|
'path_to_zip': path_to_zip,
|
|
'mods_to_copy': [key for key in collection_data['Settings'].keys()]
|
|
}
|
|
|
|
|
|
print(payload)
|
|
self.progress_dialog = import_process.ImportProgressDialog(self, payload)
|
|
self.progress_dialog.import_collection()
|
|
|
|
def login_user(self):
|
|
|
|
dialog = LoginDialog(self)
|
|
if dialog.exec_():
|
|
self.app_settings.user_data = dialog.user_data
|
|
self.app_settings.connected = True
|
|
self.profile_indicator.setIcon(QIcon(settings.app_settings.user_data['avatar']))
|
|
self.profile_indicator.setText(settings.app_settings.username)
|
|
self.database_indicator.setIcon(QIcon(icons.database_good))
|
|
|
|
def filter_mods(self):
|
|
filter = self.mod_list_search.text()
|
|
count = self.mod_list.topLevelItemCount()
|
|
for index in range(count):
|
|
item = self.mod_list.topLevelItem(index)
|
|
present_filter = [mod_name.strip() for mod_name in json.loads(self.selected_item_data.settings).keys()]
|
|
if filter in item.text(0) and item.text(0) in present_filter:
|
|
item.setHidden(False)
|
|
else:
|
|
item.setHidden(True)
|
|
|
|
def repopulate_modslist(self, enabled: list = [], present: list = []):
|
|
self.mod_list.clear()
|
|
self.mod_list.setHeaderLabel('Modification Name')
|
|
self.mod_list.setAlternatingRowColors(True)
|
|
|
|
list = easySQL.fetchall_from_table(tables.modifications)
|
|
for d, row in enumerate(list):
|
|
|
|
item = ModificationItem()
|
|
item.setText(0, row.name)
|
|
|
|
if row.name in enabled:
|
|
item.setCheckState(0, 2)
|
|
else:
|
|
item.setCheckState(0, 0)
|
|
|
|
item.id = row.mod_id
|
|
self.mod_list.addTopLevelItem(item)
|
|
|
|
if row.name in present or len(present)==0:
|
|
item.setHidden(False)
|
|
item.setDisabled(False)
|
|
else:
|
|
item.setHidden(True)
|
|
item.setDisabled(True)
|
|
|
|
if not self.app_settings.get_setting('advanced_mode'):
|
|
item.setDisabled(True)
|
|
|
|
def repopulate_collections(self):
|
|
self.collection_list.clear()
|
|
self.collection_list.setAlternatingRowColors(True)
|
|
|
|
list = [(row.collection_id, row.name) for row in easySQL.fetchall_from_table(tables.collections)]
|
|
for id, name in list:
|
|
item = ColectionItem()
|
|
item.setText(name)
|
|
item.id = id
|
|
item.setToolTip(f"{id}")
|
|
self.collection_list.addItem(item)
|
|
|
|
def repopulate_character_links(self):
|
|
self.links_list.clear()
|
|
links = json.loads(self.selected_item_data.character_links)
|
|
|
|
if links['Default']:
|
|
item = QListWidgetItem()
|
|
item.setText("Default")
|
|
item.setCheckState(2)
|
|
self.links_list.addItem(item)
|
|
if links["Yourself"]:
|
|
item = QListWidgetItem()
|
|
item.setText("Yourself")
|
|
item.setCheckState(2)
|
|
self.links_list.addItem(item)
|
|
if len(links["Individuals"]) > 0:
|
|
for link in links["Individuals"]:
|
|
item = QListWidgetItem()
|
|
item.setText(link["Display"])
|
|
item.setCheckState(2)
|
|
self.links_list.addItem(item)
|
|
|
|
def populate_details(self, item):
|
|
|
|
self.export_tool.setEnabled(True)
|
|
if self.app_settings.connected:
|
|
self.upload_tool.setEnabled(True)
|
|
else:
|
|
self.upload_tool.setEnabled(False)
|
|
self.selected_item_id = item.id
|
|
self.selected_item = item
|
|
self.selected_item_data = easySQL.fetchone_from_table(tables.collections, filter=("collection_id", item.id))
|
|
self.selected_item_meta = easySQL.fetchone_from_table(tables.metadata, filter=("collection_name", self.selected_item_data.name))
|
|
settings_filter = [mod_name.strip() for mod_name, settings in json.loads(self.selected_item_data.settings).items() if settings['Enabled']]
|
|
present_filter = [mod_name.strip() for mod_name in json.loads(self.selected_item_data.settings).keys()]
|
|
self.repopulate_modslist(enabled=settings_filter, present=present_filter)
|
|
self.repopulate_character_links()
|
|
self.name_edit.setText(self.selected_item_data.name)
|
|
self.version_edit.setText(self.selected_item_meta.version)
|
|
self.comment_edit.setPlainText(self.selected_item_meta.comments)
|
|
|
|
if not self.app_settings.get_setting('advanced_mode'):
|
|
self.version_edit.setDisabled(True)
|
|
self.comment_edit.setDisabled(True)
|
|
self.links_list.setDisabled(True)
|
|
self.mod_list_search.setDisabled(True)
|
|
self.clear_filter_mods.setDisabled(True)
|
|
self.mod_list.setDisabled(True)
|
|
else:
|
|
self.version_edit.setDisabled(False)
|
|
self.comment_edit.setDisabled(False)
|
|
self.links_list.setDisabled(False)
|
|
self.mod_list_search.setDisabled(False)
|
|
self.clear_filter_mods.setDisabled(False)
|
|
self.mod_list.setDisabled(False) |