diff --git a/.gitignore b/.gitignore index 201f1e3..4127168 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,7 @@ build/ *.bak *.old +out/ -out/ \ No newline at end of file +.mypy_cache +tkinterdnd2 \ No newline at end of file diff --git a/lib/__pycache__/pyboard.cpython-39.pyc b/lib/__pycache__/pyboard.cpython-39.pyc deleted file mode 100644 index 34e3b71..0000000 Binary files a/lib/__pycache__/pyboard.cpython-39.pyc and /dev/null differ diff --git a/lib/pybfm.py b/lib/pybfm.py index 92958b2..7087947 100644 --- a/lib/pybfm.py +++ b/lib/pybfm.py @@ -1,215 +1,224 @@ -#!/usr/bin/env python3 - -from tkinter import * -from tkinterdnd2 import * -import os -import sys -import pyboard -import serial.tools.list_ports -from tkinter import filedialog - -##### 引数 ##### -args = sys.argv -try: - dev = args[1] -except IndexError as err: - dev = False -else: - dev = (dev=='-f') - -##### グローバル変数 ##### -gport = None - -##### イベントハンドラ ##### -# リストボックスにドロップ -def listbox_drop(event): - files = listbox.tk.splitlist(event.data) - putfiles(files) -# キーボード -def input_key(ev): - if ev.keysym == 'F5': - reload() - elif ev.keysym == 'Delete': - rmfiles() -# ダブルクリック -def dbl_click(ev): - run() -# 右クリックメニュー -def pop_menu(ev): - listbox.select_clear(0, END) - listbox.select_set(listbox.nearest(ev.y)) - pmenu.post(ev.x_root, ev.y_root) - -##### pyboard関数 ##### -# 接続・repl開始 -def connect(): - global gport - try: - pyb = pyboard.Pyboard(gport) - except pyboard.PyboardError as err: - gport = None - listbox.delete(0, END) - set_title('Lost device : Please reload') - raise pyboard.PyboardError('Lost device') - pyb.enter_raw_repl(False) - return pyb -# repl終了・切断 -def disconnect(pyb): - try: - pyb.exit_raw_repl() - except Exception as err: - pass - try: - pyb.close() - except Exception as err: - pass -# ファイル送信 (複数可)(files: list of local filepath) -def putfiles(files): - pyb = connect() - for src in files: - if os.path.exists(src): - if os.path.isfile(src): - dest = os.path.basename(src) - print('ファイル "%s" を転送' % src) - pyb.fs_put(src, dest) - else: - print('フォルダは転送できません : %s' % src) - else: - print('ファイル "%s" は見つかりません。' % f) - ls(pyb) - disconnect(pyb) -# ポートスキャン:アルファベット順で一番若いポートを返す -def find_device(): - global gport - for p in sorted(serial.tools.list_ports.comports()): - if p.hwid.startswith('USB'): - gport = p.device - set_title(gport) - break - else: - gport = None - set_title('No device') - raise OSError('Device not found') -# 再読込:ここのみデバイスの再スキャンが入る -def reload(): - if not gport: - find_device() - if gport: - listfiles() -# ファイルリスト取得だけを行う -def listfiles(): - pyb = connect() - ls(pyb) - disconnect(pyb) -# ファイルリストを取得し、リストボックスに表示 (pyb: pyboard handle, src: target directory on device) -def ls(pyb, src='/'): - cmd = ( - "import uos\nfor f in uos.listdir(%s):\n" - " print(f)" - % (("'%s'" % src) if src else "") - ) - retval = pyb.exec(cmd) - files = retval.decode('utf-8').splitlines() - listbox.delete(0, END) - for f in files: - listbox.insert(END, f) -# ウィンドウタイトル (title: string) -def set_title(title): - root.title('PyBfm - ' + title) -# デバイス上のファイルを実行 -def run(follow=False): - selected = listbox.curselection() - if len(selected): - src = listbox.get(selected[0]) - ext = os.path.splitext(src)[1] - if ext == '.py': - cmd = 'exec(open("%s").read())' % src - pyb = connect() - try: - if follow: - print (pyb.exec(cmd).decode('utf-8')) - else: - pyb.exec_raw_no_follow(cmd) - except Exception as err: - print("Runtime error.") - print('Done.') - disconnect(pyb) - else: - print('このファイルは実行できません') -# ファイル選択ダイアログからファイル送信 -def putdlg(): - fpath = filedialog.askopenfilename() - if fpath: - putfiles([fpath]) -# ファイル受信 -def getfile(): - selected = listbox.curselection() - if len(selected): - src = listbox.get(selected[0]) - ext = os.path.splitext(src)[1] - dest = filedialog.asksaveasfilename( - initialfile=src, - defaultextension=ext, - filetypes=[('変更なし', ext), ('全てのファイル', '.*')] - ) - if dest: - pyb = connect() - pyb.fs_get(src, dest) - disconnect(pyb) -# デバイス上のファイル削除(複数可) -def rmfiles(): - selected = listbox.curselection() - if len(selected): - pyb = connect() - for i in selected: - src = listbox.get(i) - pyb.fs_rm(src) - print('Deleted %s' % src) - ls(pyb) - disconnect(pyb) -# 何もしない関数 -def do_nothing(): - pass - -##### メイン ##### - -# メインウィンドウの生成 -root = TkinterDnD.Tk() -root.title('PyBfm') -root.geometry('400x300') -pmenu = Menu(root, tearoff=0) -pmenu.add_command(label="実行", command=run) -if dev: - pmenu.add_command(label="実行 (追跡)", command=lambda:run(True)) -pmenu.add_command(label="送る", command=putdlg) -pmenu.add_command(label="取得", command=getfile) -pmenu.add_command(label="削除", command=rmfiles) -pmenu.add_command(label="再読込", command=reload) -pmenu.add_command(label="閉じる", command=do_nothing) -# root.config(bg='#cccccc') -# Frameウィジェットの生成 -frame = Frame(root) -# Listboxウィジェットの生成 -listbox = Listbox(frame, selectmode=EXTENDED) -listbox.drop_target_register(DND_FILES) -listbox.dnd_bind('<>', listbox_drop) -listbox.bind("", input_key) -listbox.bind("", dbl_click) -listbox.bind("", pop_menu) -# スクロールバーの生成 -scroll = Scrollbar(frame, orient=VERTICAL) -listbox.configure(yscrollcommand=scroll.set) -scroll.config(command=listbox.yview) -# ウィジェットの配置 -frame.pack(expand=True,fill=BOTH) -listbox.pack(expand=True,fill=BOTH, side=LEFT) -scroll.pack(side=RIGHT, fill=Y) - -try: - find_device() -except Exception as err: - pass -else: - listfiles() - +#!/usr/bin/env python3 + +from tkinter import * +try: + from tkinterdnd2 import * +except ImportError: + dnd = False +else: + dnd = True +import os +import sys +import pyboard +import serial.tools.list_ports +from tkinter import filedialog + +##### 引数 ##### +args = sys.argv +try: + dev = args[1] +except IndexError as err: + dev = False +else: + dev = (dev=='-f') + +##### グローバル変数 ##### +gport = None + +##### イベントハンドラ ##### +# リストボックスにドロップ +def listbox_drop(event): + files = listbox.tk.splitlist(event.data) + putfiles(files) +# キーボード +def input_key(ev): + if ev.keysym == 'F5': + reload() + elif ev.keysym == 'Delete': + rmfiles() +# ダブルクリック +def dbl_click(ev): + run() +# 右クリックメニュー +def pop_menu(ev): + listbox.select_clear(0, END) + listbox.select_set(listbox.nearest(ev.y)) + pmenu.post(ev.x_root, ev.y_root) + +##### pyboard関数 ##### +# 接続・repl開始 +def connect(): + global gport + try: + pyb = pyboard.Pyboard(gport) + except pyboard.PyboardError as err: + gport = None + listbox.delete(0, END) + set_title('Lost device : Please reload') + raise pyboard.PyboardError('Lost device') + pyb.enter_raw_repl(False) + return pyb +# repl終了・切断 +def disconnect(pyb): + try: + pyb.exit_raw_repl() + except Exception as err: + pass + try: + pyb.close() + except Exception as err: + pass +# ファイル送信 (複数可)(files: list of local filepath) +def putfiles(files): + pyb = connect() + for src in files: + if os.path.exists(src): + if os.path.isfile(src): + dest = os.path.basename(src) + print('ファイル "%s" を転送' % src) + pyb.fs_put(src, dest) + else: + print('フォルダは転送できません : %s' % src) + else: + print('ファイル "%s" は見つかりません。' % f) + ls(pyb) + disconnect(pyb) +# ポートスキャン:アルファベット順で一番若いポートを返す +def find_device(): + global gport + for p in sorted(serial.tools.list_ports.comports()): + if p.hwid.startswith('USB'): + gport = p.device + set_title(gport) + break + else: + gport = None + set_title('No device') + raise OSError('Device not found') +# 再読込:ここのみデバイスの再スキャンが入る +def reload(): + if not gport: + find_device() + if gport: + listfiles() +# ファイルリスト取得だけを行う +def listfiles(): + pyb = connect() + ls(pyb) + disconnect(pyb) +# ファイルリストを取得し、リストボックスに表示 (pyb: pyboard handle, src: target directory on device) +def ls(pyb, src='/'): + cmd = ( + "import uos\nfor f in uos.listdir(%s):\n" + " print(f)" + % (("'%s'" % src) if src else "") + ) + retval = pyb.exec(cmd) + files = retval.decode('utf-8').splitlines() + listbox.delete(0, END) + for f in files: + listbox.insert(END, f) +# ウィンドウタイトル (title: string) +def set_title(title): + root.title('PyBfm - ' + title) +# デバイス上のファイルを実行 +def run(follow=False): + selected = listbox.curselection() + if len(selected): + src = listbox.get(selected[0]) + ext = os.path.splitext(src)[1] + if ext == '.py': + cmd = 'exec(open("%s").read())' % src + pyb = connect() + try: + if follow: + print (pyb.exec(cmd).decode('utf-8')) + else: + pyb.exec_raw_no_follow(cmd) + except Exception as err: + print("Runtime error.") + print('Done.') + disconnect(pyb) + else: + print('このファイルは実行できません') +# ファイル選択ダイアログからファイル送信 +def putdlg(): + fpath = filedialog.askopenfilename() + if fpath: + putfiles([fpath]) +# ファイル受信 +def getfile(): + selected = listbox.curselection() + if len(selected): + src = listbox.get(selected[0]) + ext = os.path.splitext(src)[1] + dest = filedialog.asksaveasfilename( + initialfile=src, + defaultextension=ext, + filetypes=[('変更なし', ext), ('全てのファイル', '.*')] + ) + if dest: + pyb = connect() + pyb.fs_get(src, dest) + disconnect(pyb) +# デバイス上のファイル削除(複数可) +def rmfiles(): + selected = listbox.curselection() + if len(selected): + pyb = connect() + for i in selected: + src = listbox.get(i) + pyb.fs_rm(src) + print('Deleted %s' % src) + ls(pyb) + disconnect(pyb) +# 何もしない関数 +def do_nothing(): + pass + +##### メイン ##### + +# メインウィンドウの生成 +if dnd: + root = TkinterDnD.Tk() +else: + root = Tk() +root.title('PyBfm') +root.geometry('400x300') +pmenu = Menu(root, tearoff=0) +pmenu.add_command(label="実行", command=run) +if dev: + pmenu.add_command(label="実行 (追跡)", command=lambda:run(True)) +pmenu.add_command(label="送る", command=putdlg) +pmenu.add_command(label="取得", command=getfile) +pmenu.add_command(label="削除", command=rmfiles) +pmenu.add_command(label="再読込", command=reload) +pmenu.add_command(label="閉じる", command=do_nothing) +# root.config(bg='#cccccc') +# Frameウィジェットの生成 +frame = Frame(root) +# Listboxウィジェットの生成 +listbox = Listbox(frame, selectmode=EXTENDED) +if dnd: + listbox.drop_target_register(DND_FILES) + listbox.dnd_bind('<>', listbox_drop) +listbox.bind("", input_key) +listbox.bind("", dbl_click) +listbox.bind("", pop_menu) +# スクロールバーの生成 +scroll = Scrollbar(frame, orient=VERTICAL) +listbox.configure(yscrollcommand=scroll.set) +scroll.config(command=listbox.yview) +# ウィジェットの配置 +frame.pack(expand=True,fill=BOTH) +listbox.pack(expand=True,fill=BOTH, side=LEFT) +scroll.pack(side=RIGHT, fill=Y) + +try: + find_device() +except Exception as err: + pass +else: + listfiles() + root.mainloop() \ No newline at end of file