import os import re import webbrowser import tkinter as tk import subprocess import threading from tkinter import TclError, filedialog, messagebox, simpledialog, scrolledtext from tkinter import ttk def create_editor(): window = tk.Tk() window_title_base = "C Runner" window.title(window_title_base) window.geometry("800x600") current_file_path = None current_folder_path = None overwrite_path_label = None # ボタンのフレームを作成する button_frame = tk.Frame(window) button_frame.pack(fill=tk.X) # フォントサイズ入力用のラベルを作成する font_size_label = tk.Label(button_frame, text="フォントサイズ:") font_size_label.pack(side=tk.LEFT) # フォントサイズを入力する入力欄を作成する font_size_entry = tk.Entry(button_frame) font_size_entry.pack(side=tk.LEFT) # 行文字カウンター status_label = tk.Label(window, text="Line: 1, Char: 1", bd=1, relief=tk.SUNKEN, anchor=tk.W) status_label.pack(side=tk.BOTTOM, fill=tk.X) main_pane = tk.PanedWindow(window, orient=tk.HORIZONTAL, sashrelief=tk.RAISED) main_pane.pack(fill=tk.BOTH, expand=True) explorer_frame = tk.Frame(main_pane, width=220) main_pane.add(explorer_frame) editor_container = tk.Frame(main_pane) main_pane.add(editor_container) explorer_label = tk.Label(explorer_frame, text="エクスプローラー") explorer_label.pack(anchor="w", padx=4, pady=(4, 2)) explorer_tree = ttk.Treeview(explorer_frame, columns=("fullpath", "type"), displaycolumns=()) explorer_tree.heading("#0", text="名前", anchor="w") explorer_tree.column("#0", stretch=True, width=180) explorer_tree.column("fullpath", width=0, stretch=False) explorer_tree.column("type", width=0, stretch=False) explorer_scrollbar = tk.Scrollbar(explorer_frame, orient=tk.VERTICAL, command=explorer_tree.yview) explorer_tree.configure(yscrollcommand=explorer_scrollbar.set) explorer_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(4, 0), pady=(0, 4)) explorer_scrollbar.pack(side=tk.RIGHT, fill=tk.Y, pady=(0, 4)) # テキストエリアと行番号バーを含むフレームを作成する text_frame = tk.Frame(editor_container) text_frame.pack(fill=tk.BOTH, expand=True) # 行番号を表示するテキストウィジェットを作成 line_number_bar = tk.Text(text_frame, width=4, padx=3, takefocus=0, border=0, background='lightgrey', state='disabled', wrap='none') line_number_bar.pack(side=tk.LEFT, fill=tk.Y) # テキストエリアを作成する text_area = scrolledtext.ScrolledText(text_frame, undo=True) text_area.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True) def sync_line_number_view(first=None): try: fraction = float(first) if first is not None else text_area.yview()[0] except (TypeError, ValueError): fraction = text_area.yview()[0] line_number_bar.config(state="normal") line_number_bar.yview_moveto(fraction) line_number_bar.config(state="disabled") def on_text_scroll(first, last): sync_line_number_view(first) if hasattr(text_area, "vbar"): text_area.vbar.set(first, last) def on_vertical_scroll(*args): text_area.yview(*args) sync_line_number_view() text_area.configure(yscrollcommand=on_text_scroll) if hasattr(text_area, "vbar"): text_area.vbar.configure(command=on_vertical_scroll) sync_line_number_view(0.0) def read_file_content(path): """Return file text and optional error message.""" last_error = None for encoding in ("utf-8", "cp932", "shift_jis"): try: with open(path, "r", encoding=encoding) as file_obj: return file_obj.read(), None except UnicodeDecodeError as exc: last_error = exc except OSError as exc: return None, exc return None, last_error def write_file_content(path, data): """Persist text to disk with common encodings.""" last_error = None for encoding in ("utf-8", "cp932"): try: with open(path, "w", encoding=encoding) as file_obj: file_obj.write(data) return True, None except UnicodeEncodeError as exc: last_error = exc except OSError as exc: return False, exc return False, last_error def load_file_into_editor(path): content, error = read_file_content(path) if error or content is None: message = f"ファイルの読み込みに失敗しました:\n\n{error}" if error else "ファイルを開けませんでした。" show_message(message) return False text_area.delete("1.0", tk.END) text_area.insert("1.0", content) text_area.edit_modified(False) text_area.mark_set(tk.INSERT, "1.0") on_text_change(None) update_line_numbers() update_status(None) return True def save_editor_content(path): data = text_area.get("1.0", "end-1c") success, error = write_file_content(path, data) if not success: message = f"ファイルの保存に失敗しました:\n\n{error}" if error else "ファイルの保存に失敗しました。" show_message(message) return success def clear_explorer(): for child in explorer_tree.get_children(): explorer_tree.delete(child) def populate_tree_node(node_id): node_path = explorer_tree.set(node_id, "fullpath") if not node_path or not os.path.isdir(node_path): return children = explorer_tree.get_children(node_id) if children and explorer_tree.set(children[0], "type") == "dummy": explorer_tree.delete(children[0]) elif children: return try: entries = sorted( os.scandir(node_path), key=lambda entry: (entry.is_file(), entry.name.lower()) ) except (PermissionError, FileNotFoundError): return for entry in entries: child = explorer_tree.insert( node_id, "end", text=entry.name, values=(entry.path, "directory" if entry.is_dir() else "file") ) if entry.is_dir(): explorer_tree.insert(child, "end", values=("", "dummy")) def build_tree(root_path): nonlocal current_folder_path abs_root = os.path.abspath(root_path) current_folder_path = abs_root clear_explorer() display_name = os.path.basename(abs_root) or abs_root root_id = explorer_tree.insert("", "end", text=display_name, values=(abs_root, "directory"), open=True) explorer_tree.insert(root_id, "end", values=("", "dummy")) populate_tree_node(root_id) explorer_tree.selection_set(root_id) explorer_tree.focus(root_id) def is_subpath(child_path, parent_path): if not child_path or not parent_path: return False try: common = os.path.commonpath([os.path.abspath(child_path), os.path.abspath(parent_path)]) except ValueError: return False return os.path.normcase(common) == os.path.normcase(os.path.abspath(parent_path)) def select_tree_item_for_path(target_path): if not target_path: return target_abs = os.path.abspath(target_path) target_norm = os.path.normcase(target_abs) def search(node_id): node_path = explorer_tree.set(node_id, "fullpath") if os.path.normcase(node_path) == target_norm: return node_id if not node_path or not os.path.isdir(node_path): return None try: if not os.path.normcase(target_abs).startswith(os.path.normcase(os.path.abspath(node_path))): return None except ValueError: return None populate_tree_node(node_id) for child in explorer_tree.get_children(node_id): result = search(child) if result: explorer_tree.item(node_id, open=True) return result return None for root in explorer_tree.get_children(""): match = search(root) if match: explorer_tree.selection_set(match) explorer_tree.focus(match) explorer_tree.see(match) break # タグの設定 text_area.tag_configure("magenta", foreground="#da70d6") text_area.tag_configure("orange", foreground="#d2691e") text_area.tag_configure("oranges", foreground="#d2691e") text_area.tag_configure("orangese", foreground="#ff8c00") text_area.tag_configure("green", foreground="#228b22") text_area.tag_configure("greens", foreground="#228b22") text_area.tag_configure("blue", foreground="#0000ff") # 特定の文字列をハイライトする関数 # magenta ハイライト def highlight_pattern(pattern, tag="magenta"): start = "1.0" while True: pos = text_area.search(pattern, start, tk.END) if not pos: break end_pos = f"{pos}+{len(pattern)}c" text_area.tag_add(tag, pos, end_pos) start = end_pos text_area.tag_config(tag, foreground="magenta") # orange ハイライト def highlight_quoted_text(): text_area.tag_remove("oranges", "1.0", tk.END) text = text_area.get("1.0", tk.END) for match in re.finditer(r'"[^"]*"', text): start = text_area.index(f"1.0+{match.start()}c") end = text_area.index(f"1.0+{match.end()}c") text_area.tag_add("oranges", start, end) def highlight_quoted_text_single(): text_area.tag_remove("orange", "1.0", tk.END) text = text_area.get("1.0", tk.END) for match in re.finditer(r"'[^']*'", text): start = text_area.index(f"1.0+{match.start()}c") end = text_area.index(f"1.0+{match.end()}c") text_area.tag_add("orange", start, end) # green ハイライト def highlight_comennt(): text_area.tag_remove("green", "1.0", tk.END) text = text_area.get("1.0", tk.END) for match in re.finditer(r'/\*.*?\*/', text, re.DOTALL): start = text_area.index(f"1.0+{match.start()}c") end = text_area.index(f"1.0+{match.end()}c") text_area.tag_add("green", start, end) def highlight_single_line_comments(): text_area.tag_remove("greens", "1.0", tk.END) text = text_area.get("1.0", tk.END) for match in re.finditer(r'//.*', text): start = text_area.index(f"1.0+{match.start()}c") end = text_area.index(f"{start} lineend") text_area.tag_add("greens", start, end) # orangese ハイライト def highlight_escape_sequences(): text_area.tag_remove("orangese", "1.0", tk.END) text = text_area.get("1.0", tk.END) escape_sequences = [ r'\\a', r'\\b', r'\\f', r'\\n', r'\\r', r'\\t', r'\\v', r'\\0', r'\\\\', r'\\?', r"\\'", r'\\"', r'\\[0-7]{1,3}', r'\\x[0-9A-Fa-f]{1,2}' ] pattern = '|'.join(escape_sequences) for match in re.finditer(pattern, text): start = text_area.index(f"1.0+{match.start()}c") end = text_area.index(f"1.0+{match.end()}c") text_area.tag_add("orangese", start, end) # blue ハイライト def highlight_yoyaku_sequences(): text_area.tag_remove("blue", "1.0", tk.END) text = text_area.get("1.0", tk.END) yoyaku_sequences = [ r'\bauto\b', r'\bbreak\b', r'\bcase\b', r'\bchar\b', r'\bconst\b', r'\bcontinue\b', r'\bdefault\b', r'\bdo\b', r'\bdouble\b', r'\belse\b', r'\benum\b', r'\bextern\b', r'\bfloat\b', r'\bfor\b', r'\bgoto\b', r'\bif\b', r'\bint\b', r'\blong\b', r'\bregister\b', r'\breturn\b', r'\bshort\b', r'\bsigned\b', r'\bsizeof\b', r'\bstatic\b', r'\bstruct\b', r'\bswitch\b', r'\btypedef\b', r'\bunion\b', r'\bunsigned\b', r'\bvoid\b', r'\bvolatile\b', r'\bwhile\b' ] pattern = '|'.join(yoyaku_sequences) for match in re.finditer(pattern, text): start = text_area.index(f"1.0+{match.start()}c") end = text_area.index(f"1.0+{match.end()}c") text_area.tag_add("blue", start, end) # テキスト変更時にハイライトを更新する関数 def on_text_change(event): text_area.tag_remove("magenta", "1.0", tk.END) highlight_pattern("#include") highlight_pattern("#define") highlight_quoted_text() #oranges highlight_quoted_text_single() #orange highlight_escape_sequences() #orangese highlight_comennt() #green highlight_single_line_comments() #greens highlight_yoyaku_sequences() #blue text_area.edit_modified(False) def update_line_numbers(event=None): line_numbers = '' # テキストエリアの行数を取得 line_count = int(text_area.index('end-1c').split('.')[0]) for i in range(1, line_count + 1): line_numbers += str(i) + '\n' # 行番号テキストウィジェットを更新 line_number_bar.config(state='normal') line_number_bar.delete('1.0', 'end') line_number_bar.insert('1.0', line_numbers) line_number_bar.config(state='disabled') sync_line_number_view() # テキストエリアの変更を監視するイベントバインド text_area.bind('', update_line_numbers) text_area.bind('', update_line_numbers) text_area.bind('', update_line_numbers) text_area.bind('', update_line_numbers) # 初期の行番号を表示 update_line_numbers() # 行文字カウンター def update_status(event): line_no = text_area.index(tk.INSERT).split('.')[0] char_no = text_area.index(tk.INSERT).split('.')[1] status_label['text'] = f"Line: {line_no}, Char: {char_no}" def update_status_click(event): line_no = text_area.index(tk.INSERT).split('.')[0] char_no = text_area.index(tk.INSERT).split('.')[1] status_label['text'] = f"Line: {line_no}, Char: {char_no}" def update_status_wheel(event): line_no = text_area.index(tk.INSERT).split('.')[0] char_no = text_area.index(tk.INSERT).split('.')[1] status_label['text'] = f"Line: {line_no}, Char: {char_no}" # 文字サイズ変更機能 def change_font_size(event): try: font_size = int(font_size_entry.get()) text_area.config(font=("Helvetica", font_size)) line_number_bar.config(font=("Helvetica", font_size)) update_tab_width(font_size) except ValueError: pass #エラー表示 def show_message(message): messagebox.showerror("エラー", message) root = tk.Tk() root.withdraw() frame = tk.Frame(root) frame.pack() frame.master.title("コピペ用ウィンドウ") text_widget = tk.Text(root) text_widget.insert(tk.END, message) text_widget.pack() root.deiconify() root.mainloop() overwrite_path_file = "overwrite_path.txt" def update_window_title(path): if path: window.title(f"{window_title_base} - {os.path.basename(path)}") else: window.title(window_title_base) def get_initial_directory(): if current_folder_path and os.path.isdir(current_folder_path): return current_folder_path if current_file_path: candidate = os.path.dirname(current_file_path) if os.path.isdir(candidate): return candidate return os.getcwd() def set_current_file(path): nonlocal current_file_path normalized = os.path.abspath(path) if path else None current_file_path = normalized update_window_title(normalized) label_text = f"現在開いているファイル: {normalized}" if normalized else "現在開いているファイル: (未選択)" if overwrite_path_label is not None: overwrite_path_label.config(text=label_text) try: with open(overwrite_path_file, "w", encoding="utf-8") as path_record: path_record.write(normalized or "") except OSError: pass if normalized and (current_folder_path is None or not is_subpath(normalized, current_folder_path)): build_tree(os.path.dirname(normalized)) select_tree_item_for_path(normalized) def open_file_dialog(): file_path = filedialog.askopenfilename( initialdir=get_initial_directory(), title="ファイルを開く", filetypes=[("C ファイル", "*.c"), ("すべてのファイル", "*.*")] ) if file_path and load_file_into_editor(file_path): set_current_file(file_path) text_area.focus_set() def save_file_as(): file_path = filedialog.asksaveasfilename( initialdir=get_initial_directory(), title="名前を付けて保存", defaultextension=".c", filetypes=[("C ファイル", "*.c"), ("すべてのファイル", "*.*")] ) if file_path and save_editor_content(file_path): set_current_file(file_path) text_area.focus_set() def save_file(): if current_file_path: if save_editor_content(current_file_path): set_current_file(current_file_path) else: save_file_as() def open_folder_dialog(): folder_path = filedialog.askdirectory( initialdir=get_initial_directory(), title="フォルダを選択" ) if folder_path: build_tree(folder_path) select_tree_item_for_path(current_file_path) def on_tree_open(event): node_id = explorer_tree.focus() if node_id: populate_tree_node(node_id) def on_tree_double_click(event): node_id = explorer_tree.identify_row(event.y) if not node_id: return node_path = explorer_tree.set(node_id, "fullpath") node_type = explorer_tree.set(node_id, "type") if node_type == "directory": is_open = explorer_tree.item(node_id, "open") explorer_tree.item(node_id, open=not is_open) if not is_open: populate_tree_node(node_id) elif node_type == "file" and node_path: if load_file_into_editor(node_path): set_current_file(node_path) text_area.focus_set() file_open_button = tk.Button(button_frame, text="ファイルを開く", command=open_file_dialog, bg='white') file_open_button.pack(side=tk.LEFT, padx=(10, 4)) file_save_button = tk.Button(button_frame, text="保存", command=save_file, bg='white') file_save_button.pack(side=tk.LEFT, padx=4) file_save_as_button = tk.Button(button_frame, text="名前を付けて保存", command=save_file_as, bg='white') file_save_as_button.pack(side=tk.LEFT, padx=4) folder_open_button = tk.Button(button_frame, text="フォルダを開く", command=open_folder_dialog, bg='white') folder_open_button.pack(side=tk.LEFT, padx=4) #実行 def compile_and_run(filename): output_path = os.path.abspath(f"{filename}.c") if not save_editor_content(output_path): return set_current_file(output_path) command = f'gcc "{output_path}"' try: subprocess.run(command, shell=True, check=True, capture_output=True, text=True) try: subprocess.run('start /WAIT cmd /C c.bat', shell=True) except Exception as e: show_message(f"実行中にエラーが発生しました:\n\n{str(e)}") except subprocess.CalledProcessError as e: show_message(f"コンパイルでエラーが発生しました:\n\n{e.stderr}") def save_to_file(): filename = simpledialog.askstring("保存", "保存するファイル名を入力してください:") if filename: threading.Thread(target=compile_and_run, args=(filename,)).start() #保存無し実行 def compile_and_normal_run(): output_path = os.path.abspath("output.c") if not save_editor_content(output_path): return command = f'gcc "{output_path}"' try: subprocess.run(command, shell=True, check=True, capture_output=True, text=True) try: subprocess.run('start /WAIT cmd /C c.bat', shell=True) except Exception as e: show_message(f"実行中にエラーが発生しました:\n\n{str(e)}") except subprocess.CalledProcessError as e: show_message(f"コンパイルでエラーが発生しました:\n\n{e.stderr}") def run_to_file(): threading.Thread(target=compile_and_normal_run).start() #上書き実行 def compile_and_overwrite_run(): try: with open(overwrite_path_file, 'r', encoding='utf-8') as f: overwrite_path = f.read().strip() except FileNotFoundError: overwrite_path = "" if not overwrite_path: show_message("上書き対象のファイルがありません。") return overwrite_path = os.path.abspath(overwrite_path) set_current_file(overwrite_path) if not save_editor_content(overwrite_path): return command = f'gcc "{overwrite_path}"' try: subprocess.run(command, shell=True, check=True, capture_output=True, text=True) try: subprocess.run('start /WAIT cmd /C c.bat', shell=True) except Exception as e: show_message(f"実行中にエラーが発生しました:\n\n{str(e)}") except subprocess.CalledProcessError as e: show_message(f"コンパイルでエラーが発生しました:\n\n{e.stderr}") def run_to_ow_file(): threading.Thread(target=compile_and_overwrite_run).start() #アプリについて def app_info(): info = tk.Toplevel(window) info.title("このアプリについて") info.resizable(False, False) tk.Label(info, text="このアプリは python3.12 で作られたCスクリプト実行アプリです。\n最低限 + ちょっとの優しさぐらいの機能しかありません\n\nこのアプリのスクリプトは'Crun.py'に書かれてあります\nその中身を変えることで自由にアプリを改造できます\n\nなお C Runner のインストーラーでは コンパイルファイルのpath指定を行っているので、vscodeやcmdでの実行ができるようになっているはずです(たぶん)。\n\nそれでは、良いc言語ライフを\n\nver:1.0\n\n\n\n\n\nエクスプローラー機能、ファイル参照機能、フォルダ読み込み機能などなどいろいろ追加しました。(2025年10月20日)\n\nver:2.0", anchor="w", justify="left").pack(anchor="w", padx=12, pady=(12, 4)) link = tk.Label(info, text="公式サイトを見る", fg="blue", cursor="hand2") link.pack(anchor="w", padx=12, pady=(0, 12)) def open_site(event=None): webbrowser.open("https://slrte.com/slrte/index.html") link.bind("", open_site) #簡単文法まとめ def bunpo(): bunpo_window = tk.Toplevel() bunpo_window.title("簡単文法まとめ") text_widget = tk.Text(bunpo_window, wrap="word") text_widget.pack(expand=True, fill="both") scrollbar = tk.Scrollbar(bunpo_window, command=text_widget.yview) scrollbar.pack(side="right", fill="y") text_widget.config(yscrollcommand=scrollbar.set) mes = ( '【型の種類】\n\n"bool" "char" "int" "short" "long" "float" "double" の7種類の型がある\n' 'bool については、 をインクルードすることで使用可能になる\n' 'stdbool.h をインクルードすると、true と false も使用可能になる\n' 'stdbool.h をインクルードしない場合、_Boolがboolを意味し、0がfalse、0以外がtrueとして扱われる(0以外→1(真)に変換される)\n' '条件式として評価される変数や式が 0 以外であれば真(true)として扱われます\n\n\n\n' '【変換指定子】\n\n変換指定子\t\t対応する型\t\t\t意味\n' '%c \t\tchar \t\t\t 1文字だけ入出力\n' '%s \t\tchar(配列)\t\t\t 文字列を入出力\n' '%d \t\tint \t\t\t 整数を10進数として入出力\n' '%ld\t\tlong \t\t\t 倍精度整数を10進数として入出力\n' '%u \t\tunsigned int \t\t\t 符号なし整数を10進数として入出力\n' '%lu\t\tunsigned long \t\t\t 符号なし倍精度整数を10進数として入出力\n' '%o \t\tint, short, unsigned int 整数を8進数として入出力\n' '%lo\t\tlong, unsigned long 倍精度整数を8進数として入出力\n' '%x \t\tint, unsigned int 整数を16進数として入出力\n' '%lx\t\tlong, unsigned long 倍精度整数を16進数として入出力\n' '%f \t\tfloat \t\t\t 実数を入出力\n' '%lf\t\tdouble \t\t\t 倍精度実数を入出力\n' '%hd\t\tshort \t\t\t 単精度整数を10進数として入力\n' '%hu\t\tunsigned short \t\t\t 符号なし単精度整数を10進数として入力\n' '%e \t\tfloat \t\t\t 実数を指数表示で出力\n' '%g \t\tfloat \t\t\t 実数を最適な形式で出力\n' '%p \t\t\t\t\t アドレスを16進数で出力\n\n\n\n' '【入出力】\n\n入力するには\nscanf("変換指定子",&変数);\n\n出力するには\nprintf("変換指定子",変数);\n\n\n\n' '【四則演算+剰余】\n\n' 'c = a + b; // 足し算\n' 'c = a - b; // 引き算\n' 'c = a * b; // 掛け算\n' 'f = a / b; // 割り算\n' 'c = a % 2; // 変数aを2で割った余り(整数型のみ使用可)\n' '演算形式には省略形もある "a += b" (a=a+b と同じ意味)\n\n' '1足すなら a++;\n' '1引くなら a--;\n\n\n\n' '【乱数】\n\n' 'stdlib.h を使い\n' 'rand()で疑似乱数を生成できる\n\n' 'しかし、rand()だけだと何度実行しても同じ出方をするので\n' 'srand()で初期値を設定する\n' 'この初期値を time.h の time() を使うことで実行するタイミングによって初期値が毎回異なるようになる\n' 'srand((unsigned)time(NULL))\n\n\n\n' '【ループ処理 - for】\n\n' 'for( 初期値; ループする条件; ループ毎に行う処理){\n\t処理;\n}\n\n\n\n' '【ループ処理 - while】\n\n' 'while(条件式)が基本\n' 'ループする判定を前にするか後ろにするかで書き方が変わる\n\n' '前判定\nwhile(条件式){\n\t処理;\n}\n\n' '後判定\ndo{\n\t処理;\n}while(条件式);\n\n\n\n' '【if】\n\n' 'if(条件式){\n\t処理;\n}\n' 'else if(条件式){\n\t処理;\n}\n' 'else{\n\t処理;\n}\n\n' 'の三種類があり、一番最初に "if"が判定され、当てはまらなければ "else if"が判定され、それにも果てはまらなければ "else"の処理が行われる\n\n' '条件式に用いられる比較には、<、>、<=、>=、==、!=の6種類あり、不等号は必ずイコールの左側に書く\n\n\n\n' '【switch】\n\n' '"if"と同じ条件分岐だが、一つの変数に対して複数の条件分岐を行うときに使う\n\n' 'switch(変数名){\n\tcase 0:\n\t\t処理;\n\t\tbreak;\n\t' 'case 1:\n\t\t処理;\n\t\tbreak;\n\t' 'case 2:\n\t\t処理;\n\t\tbreak;\n\t' 'default:\n\t\t処理;\n}\n\n' 'この "case" というのはその判定する変数の中身がそれだったとき\n' '例でいうと\n\tcase 0: は変数の中身が 0 だったとき' '\n\tcase 1: は変数の中身が 1 だったときに処理を行うということ\n\n' 'default: はどの"case"にも当てはまらなかったときの処理' ) text_widget.insert("1.0", mes) text_widget.config(state="disabled") def sample_script(): sample_window = tk.Toplevel() sample_window.title("サンプルコード") text_widget = tk.Text(sample_window, wrap="word") text_widget.pack(expand=True, fill="both") scrollbar = tk.Scrollbar(sample_window, command=text_widget.yview) scrollbar.pack(side="right", fill="y") text_widget.config(yscrollcommand=scrollbar.set) mes = ( '【Hello World!】\n\n' '#include \nint main (void)\n{\n\tprintf("Hello World!");\n\treturn 0;\n}\n\n\n\n' '【四則演算】\n\n' '#include \nint main (void)\n{\n' '\tchar op;\n\tint num1,num2;\n' '\tprintf("演算式を入力してください 例:〇 + 〇 :");\n\tscanf("%d %c %d",&num1,&op,&num2);\n' '\tswitch(op)\n\t{\n' "\t\tcase '+':\n" '\t\t\tprintf("%d + %d = %d",num1,num2,num1+num2);\n\t\t\tbreak;\n' "\t\tcase '-':\n" '\t\t\tprintf("%d - %d = %d",num1,num2,num1-num2);\n\t\t\tbreak;\n' "\t\tcase '*':\n" '\t\t\tprintf("%d * %d = %d",num1,num2,num1*num2);\n\t\t\tbreak;\n' "\t\tcase '/':\n" '\t\t\tprintf("%d / %d = %d",num1,num2,num1/num2);\n\t\t\tbreak;\n' '\t\tdefault:\n' '\t\t\tprintf("演算子が違います + or - or * or / です");\n' '\t}\n\treturn 0;\n}\n\n\n\n' '【キーを押すと数字が増減するプログラム】\n\n' '#include \n#include \n#include \n' 'int main ()\n{\n\tint n=0,j=0;\n\tprintf("\\nキーを押すと数字が増減します\\n");\n\t' 'printf("+:増, -:減, Esc:終了\\n");\n\twhile(1){\n\t\t' 'printf("\\r%06d",n); fflush(stdout);\n\t\tj=getch( );\n\t\tBeep(j*10,100);\n\t\t' 'if(j==43){\n\t\t\tn++;\n\t\t\tcontinue;\n\t\t}\n\t\t' 'if(j==45){\n\t\t\tn--;\n\t\t\tcontinue;\n\t\t}\n\t\t' 'if(j==27)break;\n\t}\n\tprintf("\\n");\n\treturn 0;\n}' ) text_widget.insert("1.0", mes) text_widget.config(state="disabled") #実行 save_button = tk.Button(button_frame, text="実行", command=save_to_file, bg='green', fg='white') save_button.pack(side=tk.RIGHT) #上書き実行 run_ow_button = tk.Button(button_frame, text="上書き実行", command=run_to_ow_file, bg='green', fg='white') run_ow_button.pack(side=tk.RIGHT) #保存無し実行 run_button = tk.Button(button_frame, text="保存せず実行", command=run_to_file, bg='green', fg='white') run_button.pack(side=tk.RIGHT) #アプリインフォメーション app_info_button = tk.Button(button_frame, text="このアプリについて", command=app_info, bg='white', fg='blue') app_info_button.pack(side=tk.RIGHT) #簡単文法まとめ c_bunpo = tk.Button(button_frame, text="簡単文法まとめ", command=bunpo, bg='white', fg='red') c_bunpo.pack(side=tk.RIGHT) #サンプルコード c_sample_script = tk.Button(button_frame, text="サンプルコード", command=sample_script, bg='white', fg='green') c_sample_script.pack(side=tk.RIGHT) # 上書きパス表示ラベルを作成 overwrite_path_label = tk.Label(button_frame, text="現在開いているファイル: (未選択)") overwrite_path_label.pack(side=tk.RIGHT) try: with open(overwrite_path_file, "w", encoding="utf-8") as path_record: path_record.write("") except OSError: pass build_tree(os.getcwd()) explorer_tree.bind("<>", on_tree_open) explorer_tree.bind("", on_tree_double_click) explorer_tree.bind("", on_tree_double_click) def bind_shortcut(sequence, callback): def handler(event): callback() return "break" window.bind(sequence, handler) bind_shortcut("", open_file_dialog) bind_shortcut("", save_file) bind_shortcut("", save_file_as) # 関数を入力フィールドにバインドします font_size_entry.bind("", change_font_size) # タブ変換 def tab_to_spaces(event): try: tab_width = int(text_area.cget("tabs")[0].__str__()) start = text_area.index("sel.first") end = text_area.index("sel.last") lines = text_area.get(start, end).split('\n') for i, line in enumerate(lines): lines[i] = ' ' * tab_width + line text_area.delete(start, end) text_area.insert(start, '\n'.join(lines)) except TclError: # Normal tab operation text_area.insert(text_area.index("insert"), ' ' * tab_width) return "break" # タブ詰め def shift_tab_to_spaces(event): try: tab_width = int(text_area.cget("tabs")[0].__str__()) start = text_area.index("sel.first") end = text_area.index("sel.last") lines = text_area.get(start, end).split('\n') for i, line in enumerate(lines): if line.startswith(' ' * tab_width): lines[i] = line[tab_width:] text_area.delete(start, end) text_area.insert(start, '\n'.join(lines)) except TclError: # Shift tab operation current_line = text_area.get("insert linestart", "insert lineend") spaces = len(current_line) - len(current_line.lstrip()) if spaces >= int(tab_width): text_area.delete("insert linestart", f"insert linestart+{tab_width}c") return "break" # タブ幅を更新する関数 def update_tab_width(font_size): # フォントサイズに応じたタブ幅を計算(ここではフォントサイズの半分をタブ幅としています) tab_width = font_size // 2 text_area.config(tabs=(tab_width,)) # ctrlZ def undo(event): text_area.edit_undo() return "break" # ctrlY def redo(event): try: text_area.edit_redo() except tk.TclError as e: a = 1+1 return "break" # ctrlZ text_area.bind("", undo) # ctrlY text_area.bind("", redo) # タブ変換 text_area.bind("", tab_to_spaces) # タブ詰め text_area.bind("", shift_tab_to_spaces) # ここで使用する初期フォントサイズを設定 update_tab_width(12) def on_text_modified(event): on_text_change(event) update_line_numbers() # テキストエリアの変更を監視するイベントバインド text_area.bind("<>", on_text_modified) on_text_change(None) # 行文字カウンター text_area.bind('', update_status) text_area.bind('', update_status_click) text_area.bind('', update_status_click) text_area.bind('', update_status_wheel) window.mainloop() if __name__ == "__main__": create_editor()