Python Shell GUIで制御されるものを作成しようとしています。
唯一のことは、その入出力全体をどのように作成するかわかりません。私は入力を入力できるようにしたいだけですpythonコマンドを実行し、pythonコマンドの出力を提供します。IDLEはTkinterで作成されていることを知っています、それでウィジェットを使用しますか?
それは文字通り単なる「タイプ入力、表示出力」のことです。
私はそれを調べてみましたが、結果のほとんどはコマンドラインで行うようであり、これは私が探しているものではありません。私とまったく同じような他の唯一の質問も、私が考えていたものではありませんでした。また、IDLEのソースコードを調べてみましたが、探していたものが見つかりませんでした。
Linuxで機能する回答をいくつか見つけましたが、Windows 10を使用しています...
画面の片側にコマンド出力に接続されている別のものがあるので、Tkinterにある「シェル」が必要です。
非常に単純なPythonシェルを作成するために使用されるウィジェットを知っていますか?
type input
_、_show output
_ "事。 ************************_import os
from tkinter import *
from subprocess import *
class PythonShell:
def __init__(self):
self.master = Tk()
self.mem_cache = open("idle.txt", "w+")
self.body = None
self.entry = None
self.button = None
self.entry_content = None
@staticmethod
def welcome_note():
"""
To show welcome note on tkinter window
:return:
"""
Label(text="Welcome To My Python Program [Version 1.0]", font='Arial 12', background="#272626",
foreground="white").pack()
Label(text=">> Insert Python Commands <<", font='Arial 12', background="#272626",
foreground="white").pack()
def get_text(self):
"""
This method will perform following operations;
1- Get text from body
2- Implies python compilation (treat text as command)
3- Set Output in Output-Entry
:return: get and set text in body of text box
"""
content = self.body.get(1.0, "end-1c")
out_put = self.run_commands(content)
self.entry_content.set(out_put)
def store_commands(self, command=None):
try:
self.mem_cache.write(command + ';')
self.mem_cache.close()
except Exception as e:
print(e)
def get_stored_commands(self):
try:
with open("idle.txt", "r") as self.mem_cache:
self.mem_cache.seek(0)
val = self.mem_cache.read()
self.mem_cache.close()
return val
except Exception as e:
print(e)
@staticmethod
def check_if_file_empty():
size = os.stat("idle.txt").st_size
if size != 0:
return True
else:
return False
def run_commands(self, command):
"""
This method would return output of every command place in text box
:param command: python command from text box
:return: output of command
"""
print("Running command: {}".format(command))
value = None
new_line_char = command.find('\n')
semi_colons_char = command.find(';')
double_quote = command.find('"')
try:
if new_line_char != -1:
if semi_colons_char != -1 & double_quote == -1:
new_cmd = command.replace("\n", "")
cmd_value = '"' + new_cmd + '"'
self.store_commands(command)
value = check_output("python -c " + cmd_value, Shell=True).decode()
Elif semi_colons_char == -1 & double_quote == -1:
new_cmd = command.replace("\n", ";")
cmd_value = '"' + new_cmd + '"'
self.store_commands(command)
value = check_output("python -c " + cmd_value, Shell=True).decode()
Elif double_quote != -1:
cmd_1 = command.replace('"', "'")
new_cmd = cmd_1.replace('\n', ';')
cmd_value = '"' + new_cmd + '"'
self.store_commands(command)
value = check_output("python -c " + cmd_value, Shell=True).decode()
Elif self.body.compare("end-1c", "==", "1.0"):
self.entry_content.set("the widget is empty")
Elif self.body.compare("end-1c", "==", "1.0"):
value = "The widget is empty. Please Enter Something."
else:
variable_analyzer = command.find('=')
file_size = PythonShell.check_if_file_empty()
if file_size:
new_cmd = command.replace('"', "'")
cmd_value = '"' + new_cmd + '"'
stored_value = self.get_stored_commands()
cmd = stored_value + cmd_value
cmd.replace('"', '')
value = check_output("python -c " + cmd, Shell=True).decode()
Elif variable_analyzer != -1:
new_cmd = command.replace('"', "'")
cmd_value = '"' + new_cmd + '"'
self.store_commands(cmd_value)
value = 'Waiting for input...'
pass
else:
new_cmd = command.replace('"', "'")
cmd_value = '"' + new_cmd + '"'
value = check_output("python -c " + cmd_value, Shell=True).decode()
except Exception as ex:
print('>>>', ex)
self.entry_content.set('Invalid Command. Try again!!!')
print('>>', value)
# To Clear Text body After Button Click
# self.body.delete('1.0', END)
return value
def start_terminal(self):
"""
Initiate tkinter session to place and run commands
:return:
"""
self.master.propagate(0)
self.master.geometry('750x350')
self.master.title('Python IDLE')
self.master.configure(background='#272626')
terminal.welcome_note()
self.body = Text(self.master, height='10', width='75', font='Consolas 12', background="#272626",
foreground="white",
insertbackground='white')
# self.body.propagate(0)
self.body.pack(expand=True)
Label(text=">> Command Output <<", font='Arial 12', background="#272626",
foreground="white").pack()
self.entry_content = StringVar()
self.entry = Entry(self.master, textvariable=self.entry_content, width=50, font='Consolas 16',
background="white",
foreground="black")
self.entry.pack()
# self.entry.propagate(0)
self.button = Button(self.master, text="Run Command", command=self.get_text, background="white",
foreground="black",
font='Helvetica 12').pack()
self.master.mainloop()
if __name__ == '__main__':
terminal = PythonShell()
terminal.start_terminal()
_
上記のpythonスクリプトには、次の階層があります。
_ |import ...
|class PythonShell:
|def __init__(self):...
@staticmethod
|def welcome_note():...
|def get_text(self):...
|def store_commands(self, commmand):...
|def get_stored_commands(self):...
@staticmethod
|def check_if_file_empty():
|def run_commands(self, command):...
|def start_terminal(self):...
|if __name__ == '__main__':...
_
上記のコードの基本的なワークフローは次のとおりです。
def welcome_note():...
テキスト本文の外側に表示されるラベルを含めます。
def get_text(self):...
2つの操作を実行します。 **テキスト本文からテキストを取得**&**入力ボックスに出力を設定**。
def store_commands(self, command):...
変数をファイルに格納するために使用します。
def get_stored_commands(self):
...ファイルに保存されている変数を取得します。
def check_if_file_empty():
...ファイルのサイズを確認します。
def run_commands(self, command):...
このメソッドは、Pythonコンパイラとして機能し、コマンドを受け取り、処理を行い、指定されたコマンドの出力を生成します。コマンドを実行するには、_subprocess-module
_を使用することをお勧めします。これは、新しいプロセスを生成して結果を取得するためのより強力な機能を提供するためです。 pythonを使用してwindow-commandsを実行するには、次のようなさまざまな組み込みライブラリが含まれます。
1。os( 詳細 ) 、2。subprocess( 詳細 )など.
どちらを使用する方がよいかをチェックアウトするには、リファレンスにアクセスしてください os-moduleよりもsubprocess- moduleが望ましい 。
def start_terminal(self):...
このメソッドは、単純にtkinter
セッションウィンドウを開始し、入力および出力ウィンドウの基本的なレイアウトを表示する機能を含みます。
必要に応じて、このコードをさらに変更および最適化できます。
このシンプルな_tkinter GUI based python Shell
_は、windows-command-Promptとしてシンプルな機能を実行します。 pythonターミナルに移動せずにコマンドプロンプトでpythonコマンドdirectly
を実行するには、次のように簡単です。
_python -c "print('Hey Eleeza!!!')"
_
結果は次のように簡単です。
_Hey Eleeza!!!
_
同様に、指定されたように一度に複数の行を直接直接実行します。
_python -c "import platform;sys_info=platform.uname();print(sys_info)"
_
その出力は次のようになります。
_My System Info: uname_result(system='Windows', node='DESKTOP-J75UTG5', release='10', version='10.0.18362', machine='AMD64', processor='Intel64 Family 6 Model 142 Stepping 10, GenuineIntel')
_
したがって、これを使用するには_tkinter python Shell
_;
コマンドを次のように配置できます。
_import platform
value=platform.uname()
print('Value:', value)
_
またはこのように;
_import platform;value=platform.uname();
print('Value:', value)
_
または単にインラインコマンドとして
_import platform;value=platform.uname();print('Value:', value)
_
同じ結果が得られます。これは、サンプルウィンドウであり、使用法の基本的な理解をもたらす結果が得られます。
更新された機能:
これは、主にexec()
を使用してpythonステートメントを実行し、subprocess.Popen()
を使用して外部コマンドを実行する単純なシェルです。
_import tkinter as tk
import sys, io
import subprocess as subp
from contextlib import redirect_stdout
class Shell(tk.Text):
def __init__(self, parent, **kwargs):
tk.Text.__init__(self, parent, **kwargs)
self.bind('<Key>', self.on_key) # setup handler to process pressed keys
self.cmd = None # hold the last command issued
self.show_Prompt()
# to append given text at the end of Text box
def insert_text(self, txt='', end='\n'):
self.insert(tk.END, txt+end)
self.see(tk.END) # make sure it is visible
def show_Prompt(self):
self.insert_text('>> ', end='')
self.mark_set(tk.INSERT, tk.END) # make sure the input cursor is at the end
self.cursor = self.index(tk.INSERT) # save the input position
# handler to process keyboard input
def on_key(self, event):
#print(event)
if event.keysym == 'Up':
if self.cmd:
# show the last command
self.delete(self.cursor, tk.END)
self.insert(self.cursor, self.cmd)
return "break" # disable the default handling of up key
if event.keysym == 'Down':
return "break" # disable the default handling of down key
if event.keysym in ('Left', 'BackSpace'):
current = self.index(tk.INSERT) # get the current position of the input cursor
if self.compare(current, '==', self.cursor):
# if input cursor is at the beginning of input (after the Prompt), do nothing
return "break"
if event.keysym == 'Return':
# extract the command input
cmd = self.get(self.cursor, tk.END).strip()
self.insert_text() # advance to next line
if cmd.startswith('`'):
# it is an external command
self.system(cmd)
else:
# it is python statement
self.execute(cmd)
self.show_Prompt()
return "break" # disable the default handling of Enter key
if event.keysym == 'Escape':
self.master.destroy() # quit the Shell
# function to handle python statement input
def execute(self, cmd):
self.cmd = cmd # save the command
# use redirect_stdout() to capture the output of exec() to a string
f = io.StringIO()
with redirect_stdout(f):
try:
exec(self.cmd, globals())
except Exception as e:
print(e)
# then append the output of exec() in the Text box
self.insert_text(f.getvalue(), end='')
# function to handle external command input
def system(self, cmd):
self.cmd = cmd # save the command
try:
# extract the actual command
cmd = cmd[cmd.index('`')+1:cmd.rindex('`')]
proc = subp.Popen(cmd, stdout=subp.PIPE, stderr=subp.PIPE, text=True)
stdout, stderr = proc.communicate(5) # get the command output
# append the command output to Text box
self.insert_text(stdout)
except Exception as e:
self.insert_text(str(e))
root = tk.Tk()
root.title('Simple Python Shell')
Shell = Shell(root, width=100, height=50, font=('Consolas', 10))
Shell.pack(fill=tk.BOTH, expand=1)
Shell.focus_set()
root.mainloop()
_
通常のpythonステートメントを入力するだけです:
_>> x = 1
>> print(x)
1
_
または、シェルコマンドを入力します。
_>> `cmd /c date /t`
2019-12-09
_
Up
キーを使用して、最後のコマンドを呼び出すこともできます。
ユーザー入力を必要とするシステムコマンドを実行すると、シェルは5秒間フリーズします(communicate()
で使用されるタイムアウト期間)。
on_key()
関数を必要に応じて変更できます。
exec()
の使用はお勧めできません。
私はpython Shellを使用して_code.InteractiveConsole
_を使用してプロジェクトのコマンドを実行しました。以下は簡略化されたバージョンですが、特別なキーのバインディング(Returnなど)を作成したため、かなり長いですがタブ...)をpythonコンソールのように動作させます。jediを使用したオートコンプリートや、pygmentsを使用した構文強調などの機能を追加できます。
主なアイデアは、_code.InteractiveConsole
_のPush()
メソッドを使用してコマンドを実行することです。このメソッドは、部分的なコマンドの場合、True
を返します。 def test(x):
、そしてこのフィードバックを使用して_...
_プロンプトを挿入します。それ以外の場合は、出力が表示され、新しい_>>>
_プロンプトが表示されます。 _contextlib.redirect_stdout
_を使用して出力をキャプチャします。
また、ユーザーが以前に実行されたコマンド内にテキストを挿入できないようにするため、マークとインデックスの比較を含むコードがたくさんあります。アイデアは、アクティブなプロンプトの開始位置を示す「入力」というマークを作成し、self.compare('insert', '<', 'input')
を使用して、ユーザーがアクティブなプロンプトの上にテキストを挿入しようとしていることを確認できます。
_import tkinter as tk
import sys
import re
from code import InteractiveConsole
from contextlib import redirect_stderr, redirect_stdout
from io import StringIO
class History(list):
def __getitem__(self, index):
try:
return list.__getitem__(self, index)
except IndexError:
return
class TextConsole(tk.Text):
def __init__(self, master, **kw):
kw.setdefault('width', 50)
kw.setdefault('wrap', 'Word')
kw.setdefault('Prompt1', '>>> ')
kw.setdefault('Prompt2', '... ')
banner = kw.pop('banner', 'Python %s\n' % sys.version)
self._Prompt1 = kw.pop('Prompt1')
self._Prompt2 = kw.pop('Prompt2')
tk.Text.__init__(self, master, **kw)
# --- history
self.history = History()
self._hist_item = 0
self._hist_match = ''
# --- initialization
self._console = InteractiveConsole() # python console to execute commands
self.insert('end', banner, 'banner')
self.Prompt()
self.mark_set('input', 'insert')
self.mark_gravity('input', 'left')
# --- bindings
self.bind('<Control-Return>', self.on_ctrl_return)
self.bind('<Shift-Return>', self.on_shift_return)
self.bind('<KeyPress>', self.on_key_press)
self.bind('<KeyRelease>', self.on_key_release)
self.bind('<Tab>', self.on_tab)
self.bind('<Down>', self.on_down)
self.bind('<Up>', self.on_up)
self.bind('<Return>', self.on_return)
self.bind('<BackSpace>', self.on_backspace)
self.bind('<Control-c>', self.on_ctrl_c)
self.bind('<<Paste>>', self.on_paste)
def on_ctrl_c(self, event):
"""Copy selected code, removing prompts first"""
sel = self.tag_ranges('sel')
if sel:
txt = self.get('sel.first', 'sel.last').splitlines()
lines = []
for i, line in enumerate(txt):
if line.startswith(self._Prompt1):
lines.append(line[len(self._Prompt1):])
Elif line.startswith(self._Prompt2):
lines.append(line[len(self._Prompt2):])
else:
lines.append(line)
self.clipboard_clear()
self.clipboard_append('\n'.join(lines))
return 'break'
def on_paste(self, event):
"""Paste commands"""
if self.compare('insert', '<', 'input'):
return "break"
sel = self.tag_ranges('sel')
if sel:
self.delete('sel.first', 'sel.last')
txt = self.clipboard_get()
self.insert("insert", txt)
self.insert_cmd(self.get("input", "end"))
return 'break'
def Prompt(self, result=False):
"""Insert a Prompt"""
if result:
self.insert('end', self._Prompt2, 'Prompt')
else:
self.insert('end', self._Prompt1, 'Prompt')
self.mark_set('input', 'end-1c')
def on_key_press(self, event):
"""Prevent text insertion in command history"""
if self.compare('insert', '<', 'input') and event.keysym not in ['Left', 'Right']:
self._hist_item = len(self.history)
self.mark_set('insert', 'input lineend')
if not event.char.isalnum():
return 'break'
def on_key_release(self, event):
"""Reset history scrolling"""
if self.compare('insert', '<', 'input') and event.keysym not in ['Left', 'Right']:
self._hist_item = len(self.history)
return 'break'
def on_up(self, event):
"""Handle up arrow key press"""
if self.compare('insert', '<', 'input'):
self.mark_set('insert', 'end')
return 'break'
Elif self.index('input linestart') == self.index('insert linestart'):
# navigate history
line = self.get('input', 'insert')
self._hist_match = line
hist_item = self._hist_item
self._hist_item -= 1
item = self.history[self._hist_item]
while self._hist_item >= 0 and not item.startswith(line):
self._hist_item -= 1
item = self.history[self._hist_item]
if self._hist_item >= 0:
index = self.index('insert')
self.insert_cmd(item)
self.mark_set('insert', index)
else:
self._hist_item = hist_item
return 'break'
def on_down(self, event):
"""Handle down arrow key press"""
if self.compare('insert', '<', 'input'):
self.mark_set('insert', 'end')
return 'break'
Elif self.compare('insert lineend', '==', 'end-1c'):
# navigate history
line = self._hist_match
self._hist_item += 1
item = self.history[self._hist_item]
while item is not None and not item.startswith(line):
self._hist_item += 1
item = self.history[self._hist_item]
if item is not None:
self.insert_cmd(item)
self.mark_set('insert', 'input+%ic' % len(self._hist_match))
else:
self._hist_item = len(self.history)
self.delete('input', 'end')
self.insert('insert', line)
return 'break'
def on_tab(self, event):
"""Handle tab key press"""
if self.compare('insert', '<', 'input'):
self.mark_set('insert', 'input lineend')
return "break"
# indent code
sel = self.tag_ranges('sel')
if sel:
start = str(self.index('sel.first'))
end = str(self.index('sel.last'))
start_line = int(start.split('.')[0])
end_line = int(end.split('.')[0]) + 1
for line in range(start_line, end_line):
self.insert('%i.0' % line, ' ')
else:
txt = self.get('insert-1c')
if not txt.isalnum() and txt != '.':
self.insert('insert', ' ')
return "break"
def on_shift_return(self, event):
"""Handle Shift+Return key press"""
if self.compare('insert', '<', 'input'):
self.mark_set('insert', 'input lineend')
return 'break'
else: # execute commands
self.mark_set('insert', 'end')
self.insert('insert', '\n')
self.insert('insert', self._Prompt2, 'Prompt')
self.eval_current(True)
def on_return(self, event=None):
"""Handle Return key press"""
if self.compare('insert', '<', 'input'):
self.mark_set('insert', 'input lineend')
return 'break'
else:
self.eval_current(True)
self.see('end')
return 'break'
def on_ctrl_return(self, event=None):
"""Handle Ctrl+Return key press"""
self.insert('insert', '\n' + self._Prompt2, 'Prompt')
return 'break'
def on_backspace(self, event):
"""Handle delete key press"""
if self.compare('insert', '<=', 'input'):
self.mark_set('insert', 'input lineend')
return 'break'
sel = self.tag_ranges('sel')
if sel:
self.delete('sel.first', 'sel.last')
else:
linestart = self.get('insert linestart', 'insert')
if re.search(r' $', linestart):
self.delete('insert-4c', 'insert')
else:
self.delete('insert-1c')
return 'break'
def insert_cmd(self, cmd):
"""Insert lines of code, adding prompts"""
input_index = self.index('input')
self.delete('input', 'end')
lines = cmd.splitlines()
if lines:
indent = len(re.search(r'^( )*', lines[0]).group())
self.insert('insert', lines[0][indent:])
for line in lines[1:]:
line = line[indent:]
self.insert('insert', '\n')
self.Prompt(True)
self.insert('insert', line)
self.mark_set('input', input_index)
self.see('end')
def eval_current(self, auto_indent=False):
"""Evaluate code"""
index = self.index('input')
lines = self.get('input', 'insert lineend').splitlines() # commands to execute
self.mark_set('insert', 'insert lineend')
if lines: # there is code to execute
# remove prompts
lines = [lines[0].rstrip()] + [line[len(self._Prompt2):].rstrip() for line in lines[1:]]
for i, l in enumerate(lines):
if l.endswith('?'):
lines[i] = 'help(%s)' % l[:-1]
cmds = '\n'.join(lines)
self.insert('insert', '\n')
out = StringIO() # command output
err = StringIO() # command error traceback
with redirect_stderr(err): # redirect error traceback to err
with redirect_stdout(out): # redirect command output
# execute commands in interactive console
res = self._console.Push(cmds)
# if res is True, this is a partial command, e.g. 'def test():' and we need to wait for the rest of the code
errors = err.getvalue()
if errors: # there were errors during the execution
self.insert('end', errors) # display the traceback
self.mark_set('input', 'end')
self.see('end')
self.Prompt() # insert new Prompt
else:
output = out.getvalue() # get output
if output:
self.insert('end', output, 'output')
self.mark_set('input', 'end')
self.see('end')
if not res and self.compare('insert linestart', '>', 'insert'):
self.insert('insert', '\n')
self.Prompt(res)
if auto_indent and lines:
# insert indentation similar to previous lines
indent = re.search(r'^( )*', lines[-1]).group()
line = lines[-1].strip()
if line and line[-1] == ':':
indent = indent + ' '
self.insert('insert', indent)
self.see('end')
if res:
self.mark_set('input', index)
self._console.resetbuffer() # clear buffer since the whole command will be retrieved from the text widget
Elif lines:
self.history.append(lines) # add commands to history
self._hist_item = len(self.history)
out.close()
err.close()
else:
self.insert('insert', '\n')
self.Prompt()
if __name__ == '__main__':
root = tk.Tk()
console = TextConsole(root)
console.pack(fill='both', expand=True)
root.mainloop()
_