", lambda e: self.click_parse())
74 | self.cmd_btn = ttk.Button(button_frame, text="Command", underline=2,
75 | command=lambda: self.click_command())
76 | self.cmd_btn.grid(column=2, row=1, sticky=E)
77 | self.parent.bind("", lambda e: self.click_command())
78 | self.clear_btn = ttk.Button(button_frame, text="Clear", underline=0,
79 | command=lambda: self.click_clear())
80 | self.clear_btn.grid(column=3, row=1, sticky=E)
81 | self.parent.bind("", lambda e: self.click_clear())
82 | button_frame.grid(column=2, row=8, columnspan=3, sticky=E)
83 |
84 | textbox_roms.focus_set()
85 |
86 | def enable_components(self):
87 | self.change_state('normal',
88 | [self.clear_btn, self.parse_btn])
89 |
90 | def disable_components(self):
91 | self.change_state('disabled',
92 | [self.clear_btn, self.parse_btn])
93 |
94 | def change_state(self, state, components):
95 | for component in components:
96 | component.config(state=state)
97 |
98 | def click_clear(self):
99 | self.path_dir_roms.set("")
100 | self.path_pack_file.set("")
101 |
102 | def click_command(self):
103 | if self.validate_info():
104 | cmd = create_command(parse_file=self.parent.parse_file,
105 | folder=self.path_dir_roms.get(),
106 | output=self.path_pack_file.get())
107 | # TextMessage().popup("Python command", cmd)
108 | CommandDialog(self, "Python command", cmd)
109 |
110 | def validate_info(self):
111 | out = False
112 | if (not self.path_dir_roms.get() == "" and
113 | os.path.lexists(self.path_dir_roms.get()) and
114 | not self.path_pack_file.get() == ""):
115 | out = True
116 | else:
117 | error_msg = ""
118 | if self.path_dir_roms.get() == "":
119 | error_msg += "ROMs folder is a required field.\n"
120 | if not os.path.lexists(self.path_dir_roms.get()):
121 | error_msg += "ROMs folder does not exist, "
122 | error_msg += "please select a valid folder.\n"
123 | if self.path_pack_file.get() == "":
124 | error_msg += "New pack file is a required field.\n"
125 | TextMessage().popup("Error", error_msg)
126 |
127 | return out
128 |
129 | def click_parse(self):
130 | if self.validate_info():
131 | self.disable_components()
132 | cmd = create_command_array(parse_file=self.parent.parse_file,
133 | folder=self.path_dir_roms.get(),
134 | output=self.path_pack_file.get())
135 | # print("cmd ", cmd)
136 | self.process = Popen(cmd, stdout=PIPE)
137 |
138 | q = Queue(maxsize=1024)
139 | t = Thread(target=self.reader_thread, args=[q])
140 | t.daemon = True # close pipe if GUI process exits
141 | t.start()
142 | self.parent.progress["mode"] = "indeterminate"
143 | self.update(q) # start update loop
144 |
145 | def reader_thread(self, q):
146 | """Read subprocess output and put it into the queue."""
147 | try:
148 | with self.process.stdout as pipe:
149 | for line in iter(pipe.readline, b''):
150 | q.put(line)
151 | finally:
152 | q.put(None)
153 |
154 | def update(self, q):
155 | """Update GUI with items from the queue."""
156 | # display all content
157 | for line in iter_except(q.get_nowait, Empty):
158 | if line is None:
159 | # print("Work is done!!!!")
160 | tmp_str = "Parsed " + str(self.value_buffer)[:-self.new_line_size] + " files."
161 | self.parent.text_label.set("Parse completed. " + tmp_str)
162 | self.parent.progress["mode"] = "determinate"
163 | self.parent.progress["value"] = 100
164 | self.parent.display_sucess("Parse Pack Finished", tmp_str)
165 | self.finish()
166 | return
167 | else:
168 | # print("line " + str(line))
169 | self.value_buffer = float(line[17:26])
170 | if self.value_buffer > 100:
171 | self.value_buffer = self.value_buffer % 100
172 | self.parent.progress["value"] = self.value_buffer
173 | self.parent.text_label.set(line[:26])
174 | break # display no more than one line per 40 milliseconds
175 | self.parent.after(40, self.update, q) # schedule next update
176 |
177 | def finish(self):
178 | self.process.kill() # exit subprocess if GUI is closed (zombie!)
179 | self.enable_components()
180 |
--------------------------------------------------------------------------------
/py2app_setup.py:
--------------------------------------------------------------------------------
1 | """
2 | py2app/py2exe build script for MyApplication.
3 |
4 | Will automatically ensure that all build prerequisites are available
5 | via ez_setup
6 |
7 | Usage (Mac OS X):
8 | python setup.py py2app
9 |
10 | Usage (Windows) does not work on Python 3.5+, but I leave it here:
11 | python setup.py py2exe
12 | """
13 |
14 | # import ez_setup
15 | import sys
16 | import utils
17 | from setuptools import setup
18 |
19 | NAME = utils.APP_NAME
20 | MAIN_SCRIPT = 'SmokeMonster-packs-UI.py'
21 | APP = [MAIN_SCRIPT]
22 | DATA_FILES = ['LICENSE']
23 | OPTIONS = {
24 | 'iconfile':'logoapp.icns',
25 | 'argv_emulation': True,
26 | 'packages': ['certifi'],
27 |
28 | }
29 |
30 | if sys.platform == 'darwin':
31 | extra_options = dict(
32 | setup_requires=['py2app'],
33 | app=APP,
34 | data_files=DATA_FILES,
35 | # Cross-platform applications generally expect sys.argv to
36 | # be used for opening files.
37 | options=dict(py2app=OPTIONS),
38 | )
39 | elif sys.platform == 'win32':
40 | # ez_setup.use_setuptools()
41 | extra_options = dict(
42 | setup_requires=['py2exe'],
43 | app=APP,
44 | )
45 | else:
46 | extra_options = dict(
47 | # Normally unix-like platforms will use "setup.py install"
48 | # and install the main script as such
49 | scripts=[MAIN_SCRIPT],
50 | )
51 |
52 | setup(
53 | name="MyApplication",
54 | **extra_options
55 | )
--------------------------------------------------------------------------------
/textmessage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | Graphical User Iterface for build_pack and parse_pack scripts.
5 | """
6 |
7 |
8 | import tkinter as tk
9 | from tkinter import *
10 | from platform import system
11 | from utils import *
12 |
13 |
14 | __author__ = "Aleyr"
15 | __date__ = "2018/11/13"
16 | __version__ = "$Revision: 0.9.2"
17 |
18 |
19 | class TextMessage(object):
20 |
21 | def __init__(self, parent=None):
22 | self.parent = parent
23 | # Fonts
24 | if "Darwin" in system():
25 | # print("\nOS X detected")
26 | self.fontsize = 12
27 | self.pad_radio = 3
28 | self.but_size = -2
29 | self.res_size = -1
30 | else:
31 | # print("\nWindows detected")
32 | self.fontsize = 10
33 | self.pad_radio = 0
34 | self.but_size = -2
35 | self.res_size = -2
36 |
37 | self.fontz = {
38 | "bold": ("TkDefaultFont", self.fontsize, "bold"),
39 | "normal_small": ("TkDefaultFont",
40 | self.fontsize + self.but_size, "normal"),
41 | "italic_small": ("TkDefaultFont",
42 | self.fontsize + self.but_size, "italic")}
43 |
44 | def popup(self, title, message, size=10):
45 | top = tk.Toplevel()
46 | top.title(title)
47 | about_message = (message)
48 | top.lift()
49 | msg = tk.Text(top, width=90, font=('courier', size, 'normal'))
50 | msg.grid(stick=tk.N, padx=(10, 10), pady=(10, 10))
51 | msg.insert("1.0", about_message)
52 | button = tk.Button(top, height=1, width=20, text="OK", underline=0,
53 | command=top.destroy, bg='gray97', relief=tk.GROOVE)
54 | button.grid(sticky=tk.S, pady=(0, 10))
55 | s = tk.Scrollbar(top, width=20)
56 | s.grid(row=0, column=0, sticky=E + N + S, padx=(0, 10), pady=(10, 10))
57 | s['command'] = msg.yview
58 | msg['yscrollcommand'] = s.set
59 | top.resizable(width=tk.FALSE, height=tk.FALSE)
60 | button.focus_set()
61 | top.bind("", lambda e: top.destroy())
62 | top.bind('', lambda e: top.destroy())
63 |
64 | def about(self):
65 | top = tk.Toplevel()
66 | top.title("About")
67 | top.lift()
68 | tk.Label(top,
69 | text="""Smoke Monster Packs UI
70 | by Aleyr""",
71 | justify=CENTER,
72 | font=self.fontz["bold"]).grid(row=0, column=0, columnspan=2,
73 | pady=(10, 10), padx=(10, 10))
74 | tk.Label(top,
75 | text="""2018""",
76 | justify=CENTER).grid(row=1, column=0, columnspan=2,
77 | pady=(10, 10), padx=(10, 10))
78 | tk.Label(top,
79 | text="""Contact:""",
80 | justify=CENTER).grid(row=2, column=0, columnspan=1,
81 | pady=(10, 10), padx=(10, 10))
82 | about_message = """aleyr@walla.com"""
83 | msg = tk.Text(top, height=1, width=27)
84 | msg.grid(row=2, column=1, padx=(10, 10), pady=(10, 10))
85 | msg.insert("1.0", about_message)
86 | button = tk.Button(top, height=1, width=20, text="OK", underline=0,
87 | command=top.destroy, bg='gray97', relief=tk.GROOVE)
88 | button.grid(row=3, column=0, columnspan=2, sticky=S, pady=(0, 10))
89 | top.resizable(width=FALSE, height=FALSE)
90 | button.focus_set()
91 | top.bind('', lambda e: top.destroy())
92 | top.bind('', lambda e: top.destroy())
93 |
--------------------------------------------------------------------------------
/utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | Graphical User Iterface for build_pack and parse_pack scripts.
5 | """
6 |
7 |
8 | import configparser
9 | import os
10 | import sys
11 |
12 | from tkinter import *
13 | from tkinter import filedialog as fd
14 | from pathlib import Path
15 | from platform import system
16 |
17 |
18 | __author__ = "Aleyr"
19 | __date__ = "2018/11/10"
20 | VERSION = "0.9.2"
21 | __version__ = "$Revision: " + VERSION
22 |
23 | BUILD_EXE_NAME = "build_pack.exe"
24 | PARSE_EXE_NAME = "parse_pack.exe"
25 |
26 | BUILD_SCRIPT_NAME = "build_pack.py"
27 | PARSE_SCRIPT_NAME = "parse_pack.py"
28 |
29 | APP_LOGO_ICO = "./logoapp.ico"
30 |
31 | APP_NAME = "EverDrive-Packs-Lists-Database-UI"
32 | INI_FILE_NAME = APP_NAME + ".cfg"
33 | INI_DIR_MAC = "~/Library/Application Support/" + APP_NAME + "/"
34 | INI_FILE_MAC = INI_DIR_MAC + APP_NAME + ".cfg"
35 | if "LOCALAPPDATA" in os.environ:
36 | INI_DIR_WINDOWS = os.environ["LOCALAPPDATA"] + "\\" + APP_NAME + "\\"
37 | INI_FILE_WINDOWS = INI_DIR_WINDOWS + APP_NAME + ".cfg"
38 | INI_DIR_UNIX = "~/"
39 | INI_FILE_UNIX = INI_DIR_UNIX + APP_NAME + "/" + APP_NAME + ".cfg"
40 |
41 | def select_folder(directory, title):
42 | path = fd.askdirectory(initialdir=os.getcwd(), title=title)
43 | if path:
44 | directory.set(path)
45 |
46 |
47 | def select_file_open(filename, title):
48 | path = fd.askopenfilename(initialdir=os.getcwd(), title=title)
49 | if path:
50 | filename.set(path)
51 |
52 |
53 | def select_file_save(filename, title):
54 | path = fd.asksaveasfilename(initialdir=os.getcwd(), title=title)
55 | if path:
56 | filename.set(path)
57 |
58 |
59 | def create_command(build_file=None, parse_file=None,
60 | folder=None, output=None, input_folder=None,
61 | database=None, output_folder=None, missing=None,
62 | file_strategy=None, skip_eisting=None):
63 | arr = create_command_array(build_file=build_file,
64 | parse_file=parse_file,
65 | folder=folder,
66 | output=output,
67 | input_folder=input_folder,
68 | database=database,
69 | output_folder=output_folder,
70 | missing=missing,
71 | file_strategy=file_strategy,
72 | skip_eisting=skip_eisting,
73 | new_line=False,
74 | add_quotes=True)
75 | cmd = " ".join(arr)
76 |
77 | return cmd
78 |
79 |
80 | def get_ini_file():
81 | tmp = INI_FILE_UNIX
82 | if "Darwin" in system():
83 | tmp = INI_FILE_MAC
84 | elif "Windows" in system():
85 | tmp = INI_FILE_WINDOWS
86 |
87 | # print("tmp " + tmp)
88 | out = Path(tmp)
89 | # print("out " + str(out))
90 | return out
91 |
92 |
93 | def save_ini_file(ini_file, section, values, root=None):
94 | config = configparser.ConfigParser()
95 | config[section] = values
96 | # folder check
97 | ini_dir = INI_DIR_UNIX
98 | if "Darwin" in system():
99 | ini_dir = INI_DIR_MAC
100 | elif "Windows" in system():
101 | ini_dir = INI_DIR_WINDOWS
102 | ini_dir = Path(ini_dir)
103 | # print ("save_ini_file ini_dir " + str(ini_dir))
104 | # print ("save_ini_file ini_dir.exists() " + str(ini_dir.exists()))
105 | try:
106 | if not ini_dir.exists():
107 | # print("Will create ini directory")
108 | ini_dir.mkdir()
109 | # print("ini dir created!")
110 | with ini_file.open(mode="w") as configfile:
111 | config.write(configfile)
112 | except:
113 | # TODO create dialog with error
114 | print("Error when saving file")
115 |
116 |
117 | def is_pack_scripts_folder(scripts_folder):
118 | out = False
119 | if scripts_folder:
120 | folder = Path(scripts_folder)
121 |
122 | out = _is_pack_scripts_folder(folder)
123 |
124 | return out
125 |
126 |
127 | def _is_pack_scripts_folder(folder):
128 | out = False
129 | if folder.exists() and folder.is_dir():
130 | build_file = folder / BUILD_SCRIPT_NAME
131 | parse_file = folder / PARSE_SCRIPT_NAME
132 |
133 | build_exe = folder / BUILD_EXE_NAME
134 | parse_exe = folder / PARSE_EXE_NAME
135 |
136 | if build_file.exists() and parse_file.exists():
137 | out = True
138 |
139 | if build_exe.exists() and parse_exe.exists():
140 | out = True
141 |
142 | return out
143 |
144 |
145 | def get_pack_scripts_paths(folder):
146 | build_file = folder / BUILD_SCRIPT_NAME
147 | parse_file = folder / PARSE_SCRIPT_NAME
148 | if "Windows" in system() and str(sys.executable).lower().find("python") == -1:
149 | build_file = Path(".") / BUILD_EXE_NAME
150 | parse_file = Path(".") / PARSE_EXE_NAME
151 |
152 | return (build_file, parse_file)
153 |
154 |
155 | def get_abs_path(path, add_quotes=False, quote="\""):
156 | out = os.path.abspath(str(path))
157 | if add_quotes:
158 | out = quote + out + quote
159 |
160 | return out
161 |
162 |
163 | def create_command_array(build_file=None, parse_file=None,
164 | folder=None, output=None, input_folder=None,
165 | database=None, output_folder=None, missing=None,
166 | file_strategy=None, skip_eisting=None, new_line=True,
167 | add_quotes=False):
168 | python_path = Path(sys.executable)
169 | cmd = [get_abs_path(python_path, add_quotes)]
170 | if "Windows" in system() and cmd[0].lower().find("python") == -1:
171 | cmd = []
172 | if parse_file:
173 | cmd.append(get_abs_path(parse_file, add_quotes))
174 | cmd.append("-f")
175 | cmd.append(get_abs_path(Path(folder), add_quotes))
176 | cmd.append("-o")
177 | cmd.append(get_abs_path(Path(output), add_quotes))
178 | else:
179 | cmd.append(get_abs_path(build_file, add_quotes))
180 | cmd.append("-i")
181 | cmd.append(get_abs_path(Path(input_folder), add_quotes))
182 | cmd.append("-d")
183 | cmd.append(get_abs_path(Path(database), add_quotes))
184 | cmd.append("-o")
185 | cmd.append(get_abs_path(Path(output_folder), add_quotes))
186 | if missing and len(missing) > 0:
187 | cmd.append("-m")
188 | cmd.append(get_abs_path(Path(missing), add_quotes))
189 | if file_strategy == 0:
190 | cmd.append("--file_strategy")
191 | cmd.append("copy")
192 | else:
193 | cmd.append("--file_strategy")
194 | cmd.append("hardlink")
195 | if skip_eisting:
196 | cmd.append("--skip_existing")
197 |
198 | if new_line:
199 | cmd.append("--new_line")
200 |
201 | return cmd
202 |
203 |
204 | def iter_except(function, exception):
205 | """Works like builtin 2-argument `iter()`, but stops on `exception`."""
206 | try:
207 | while True:
208 | yield function()
209 | except exception:
210 | return
211 |
212 |
213 | def click_set_paths(parent, script_path):
214 | pass
215 |
--------------------------------------------------------------------------------