From e92fbfc6edf41cb42a0da7b38340ec31ef973feb Mon Sep 17 00:00:00 2001 From: ocogeclub Date: Thu, 11 Aug 2022 20:33:56 +0900 Subject: [PATCH] =?UTF-8?q?[update]=20pybfm.py=20=E3=82=92=20TkinterDnD2?= =?UTF-8?q?=20=E3=81=8C=E3=81=AA=E3=81=8F=E3=81=A6=E3=82=82=E8=B5=B7?= =?UTF-8?q?=E5=8B=95=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- lib/__pycache__/pyboard.cpython-39.pyc | Bin 19388 -> 0 bytes lib/pybfm.py | 437 +++++++++++++------------ 3 files changed, 226 insertions(+), 215 deletions(-) delete mode 100644 lib/__pycache__/pyboard.cpython-39.pyc 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 34e3b7155b72e01ef454edd9e5d3600c06f83216..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19388 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

R?_Ig~Ux-s#Zu|$ka`n7>@!j1NoPo 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 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