# OctaDist Copyright (C) 2019-2024 Rangsiman Ketkaew et al.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
import tkinter as tk
import octadist
[docs]
class ScriptingConsole:
"""
Start scripting interface for an interactive code.
User can access to class variable (dynamic variable).
+------------+
| Output box |
+------------+
| Input box |
+------------+
Parameters
----------
root : object
Passing self object from another class to this class as root argument.
See Also
--------
settings :
Program settings.
Examples
--------
>>> import tkinter as tk
>>> master = tk.Tk()
>>> console = ScriptingConsole(master)
>>> console.scripting_start()
"""
def __init__(self, root):
self.root = root
self.wd = None
self.history_command = []
[docs]
def scripting_start(self):
"""
Start scripting console.
"""
self.wd = tk.Toplevel(self.root.master)
if self.root.octadist_icon is not None:
self.wd.wm_iconbitmap(self.root.octadist_icon)
self.wd.title("OctaDist Scripting Interface")
self.wd.bind("<Return>", self.script_execute)
self.wd.resizable(0, 0)
lbl = tk.Label(self.wd, text="Output:")
lbl.grid(padx="5", pady="5", sticky=tk.W, row=0, column=0)
self.box_script = tk.Text(self.wd, width=70, height=20)
self.box_script.grid(padx="5", pady="5", row=1, column=0, columnspan=2)
lbl = tk.Label(self.wd, text="Input:")
lbl.grid(padx="5", pady="5", sticky=tk.W, row=2, column=0)
self.entry_script = tk.Entry(self.wd, width=62)
self.entry_script.grid(padx="5", pady="5", sticky=tk.W, row=3, column=0)
btn_script = tk.Button(self.wd, text="Run")
btn_script.bind("<Button-1>", self.script_execute)
btn_script.grid(padx="5", pady="5", row=3, column=1)
self.box_script.insert(
tk.INSERT, "Welcome to OctaDist interactive scripting console\n"
)
self.box_script.insert(
tk.INSERT,
"If you have no idea what to do about scripting, "
'type "help" to get started.\n\n',
)
self.wd.mainloop()
[docs]
def script_run_help(self):
"""
Show help messages.
"""
help_msg = f"""\
>>> Interactive code console for OctaDist.
>>> This scripting interface supports built-in commands as follows:
>>>
>>> Command\t\tDescription
>>> =======\t\t===========
>>> help\t\tShow this help info.
>>> list\t\tList all commands.
>>> info\t\tShow info of program.
>>> doc\t\tShow docstring of this function.
>>> show\t\tShow values of variables.
>>> \t\tUsage: show var1 [var2] [var3] [...]
>>> type\t\tShow type of variables.
>>> \t\tUsage: type var1 [var2] [var3] [...]
>>> set\t\tSet new value to variable.
>>> \t\tUsage: set var value
>>> clear\t\tClear stdout/stderr.
>>> clean\t\tClear stdout/stderr and command history.
>>> \t\thistory - Clean previous commands history.
>>> \t\tmaster - Clean cache of OctaDist master class.
>>> restore\t\tRestore program settings.
>>> history\t\tCommand history.
>>>
>>> Rangsiman Ketkaew\t\t<rangsiman1993@gmail.com>\t\tOctaDist {octadist.__version__}
"""
self.box_script.insert(tk.INSERT, help_msg + "\n")
[docs]
def script_run_list(self):
"""
Show list of commands in scripting run.
"""
all_command = (
"help, list, info, doc, show, type, set, "
"clear, clean, restore, history, exit"
)
self.box_script.insert(tk.INSERT, f">>> {all_command}\n")
[docs]
def script_run_info(self):
"""
Show info of program.
"""
self.box_script.insert(tk.INSERT, f">>> {octadist.__description__}\n")
[docs]
def script_run_doc(self):
"""
Show document of program.
"""
self.box_script.insert(tk.INSERT, f">>> {octadist.__doc__}\n")
[docs]
def script_run_show(self, args):
"""
Show value of variable that user requests.
Parameters
----------
args : str
Arbitrary argument.
"""
if not args:
self.box_script.insert(
tk.INSERT, ">>> show command needs at least 1 parameter\n"
)
return 1
first_arg = args[0].lower()
if first_arg == "all" or first_arg == "*":
for key in self.root.__dict__.keys():
value = self.root.__dict__[key]
self.box_script.insert(tk.INSERT, f">>> {key} = {value}\n")
return 0
for i in range(len(args)):
try:
key = args[i]
value = self.root.__dict__[f"{key}"]
self.box_script.insert(tk.INSERT, f">>> {key} = {value}\n")
except KeyError:
self.box_script.insert(
tk.INSERT, f'>>> variable "{args[i]}" is not defined\n'
)
[docs]
def script_run_type(self, args):
"""
Show data type of variable.
Parameters
----------
args : str
Arbitrary argument.
"""
if not args:
self.box_script.insert(
tk.INSERT, '>>> "type" command needs at least 1 parameter\n'
)
return 1
for i in range(len(args)):
try:
key = args[i]
data_type = type(self.root.__dict__[f"{key}"])
self.box_script.insert(tk.INSERT, f">>> {key} : {data_type}\n")
except KeyError:
self.box_script.insert(
tk.INSERT, f'>>> variable "{args[i]}" is not defined\n'
)
[docs]
def script_run_set(self, args):
"""
Set new value to variable.
Parameters
----------
args : str
Arbitrary argument.
"""
if len(args) != 2:
self.box_script.insert(tk.INSERT, ">>> No variable specified\n")
self.box_script.insert(tk.INSERT, ">>> set command needs 2 parameters\n")
return 1
key = args[0]
value = args[1]
self.root.__dict__[f"{key}"] = value
if f"key" not in self.root.__dict__:
self.box_script.insert(
tk.INSERT, f'>>> new dynamic variable "{key}" defined\n'
)
self.box_script.insert(tk.INSERT, f">>> {key} = {value}\n")
[docs]
def script_run_clear(self):
"""
Clear output box.
"""
self.box_script.delete(1.0, tk.END)
[docs]
def script_run_clean(self, args):
"""
Clear output box and clean variable.
"""
if not args:
# clean scripting console
self.script_run_clear()
elif args[0].lower() == "history":
self.history_command = []
elif args[0].lower() == "master":
# clean master windows
self.root.clear_cache()
[docs]
def script_run_restore(self):
"""
Restore all default settings.
"""
self.root.cutoff_metal_ligand = self.root.backup_cutoff_metal_ligand
self.root.cutoff_global = self.root.backup_cutoff_global
self.root.cutoff_hydrogen = self.root.backup_cutoff_hydrogen
self.root.text_editor = self.root.backup_text_editor
self.root.show_title = self.root.backup_show_title
self.root.show_axis = self.root.backup_show_axis
self.root.show_grid = self.root.backup_show_grid
self.box_script.insert(tk.INSERT, f">>> Restore all settings\n")
var_settings = (
"cutoff_metal_ligand",
"cutoff_global",
"cutoff_hydrogen",
"text_editor",
"show_title",
"show_axis",
"show_grid",
)
for key in var_settings:
value = self.root.__dict__[f"{key}"]
self.box_script.insert(tk.INSERT, f">>> {key} = {value}\n")
[docs]
def script_run_history(self):
"""
Show history of command.
"""
if not self.history_command:
self.box_script.insert(tk.INSERT, f">>> no history\n")
for item in self.history_command:
self.box_script.insert(tk.INSERT, f">>> {item}\n")
[docs]
def script_no_command(self, command):
"""
Show statement if command not found.
Parameters
----------
command : str
Command that user submits.
"""
self.box_script.insert(tk.INSERT, f'>>> Command "{command}" not found\n')
[docs]
def script_execute(self, event):
"""
Execute input command scripting.
Parameters
----------
event : object
Object for button interaction
"""
user_command = self.entry_script.get().strip().split()
self.entry_script.delete(0, tk.END)
# print input command
if user_command:
res = " ".join([str(i) for i in user_command])
self.box_script.insert(tk.INSERT, f"{res}\n")
# collect command to history list
if res.lower() != "history":
self.history_command.append(res)
if len(user_command) == 0:
self.box_script.insert(tk.INSERT, ">>> no command\n")
self.box_script.see(tk.END)
return 1
command = user_command[0].lower()
args = user_command[1:]
if command == "help":
self.script_run_help()
elif command == "list":
self.script_run_list()
elif command == "info":
self.script_run_info()
elif command == "doc":
self.script_run_doc()
elif command == "show":
self.script_run_show(args)
elif command == "type":
self.script_run_type(args)
elif command == "set":
self.script_run_set(args)
elif command == "clear":
self.script_run_clear()
elif command == "clean":
self.script_run_clean(args)
elif command == "restore":
self.script_run_restore()
elif command == "history":
self.script_run_history()
elif command == "exit" or command == "quit":
self.wd.destroy()
return 1
else:
self.script_no_command(command)
self.box_script.see(tk.END)