PyELSSA 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- PyELSSA/__init__.py +28 -0
- PyELSSA/configuration.py +325 -0
- PyELSSA/data/TestFile.txt +0 -0
- PyELSSA/data/TestFile1.csv +0 -0
- PyELSSA/data/TestFile2.csv +0 -0
- PyELSSA/gui/gui_base_frame.py +221 -0
- PyELSSA/gui/gui_main_page.py +142 -0
- PyELSSA/gui/gui_start.py +424 -0
- PyELSSA/gui/gui_sub_frame.py +414 -0
- PyELSSA/licensing.py +224 -0
- PyELSSA/master.py +87 -0
- PyELSSA/release_update.py +59 -0
- PyELSSA/shared_contents.py +491 -0
- PyELSSA/shared_msazure_api.py +469 -0
- pyelssa-0.1.0.dist-info/METADATA +44 -0
- pyelssa-0.1.0.dist-info/RECORD +18 -0
- pyelssa-0.1.0.dist-info/WHEEL +4 -0
- pyelssa-0.1.0.dist-info/licenses/LICENSE +88 -0
PyELSSA/__init__.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
#=================================================================
|
|
4
|
+
# Created by: Jieming Ye
|
|
5
|
+
# Created on: Sep 2026
|
|
6
|
+
# Last Modified: XX/XX/2026
|
|
7
|
+
#=================================================================
|
|
8
|
+
# Copyright (c) 2026 [Jieming Ye]
|
|
9
|
+
#
|
|
10
|
+
# This Python source code is licensed under the
|
|
11
|
+
# Open Source Non-Commercial License (OSNCL) v1.0
|
|
12
|
+
# See LICENSE for details.
|
|
13
|
+
#=================================================================
|
|
14
|
+
"""
|
|
15
|
+
Python on ELSSA: PyELSSA Package
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
#=================================================================
|
|
19
|
+
# VERSION CONTROL
|
|
20
|
+
# V1.0.0 (Jieming Ye) - Initial Version
|
|
21
|
+
#=================================================================
|
|
22
|
+
# Set Information Variable
|
|
23
|
+
__author__ = "Jieming Ye"
|
|
24
|
+
__copyright__ = "Copyright 2026, Engineering Services"
|
|
25
|
+
__credits__ = "Jieming Ye"
|
|
26
|
+
__version__ = "0.1.0"
|
|
27
|
+
__email__ = "jieming.ye@networkrail.co.uk"
|
|
28
|
+
__status__ = "Beta Version"
|
PyELSSA/configuration.py
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
#
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
#=================================================================
|
|
4
|
+
# Created by: Jieming Ye
|
|
5
|
+
# Created on: July 2026
|
|
6
|
+
# Last Modified: July 2026
|
|
7
|
+
#=================================================================
|
|
8
|
+
# Copyright (c) 2026 [Jieming Ye]
|
|
9
|
+
#
|
|
10
|
+
# This Python source code is licensed under the
|
|
11
|
+
# Open Source Non-Commercial License (OSNCL) v1.0
|
|
12
|
+
# See LICENSE for details.
|
|
13
|
+
#=================================================================
|
|
14
|
+
"""
|
|
15
|
+
This scripts contains the functions that control the configuration file settings
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
#=================================================================
|
|
19
|
+
# VERSION CONTROL
|
|
20
|
+
# V1.0 (Jieming Ye) - Initial Version
|
|
21
|
+
#
|
|
22
|
+
#=================================================================
|
|
23
|
+
# Set Information Variable
|
|
24
|
+
# N/A
|
|
25
|
+
#=================================================================
|
|
26
|
+
|
|
27
|
+
import os
|
|
28
|
+
import json
|
|
29
|
+
import importlib
|
|
30
|
+
import requests
|
|
31
|
+
import shutil
|
|
32
|
+
|
|
33
|
+
from PyELSSA.shared_contents import SharedVariables, SharedMethods, DataFilenames, InternetValidation
|
|
34
|
+
from PyELSSA.shared_msazure_api import AzureWorkflow, AzureCloudID
|
|
35
|
+
|
|
36
|
+
CONFIG_FILE = SharedVariables.config_file
|
|
37
|
+
|
|
38
|
+
# initial database system check
|
|
39
|
+
def initial_missing_user_database_check():
|
|
40
|
+
'''
|
|
41
|
+
This function doing application database entering checking and returing:
|
|
42
|
+
FALSE: if the initial database check passed i.e. no missing files.
|
|
43
|
+
Filenamelist: if the initial database check failed.
|
|
44
|
+
'''
|
|
45
|
+
filenamelist = []
|
|
46
|
+
# check if the database path exist.
|
|
47
|
+
if not os.path.exists(SharedVariables.database_path_user):
|
|
48
|
+
for filename in DataFilenames.private_file.values():
|
|
49
|
+
filenamelist.append(filename)
|
|
50
|
+
for filename in DataFilenames.public_file.values():
|
|
51
|
+
filenamelist.append(filename)
|
|
52
|
+
|
|
53
|
+
# loop the database folder and add the missing files into the filename list
|
|
54
|
+
else:
|
|
55
|
+
for filename in DataFilenames.private_file.values():
|
|
56
|
+
file_in_package = os.path.join(SharedVariables.database_path_user, filename)
|
|
57
|
+
if not os.path.isfile(file_in_package):
|
|
58
|
+
filenamelist.append(filename)
|
|
59
|
+
for filename in DataFilenames.public_file.values():
|
|
60
|
+
file_in_package = os.path.join(SharedVariables.database_path_user, filename)
|
|
61
|
+
if not os.path.isfile(file_in_package):
|
|
62
|
+
filenamelist.append(filename)
|
|
63
|
+
|
|
64
|
+
# if not empty filename list return filenamelist otherwise False
|
|
65
|
+
if filenamelist:
|
|
66
|
+
return filenamelist
|
|
67
|
+
else:
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
# update the customised database library
|
|
71
|
+
@staticmethod
|
|
72
|
+
def update_database_library(filenamelist: list=None, library: int = 0):
|
|
73
|
+
'''
|
|
74
|
+
This function updates the customised database library from GitHub Repo online (if public)
|
|
75
|
+
or data folder (if private).
|
|
76
|
+
This will overwrite the existing database file blindly.
|
|
77
|
+
|
|
78
|
+
if no input is not provided, this will restore all default database library
|
|
79
|
+
|
|
80
|
+
library: (int)
|
|
81
|
+
default to 0: default library
|
|
82
|
+
1: user library
|
|
83
|
+
'''
|
|
84
|
+
if library == 0:
|
|
85
|
+
controlled_data_path = SharedVariables.database_path_default
|
|
86
|
+
else:
|
|
87
|
+
controlled_data_path = SharedVariables.database_path_user
|
|
88
|
+
|
|
89
|
+
print(f"Config Path: '{controlled_data_path}'")
|
|
90
|
+
|
|
91
|
+
os.makedirs(controlled_data_path, exist_ok=True)
|
|
92
|
+
|
|
93
|
+
private_file_list = []
|
|
94
|
+
public_file_list = []
|
|
95
|
+
|
|
96
|
+
if filenamelist:
|
|
97
|
+
for filename in filenamelist:
|
|
98
|
+
# check if the filename belongs public or private dataset, and update accordingly
|
|
99
|
+
if filename in DataFilenames.private_file.values():
|
|
100
|
+
private_file_list.append(filename)
|
|
101
|
+
elif filename in DataFilenames.public_file.values():
|
|
102
|
+
public_file_list.append(filename)
|
|
103
|
+
else:
|
|
104
|
+
SharedMethods.print_message(f"ERROR: File '{filename}' is not recognised as a valid database file. Please contact support using this error message...", "31")
|
|
105
|
+
return False
|
|
106
|
+
else: # default to updating all database files
|
|
107
|
+
for file in DataFilenames.private_file.values():
|
|
108
|
+
private_file_list.append(file)
|
|
109
|
+
for file in DataFilenames.public_file.values():
|
|
110
|
+
public_file_list.append(file)
|
|
111
|
+
|
|
112
|
+
# copy the private data from the packaged data folder
|
|
113
|
+
distribution = importlib.resources.files(SharedVariables.package_name)
|
|
114
|
+
package_path = os.path.join(str(distribution), SharedVariables.private_data_name)
|
|
115
|
+
|
|
116
|
+
# Get the absolute path of the file in the package location
|
|
117
|
+
for file in private_file_list:
|
|
118
|
+
file_in_package = os.path.join(package_path, file)
|
|
119
|
+
target_file = os.path.join(controlled_data_path, file)
|
|
120
|
+
try:
|
|
121
|
+
shutil.copy(file_in_package, target_file)
|
|
122
|
+
SharedMethods.print_message(f"ATTENTION: '{file}' database updated to default dataset.","33")
|
|
123
|
+
except Exception as e:
|
|
124
|
+
SharedMethods.print_message(f"ERROR: Error copying file {file} to {target_file}: {e}. Please contact support using this error message...", "31")
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
# check online resources and download the public data to the same folder
|
|
128
|
+
# Check internet connection
|
|
129
|
+
if not InternetValidation.internet_check(SharedVariables.public_data_path):
|
|
130
|
+
SharedMethods.print_message(f"WARNING: Online databse updated cannot proceed due to not allowed network. Skipping...","33")
|
|
131
|
+
return False
|
|
132
|
+
|
|
133
|
+
for file in public_file_list:
|
|
134
|
+
file_url = SharedVariables.raw_data_url_begin + file
|
|
135
|
+
# download the csv file to the target folder
|
|
136
|
+
target_file = os.path.join(controlled_data_path, file)
|
|
137
|
+
try:
|
|
138
|
+
response = requests.get(file_url)
|
|
139
|
+
response.raise_for_status() # Raise an error for bad responses
|
|
140
|
+
with open(target_file, 'wb') as f:
|
|
141
|
+
f.write(response.content)
|
|
142
|
+
SharedMethods.print_message(f"ATTENTION: '{file}' database updated to default dataset.","33")
|
|
143
|
+
except Exception as e:
|
|
144
|
+
SharedMethods.print_message(f"ERROR: Error downloading file {file} from {file_url}: {e}. Please contact support using this error message...", "31")
|
|
145
|
+
return False
|
|
146
|
+
|
|
147
|
+
return True
|
|
148
|
+
|
|
149
|
+
# reset configuraiton file varile to default
|
|
150
|
+
def reset_configuration_file_varible(key: str = "DEFAULT_ALL"):
|
|
151
|
+
"""
|
|
152
|
+
This function reset configuration varible to default
|
|
153
|
+
return True of False
|
|
154
|
+
"""
|
|
155
|
+
try:
|
|
156
|
+
if not os.path.exists(SharedVariables.config_file):
|
|
157
|
+
SharedMethods.print_message(f"ERROR: Configuration file not found. ","31")
|
|
158
|
+
return False
|
|
159
|
+
|
|
160
|
+
config_data = DataFilenames.default_config
|
|
161
|
+
|
|
162
|
+
if key == "DEFAULT_ALL":
|
|
163
|
+
with open(SharedVariables.config_file, "w") as file:
|
|
164
|
+
json.dump(config_data, file, indent=4)
|
|
165
|
+
return True
|
|
166
|
+
else:
|
|
167
|
+
return set_configuration_file_variable(key,config_data[key])
|
|
168
|
+
|
|
169
|
+
except Exception as e:
|
|
170
|
+
SharedMethods.print_message(f"ERROR: Error resetting configuration: {e}","31")
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
# set configuration file
|
|
175
|
+
def set_configuration_file_variable(key:str, value):
|
|
176
|
+
"""
|
|
177
|
+
This function handle the configuration file value update
|
|
178
|
+
return True or False
|
|
179
|
+
"""
|
|
180
|
+
try:
|
|
181
|
+
if not os.path.exists(CONFIG_FILE):
|
|
182
|
+
SharedMethods.print_message(f"ERROR: Configuration file not found. ","31")
|
|
183
|
+
return False
|
|
184
|
+
|
|
185
|
+
with open(CONFIG_FILE, "r") as file:
|
|
186
|
+
config_data = json.load(file)
|
|
187
|
+
|
|
188
|
+
if config_data.get(key) != value:
|
|
189
|
+
config_data[key] = value
|
|
190
|
+
with open(CONFIG_FILE, "w") as file:
|
|
191
|
+
json.dump(config_data, file, indent=4)
|
|
192
|
+
return True
|
|
193
|
+
|
|
194
|
+
except Exception as e:
|
|
195
|
+
SharedMethods.print_message(f"ERROR: Error setting configuration: {e}","31")
|
|
196
|
+
return False
|
|
197
|
+
|
|
198
|
+
# read configuration file varible
|
|
199
|
+
def read_configuration_file_variable(key:str, update_required: bool = False):
|
|
200
|
+
"""
|
|
201
|
+
This function read the configuration file and return value
|
|
202
|
+
return Value or False / None
|
|
203
|
+
"""
|
|
204
|
+
try:
|
|
205
|
+
if not os.path.exists(CONFIG_FILE):
|
|
206
|
+
SharedMethods.print_message(f"ERROR: Configuration file not found. ","31")
|
|
207
|
+
return False
|
|
208
|
+
|
|
209
|
+
config_data = {}
|
|
210
|
+
with open(CONFIG_FILE, "r") as file:
|
|
211
|
+
config_data = json.load(file)
|
|
212
|
+
|
|
213
|
+
if key not in config_data:
|
|
214
|
+
SharedMethods.print_message(f"WARNING: Configuration does not contain setting of {key}. ","33")
|
|
215
|
+
if update_required:
|
|
216
|
+
SharedMethods.print_message(f"WARNING: {key} will be set to default value. ","33")
|
|
217
|
+
return False
|
|
218
|
+
|
|
219
|
+
return config_data[key]
|
|
220
|
+
|
|
221
|
+
except Exception as e:
|
|
222
|
+
SharedMethods.print_message(f"ERROR: Error reading configuration: {e}","31")
|
|
223
|
+
return False
|
|
224
|
+
|
|
225
|
+
def version_updated():
|
|
226
|
+
try:
|
|
227
|
+
if os.path.exists(SharedVariables.config_file):
|
|
228
|
+
with open(SharedVariables.config_file, 'r') as file:
|
|
229
|
+
config_data = json.load(file)
|
|
230
|
+
if not config_data["package_version_number"] == SharedVariables.installed_version:
|
|
231
|
+
return True
|
|
232
|
+
return False
|
|
233
|
+
except Exception as e:
|
|
234
|
+
SharedMethods.print_message(f"ERROR: Error reading configuration: {e}","31")
|
|
235
|
+
return False
|
|
236
|
+
|
|
237
|
+
def update_elssa_core():
|
|
238
|
+
try:
|
|
239
|
+
# ensure the installation folder location exist
|
|
240
|
+
if os.path.exists(SharedVariables.package_path):
|
|
241
|
+
# validate if configuration path folder exist if not, create it
|
|
242
|
+
os.makedirs(SharedVariables.elssa_core_path, exist_ok=True)
|
|
243
|
+
# download the latest
|
|
244
|
+
if not AzureWorkflow.download_tdns_sharepoint_folder(AzureCloudID.DOC_LIBRARY,AzureCloudID.ELSSA_CORE_PATH,SharedVariables.elssa_core_path,True):
|
|
245
|
+
return False
|
|
246
|
+
return True
|
|
247
|
+
|
|
248
|
+
else:
|
|
249
|
+
SharedMethods.print_message(f"ERROR: {SharedVariables.package_name} has not been installed yet...","31")
|
|
250
|
+
return False
|
|
251
|
+
except Exception as e:
|
|
252
|
+
SharedMethods.print_message(f"ERROR: Error updating the PyELSSA core: {e}","31")
|
|
253
|
+
SharedMethods.print_message(f"ERROR: Please check the message above. Try again with proper internet or contact support...","31")
|
|
254
|
+
return False
|
|
255
|
+
|
|
256
|
+
# processing of checking configuraiton file and set up approprite action
|
|
257
|
+
def configuration_main():
|
|
258
|
+
# disable in debug mode (bypass the process in debug mode):
|
|
259
|
+
if SharedMethods.is_debug_mode():
|
|
260
|
+
return True
|
|
261
|
+
|
|
262
|
+
# validate if configuration path folder exist if not, create it
|
|
263
|
+
os.makedirs(SharedVariables.configuration_path, exist_ok=True)
|
|
264
|
+
|
|
265
|
+
# update default database if configuration file does not exist (1st run only)
|
|
266
|
+
if not os.path.exists(CONFIG_FILE):
|
|
267
|
+
# create empty configuration file.
|
|
268
|
+
try:
|
|
269
|
+
with open(CONFIG_FILE, "w") as file:
|
|
270
|
+
json.dump({}, file)
|
|
271
|
+
except Exception as e:
|
|
272
|
+
SharedMethods.print_message(f"ERROR: Error creating configuration: {e}","31")
|
|
273
|
+
return False
|
|
274
|
+
|
|
275
|
+
# version update steps
|
|
276
|
+
try:
|
|
277
|
+
if version_updated():
|
|
278
|
+
# after the version udpate, elssa core is forced to be update, set to false
|
|
279
|
+
set_configuration_file_variable(key = 'elssa_core_updated',value = False)
|
|
280
|
+
|
|
281
|
+
# download the ELSSA core from the SharePoint
|
|
282
|
+
if not read_configuration_file_variable(key = 'elssa_core_updated'):
|
|
283
|
+
# trying the update the default library if success or not.
|
|
284
|
+
if not update_elssa_core():
|
|
285
|
+
# update not successful, force exit until update completed to ensure consistent version
|
|
286
|
+
return False
|
|
287
|
+
else:
|
|
288
|
+
# write the status to json configuraiton file
|
|
289
|
+
set_configuration_file_variable(key = 'elssa_core_updated',value = True)
|
|
290
|
+
SharedMethods.print_message(f"INFO: ELSSA Core Package Updating Succesfully. Consider relauch the application for proper reloading.","32")
|
|
291
|
+
|
|
292
|
+
if not read_configuration_file_variable(key = 'default_database_updated', update_required = True):
|
|
293
|
+
# trying the update the default library if success or not.
|
|
294
|
+
if not update_database_library():
|
|
295
|
+
# update not successful
|
|
296
|
+
database_library = False
|
|
297
|
+
else:
|
|
298
|
+
# update succesfully
|
|
299
|
+
database_library = True
|
|
300
|
+
# write the status to json configuraiton file
|
|
301
|
+
set_configuration_file_variable(key = 'default_database_updated',value = database_library)
|
|
302
|
+
|
|
303
|
+
set_configuration_file_variable(key = 'package_version_number', value = SharedVariables.installed_version)
|
|
304
|
+
return True
|
|
305
|
+
|
|
306
|
+
except Exception as e:
|
|
307
|
+
SharedMethods.print_message(f"ERROR: Error reading / writing configuration: {e}","31")
|
|
308
|
+
return False
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def main():
|
|
312
|
+
if not configuration_main():
|
|
313
|
+
SharedMethods.print_message(f"ERROR: Configuration environment is not set properly. Close and try it again...","31")
|
|
314
|
+
SharedMethods.print_message(f"ERROR: Contact support {SharedVariables.contacts} if the issue persist...","31")
|
|
315
|
+
return False
|
|
316
|
+
else:
|
|
317
|
+
# database folder check: This is on customised user database only
|
|
318
|
+
filenamelist = initial_missing_user_database_check()
|
|
319
|
+
if filenamelist:
|
|
320
|
+
update_database_library(filenamelist=filenamelist,library=1)
|
|
321
|
+
return True
|
|
322
|
+
|
|
323
|
+
# Programme running
|
|
324
|
+
if __name__ == '__main__':
|
|
325
|
+
main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#=================================================================
|
|
3
|
+
# Created by: Jieming Ye
|
|
4
|
+
# Created on: Aug 2026
|
|
5
|
+
# Last Modified: Aug 2026
|
|
6
|
+
#=================================================================
|
|
7
|
+
# Copyright (c) 2026 [Jieming Ye]
|
|
8
|
+
#
|
|
9
|
+
# This Python source code is licensed under the
|
|
10
|
+
# Open Source Non-Commercial License (OSNCL) v1.0
|
|
11
|
+
# See LICENSE for details.
|
|
12
|
+
#=================================================================
|
|
13
|
+
"""
|
|
14
|
+
This the base frame for this GUI
|
|
15
|
+
"""
|
|
16
|
+
#=================================================================
|
|
17
|
+
# VERSION CONTROL
|
|
18
|
+
# V1.0 (Jieming Ye) - Initial Version
|
|
19
|
+
#
|
|
20
|
+
#
|
|
21
|
+
#=================================================================
|
|
22
|
+
|
|
23
|
+
import os
|
|
24
|
+
import tkinter as tk
|
|
25
|
+
from tkinter import filedialog, ttk
|
|
26
|
+
from threading import Thread
|
|
27
|
+
from multiprocessing import Process
|
|
28
|
+
from PyELSSA.shared_contents import SharedMethods, SharedVariables
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class BasePage(tk.Frame):
|
|
32
|
+
def __init__(self, parent, controller):
|
|
33
|
+
super().__init__(parent)
|
|
34
|
+
self.controller = controller
|
|
35
|
+
self.style = ttk.Style()
|
|
36
|
+
self._configure_styles()
|
|
37
|
+
|
|
38
|
+
self.headframe = self.create_frame(padding=(16, 16))
|
|
39
|
+
self.contentframe = self.create_frame(padding=(12, 8))
|
|
40
|
+
self.footerframe = self.create_frame(padding=(10, 6))
|
|
41
|
+
|
|
42
|
+
def _configure_styles(self):
|
|
43
|
+
self.style.configure('PageTitle.TLabel', font=('Helvetica', 16, 'bold'))
|
|
44
|
+
self.style.configure('PageSubtitle.TLabel', font=('Helvetica', 12, 'bold'))
|
|
45
|
+
self.style.configure('PageText.TLabel', font=('Helvetica', 10), foreground='#333333')
|
|
46
|
+
self.style.configure('Card.TLabelframe', borderwidth=1, relief='solid')
|
|
47
|
+
self.style.configure('Card.TLabelframe.Label', font=('Helvetica', 12, 'bold'))
|
|
48
|
+
self.style.configure('Action.TButton', font=('Helvetica', 10, 'bold'), padding=8)
|
|
49
|
+
self.style.configure('Secondary.TButton', font=('Helvetica', 10), padding=6)
|
|
50
|
+
self.style.configure('OptionText.TLabel', font=('Helvetica', 10), wraplength=660, foreground='#222222')
|
|
51
|
+
self.style.configure('Info.TLabel', font=('Helvetica', 9), foreground='#555555', wraplength=660)
|
|
52
|
+
self.style.configure('SmallText.TLabel', font=('Helvetica', 8), foreground='#444444', wraplength=660)
|
|
53
|
+
|
|
54
|
+
def create_frame(self, padding=(0, 0), row_weights=None, column_weights=None, **kwargs):
|
|
55
|
+
pack_options = {}
|
|
56
|
+
for option in ('fill', 'expand', 'side', 'anchor', 'padx', 'pady'):
|
|
57
|
+
if option in kwargs:
|
|
58
|
+
pack_options[option] = kwargs.pop(option)
|
|
59
|
+
|
|
60
|
+
frame = ttk.Frame(self, padding=padding, **kwargs)
|
|
61
|
+
if not pack_options:
|
|
62
|
+
pack_options = {'fill': tk.BOTH, 'expand': True}
|
|
63
|
+
frame.pack(**pack_options)
|
|
64
|
+
|
|
65
|
+
if row_weights is not None:
|
|
66
|
+
for index, weight in enumerate(row_weights):
|
|
67
|
+
frame.rowconfigure(index, weight=weight)
|
|
68
|
+
if column_weights is not None:
|
|
69
|
+
for index, weight in enumerate(column_weights):
|
|
70
|
+
frame.columnconfigure(index, weight=weight)
|
|
71
|
+
|
|
72
|
+
return frame
|
|
73
|
+
|
|
74
|
+
def create_section(self, parent, title=None, padding=(8, 6), pack=True, pack_options=None):
|
|
75
|
+
if title:
|
|
76
|
+
section = ttk.Labelframe(parent, text=title, style='Card.TLabelframe', padding=padding)
|
|
77
|
+
else:
|
|
78
|
+
section = ttk.Frame(parent, padding=padding)
|
|
79
|
+
if pack:
|
|
80
|
+
options = {'fill': tk.BOTH, 'expand': True, 'pady': (0, 8)}
|
|
81
|
+
if pack_options:
|
|
82
|
+
options.update(pack_options)
|
|
83
|
+
section.pack(**options)
|
|
84
|
+
return section
|
|
85
|
+
|
|
86
|
+
def create_scrollable_section(self, parent, title=None, height=280, padding=(8, 8)):
|
|
87
|
+
section = self.create_section(parent, title=title, padding=padding)
|
|
88
|
+
bg_color = '#f4f6f9'
|
|
89
|
+
try:
|
|
90
|
+
bg_color = section.cget('background')
|
|
91
|
+
except tk.TclError:
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
canvas = tk.Canvas(section, borderwidth=0, highlightthickness=0, background=bg_color)
|
|
95
|
+
scrollbar = ttk.Scrollbar(section, orient='vertical', command=canvas.yview)
|
|
96
|
+
content = ttk.Frame(canvas)
|
|
97
|
+
|
|
98
|
+
canvas.configure(yscrollcommand=scrollbar.set)
|
|
99
|
+
scrollbar.pack(side='right', fill='y')
|
|
100
|
+
canvas.pack(side='left', fill='both', expand=True)
|
|
101
|
+
window_id = canvas.create_window((0, 0), window=content, anchor='nw')
|
|
102
|
+
|
|
103
|
+
def on_configure(event):
|
|
104
|
+
canvas.configure(scrollregion=canvas.bbox('all'))
|
|
105
|
+
|
|
106
|
+
def on_canvas_resize(event):
|
|
107
|
+
canvas.itemconfigure(window_id, width=event.width)
|
|
108
|
+
|
|
109
|
+
content.bind('<Configure>', on_configure)
|
|
110
|
+
canvas.bind('<Configure>', on_canvas_resize)
|
|
111
|
+
|
|
112
|
+
def on_mousewheel(event):
|
|
113
|
+
delta = -1 if event.delta > 0 else 1
|
|
114
|
+
canvas.yview_scroll(delta, 'units')
|
|
115
|
+
|
|
116
|
+
content.bind('<Enter>', lambda _: content.bind_all('<MouseWheel>', on_mousewheel))
|
|
117
|
+
content.bind('<Leave>', lambda _: content.unbind_all('<MouseWheel>'))
|
|
118
|
+
section.configure(height=height)
|
|
119
|
+
return content
|
|
120
|
+
|
|
121
|
+
def create_label(self, parent, text, style='PageText.TLabel', **kwargs):
|
|
122
|
+
label = ttk.Label(parent, text=text, style=style, **kwargs)
|
|
123
|
+
return label
|
|
124
|
+
|
|
125
|
+
def create_button(self, parent, text, command, style='Action.TButton', **kwargs):
|
|
126
|
+
button = ttk.Button(parent, text=text, command=command, style=style, **kwargs)
|
|
127
|
+
return button
|
|
128
|
+
|
|
129
|
+
def create_entry(self, parent, variable, width=32, **kwargs):
|
|
130
|
+
entry = ttk.Entry(parent, textvariable=variable, width=width, **kwargs)
|
|
131
|
+
return entry
|
|
132
|
+
|
|
133
|
+
def create_radio(self, parent, text, variable, value, **kwargs):
|
|
134
|
+
radio = ttk.Radiobutton(parent, text=text, variable=variable, value=value, **kwargs)
|
|
135
|
+
return radio
|
|
136
|
+
|
|
137
|
+
def auto_excel_select(self, variable):
|
|
138
|
+
initial_dir = os.getcwd()
|
|
139
|
+
file_path = filedialog.askopenfilename(title='Select File...', initialdir=initial_dir)
|
|
140
|
+
if not file_path:
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
variable.set(file_path)
|
|
144
|
+
selected_dir = os.path.dirname(file_path)
|
|
145
|
+
selected_dir = os.path.normpath(selected_dir)
|
|
146
|
+
if selected_dir != os.getcwd():
|
|
147
|
+
os.chdir(selected_dir)
|
|
148
|
+
SharedVariables.current_path = selected_dir
|
|
149
|
+
self.controller.working_directory.set(selected_dir)
|
|
150
|
+
SharedMethods.print_message(f'ATTENTION: Working directory set to: {selected_dir}','33')
|
|
151
|
+
|
|
152
|
+
def auto_file_select(self, variable):
|
|
153
|
+
initial_dir = os.getcwd()
|
|
154
|
+
file_path = filedialog.askopenfilename(title='Select File...', initialdir=initial_dir)
|
|
155
|
+
if not file_path:
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
variable.set(file_path)
|
|
159
|
+
selected_dir = os.path.dirname(file_path)
|
|
160
|
+
selected_dir = os.path.normpath(selected_dir)
|
|
161
|
+
if selected_dir != os.getcwd():
|
|
162
|
+
os.chdir(selected_dir)
|
|
163
|
+
SharedVariables.current_path = selected_dir
|
|
164
|
+
self.controller.working_directory.set(selected_dir)
|
|
165
|
+
SharedMethods.print_message(f'ATTENTION: Working directory set to: {selected_dir}','33')
|
|
166
|
+
|
|
167
|
+
def run_new_thread_or_process(self, target_script, **inputs):
|
|
168
|
+
try:
|
|
169
|
+
args = [
|
|
170
|
+
target_script,
|
|
171
|
+
None,
|
|
172
|
+
None,
|
|
173
|
+
None,
|
|
174
|
+
None,
|
|
175
|
+
None,
|
|
176
|
+
None,
|
|
177
|
+
None,
|
|
178
|
+
None,
|
|
179
|
+
None,
|
|
180
|
+
None,
|
|
181
|
+
SharedVariables.current_path,
|
|
182
|
+
]
|
|
183
|
+
|
|
184
|
+
input_mapping = {
|
|
185
|
+
'text_input1': 1,
|
|
186
|
+
'text_input2': 2,
|
|
187
|
+
'option_input1': 3,
|
|
188
|
+
'option_input2': 4,
|
|
189
|
+
'time_input1': 5,
|
|
190
|
+
'time_input2': 6,
|
|
191
|
+
'add_text': 7,
|
|
192
|
+
'add_input1': 8,
|
|
193
|
+
'add_input2': 9,
|
|
194
|
+
'config_input': 10,
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
independent_process = False
|
|
198
|
+
for key, value in inputs.items():
|
|
199
|
+
if key in input_mapping:
|
|
200
|
+
index = input_mapping[key]
|
|
201
|
+
if isinstance(value, tk.Variable):
|
|
202
|
+
args[index] = value.get()
|
|
203
|
+
else:
|
|
204
|
+
args[index] = value
|
|
205
|
+
elif key == 'independent_process':
|
|
206
|
+
independent_process = bool(value)
|
|
207
|
+
else:
|
|
208
|
+
SharedMethods.print_message(f'WARNING: Unrecognized input key "{key}" provided. Skipping...','33')
|
|
209
|
+
|
|
210
|
+
if independent_process:
|
|
211
|
+
process = Process(target=SharedMethods.launch_new_thread_or_process, args=tuple(args))
|
|
212
|
+
process.start()
|
|
213
|
+
else:
|
|
214
|
+
thread = Thread(target=SharedMethods.launch_new_thread_or_process, args=tuple(args))
|
|
215
|
+
thread.start()
|
|
216
|
+
|
|
217
|
+
except Exception as e:
|
|
218
|
+
SharedMethods.print_message(
|
|
219
|
+
f'ERROR: Error in threading/processing...{e}. Contact Support / Do not carry out multiple tasking at the same time.',
|
|
220
|
+
'31'
|
|
221
|
+
)
|