[Python] exe化ツール v0.0.1 全標準出力の取得

pyinstallerを使ってソースコードから実行ファイルを作成するツールです。

標準出力を全て取得してTextウィジェットに表示するところで手間取りました。

標準出力を1行ずつ取得してリストにしたものをそのまま表示しているため、波カッコが目障りです。できれば消去したいです。

import subprocess,datetime,os,sys,shutil,glob
import tkinter as tk
import tkinter.font as font
from tkinter import ttk

# 16進数カラーコード
# #EDFFBE #C2EEFF #7B3CFF

class FrameA(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.configure(background = '#b0e0e6')
        self.grid(row=1,column=2, sticky=tk.NSEW, padx=5, pady=5)

class FrameB(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.configure(background = '#C2EEFF',borderwidth=0)
        self.grid(row=1,column=1, sticky=tk.NSEW, padx=5, pady=5)
        # Text設定
        self.text = tk.Text(self,height=23,width=45,highlightbackground="#f0f8ff",background = '#C2EEFF',foreground = '#8b0000')
        # ハイライトを消す場合
        # self.text = tk.Text(self,height=20,width=45,highlightthickness=0,background = '#C2EEFF',foreground = '#8b0000')

        # Y方向スクロールバーの設定
        self.scrollable_frame = ttk.Frame(self.text)
        self.scrollbar_y = ttk.Scrollbar(self, orient="vertical", command=self.text.yview,style= 'Vertical.TScrollbar')
        self.scrollbar_y.pack(side=tk.RIGHT, fill="y")

        self.text.configure(yscrollcommand=self.scrollbar_y.set)
        self.text.pack(side=tk.LEFT, fill="both", expand=True)

def list_text():
    # frameB全ウィジェットのinfoを取得
    children = frameB.winfo_children()
    text_list = [entry for entry in children if type(entry)==tk.Text]

    return text_list

def exe_make(path):
    # ソースコードの作業ディレクトリへのコピー
    working_dir = '/test_app/'
    new_path = working_dir + (path.split('test_GUI/'))[1]
    print(f'new_path {new_path}')
    shutil.copy2(path, new_path)

    # buildディレクトリ内関連ディレクトリの削除
    filename = (path.split('test_GUI/'))[1][:-3]
    remove_dir = '/test_app/build/' + filename + '/'
    try:
        shutil.rmtree(remove_dir)
    except:
        pass

    # 作業ディレクトリ内specファイルの削除
    all_files2 = [path for path in glob.glob('/test_app/*.spec')]
    remove_files2 = [path for path in all_files2 if filename in path]
    print(remove_files2)
    if len(remove_files2) != 0:
        for path in remove_files2:
            os.remove(path)

    # カレントディレクトリの変更
    os.chdir('/test_app/')

    # exeファイル作成コマンド実行
    def run(cmd):
        proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

        while True:
            line = proc.stdout.readline()
            if line:
                yield line
            if not line and proc.poll() is not None:
                break

    cmd = f"pyinstaller {new_path} --onefile --noconsole"
    lines = []
    for line in run(cmd):
        sys.stdout.buffer.write(line)
        lines.append(line)

    list_text()[0].insert(tk.END,'exeファイル作成完了\n')

    return lines


def list_text():
    # frameB全ウィジェットのinfoを取得
    children = frameB.winfo_children()
    text_list = [entry for entry in children if type(entry)==tk.Text]

    return text_list

def auto_scroll():
    list_text()[0].see("end")

# ウィンドウ作成
root = tk.Tk()
root.title("EXE MAKER v0.0.1")
root.geometry("555x375")
root.configure(bg='#b0e0e6')
style = ttk.Style()
style.theme_use('classic')
style.configure("MyWidget.TButton", gripcount=0,
                background="#7B3CFF", foreground="#EEFFFF",darkcolor='#7B3CFF', lightcolor="LightGreen",
                troughcolor="gray", bordercolor="gray", arrowcolor="white")
style.configure("MyWidget2.TButton", gripcount=0,
                background="#ffe4b5", foreground="#556b2f",darkcolor='#ffe4b5', lightcolor="LightGreen",
                troughcolor="gray", bordercolor="gray", arrowcolor="white")
style.configure("Vertical.TScrollbar", gripcount=0,
                background="#98fb98", foreground="#98fb98", darkcolor="#f5f5dc", lightcolor="#98fb98",
                troughcolor="#EEFFFF", bordercolor="#EEFFFF", arrowcolor="#EEFFFF")

# フォント設定
my_font = font.Font(root,family="System",size=18,weight="normal")
my_font2 = font.Font(root,family="System",size=16,weight="normal")
my_font3 = font.Font(root,family="System",size=14,weight="normal")
my_font4 = font.Font(root,family="System",size=12,weight="normal")

# Frame設定
frame = tk.Frame(root,background = '#b0e0e6')
frame.grid(row=0,column=0, sticky=tk.NSEW, padx=5, pady=10)
frameA = FrameA(master=frame)
frameB = FrameB(master=frame)

# Entry設定
entry = ttk.Entry(frame,width=45,background = '#C2EEFF',foreground = '#8b0000',font=my_font3)
entry.grid(row=0,column=1, padx=5,sticky=tk.W)

# Button設定
btn = ttk.Button(frame, text="実行",command=lambda:[list_text()[0].insert(tk.END,exe_make(entry.get())),auto_scroll()],width=8,style= 'MyWidget.TButton')
btn.grid(row=0,column=2,sticky=tk.W,padx=10)

btnA = ttk.Button(frameA, text="クリア",command=lambda:entry.delete(0,tk.END),width=8,style= 'MyWidget2.TButton')
btnA.pack(padx=5,pady=3,side=tk.TOP)

root.mainloop()

[Python] pyenv管理アプリ v0.0.6 コマンド直接入力

コマンド直接入力もできるようにしました。

<追記箇所のみ>

def direct(cmd):
    proc = subprocess.run(cmd, shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    return proc.stdout

btnG = ttk.Button(frameA, text="直接入力",command=lambda:[list_text()[0].insert(tk.END,direct(str(entry.get()))),auto_scroll()],width=8,style= 'MyWidget4.TButton')
btnG.pack(padx=5,pady=3,side=tk.TOP)

[Python] pyenv管理アプリ v0.0.5 pip関連操作

以下の4機能を追加しました。
1.pipによりインストールしたライブラリのリスト表示
2.アップデート可能なライブラリのリスト表示
3.ライブラリのアップデート
4.新規ライブラリのインストール

これでひと段落したのでexe化して普段使いします。

import subprocess,datetime,os
import tkinter as tk
import tkinter.font as font
from tkinter import ttk

# 16進数カラーコード
# #EDFFBE #C2EEFF #7B3CFF

class FrameA(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.configure(background = '#EDFFBE')
        self.grid(row=1,column=1, sticky=tk.NSEW, padx=5, pady=5)

class FrameB(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.configure(background = '#C2EEFF',borderwidth=0)
        self.grid(row=1,column=0, sticky=tk.NSEW, padx=5, pady=5)
        # Text設定
        self.text = tk.Text(self,height=20,width=45,highlightbackground="#f0f8ff",background = '#C2EEFF',foreground = '#8b0000')
        # ハイライトを消す場合
        # self.text = tk.Text(self,height=20,width=45,highlightthickness=0,background = '#C2EEFF',foreground = '#8b0000')

        # Y方向スクロールバーの設定
        self.scrollable_frame = ttk.Frame(self.text)
        self.scrollbar_y = ttk.Scrollbar(self, orient="vertical", command=self.text.yview,style= 'Vertical.TScrollbar')
        self.scrollbar_y.pack(side=tk.RIGHT, fill="y")

        self.text.configure(yscrollcommand=self.scrollbar_y.set)
        self.text.pack(side=tk.LEFT, fill="both", expand=True)

def version_check():
    proc = subprocess.run("pyenv versions", shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    return proc.stdout

def version_change(num):
    print(num)
    cmd = f"pyenv global {num}"
    subprocess.run(cmd, shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    proc = subprocess.run("pyenv versions", shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    return proc.stdout

def list_text():
    # frameB全ウィジェットのinfoを取得
    children = frameB.winfo_children()
    text_list = [entry for entry in children if type(entry)==tk.Text]

    return text_list

def auto_scroll():
    list_text()[0].see("end")

def piplist_check():
    proc = subprocess.run("python -m pip list", shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    return proc.stdout

def pipupd_check():
    proc = subprocess.run("python -m pip list --outdated", shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    return proc.stdout

def lib_update(lib):
    print(lib)
    cmd = f"python -m pip install -U {lib}"
    proc = subprocess.run(cmd, shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    return proc.stdout

def lib_install(lib):
    print(lib)
    cmd = f"python -m pip install {lib}"
    proc = subprocess.run(cmd, shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    return proc.stdout

# ウィンドウ作成
root = tk.Tk()
root.title("PYENV MANAGER v0.0.5")
root.geometry("480x330")
root.configure(bg='#EDFFBE')
style = ttk.Style()
style.theme_use('classic')
style.configure("MyWidget.TButton", gripcount=0,
                background="#7B3CFF", foreground="#EEFFFF",darkcolor='#7B3CFF', lightcolor="LightGreen",
                troughcolor="gray", bordercolor="gray", arrowcolor="white")
style.configure("MyWidget2.TButton", gripcount=0,
                background="#ffe4b5", foreground="#556b2f",darkcolor='#ffe4b5', lightcolor="LightGreen",
                troughcolor="gray", bordercolor="gray", arrowcolor="white")
style.configure("MyWidget3.TButton", gripcount=0,
                background="#2f4f4f", foreground="#EEFFFF",darkcolor='#2f4f4f', lightcolor="LightGreen",
                troughcolor="gray", bordercolor="gray", arrowcolor="white")
style.configure("MyWidget4.TButton", gripcount=0,
                background="#00008b", foreground="#EEFFFF",darkcolor='#00008b', lightcolor="LightGreen",
                troughcolor="gray", bordercolor="gray", arrowcolor="white")
style.configure("Vertical.TScrollbar", gripcount=0,
                background="#98fb98", foreground="#98fb98", darkcolor="#f5f5dc", lightcolor="#98fb98",
                troughcolor="#EEFFFF", bordercolor="#EEFFFF", arrowcolor="#EEFFFF")

# フォント設定
my_font = font.Font(root,family="System",size=18,weight="normal")
my_font2 = font.Font(root,family="System",size=16,weight="normal")
my_font3 = font.Font(root,family="System",size=14,weight="normal")
my_font4 = font.Font(root,family="System",size=12,weight="normal")

# Frame設定
frame = tk.Frame(root,background = '#EDFFBE')
frame.grid(row=0,column=0, sticky=tk.NSEW, padx=5, pady=10)
frameA = FrameA(master=frame)
frameB = FrameB(master=frame)

# Entry設定
entry = ttk.Entry(frame,width=25,background = '#C2EEFF',foreground = '#8b0000',font=my_font3)
entry.insert(tk.END, 'バージョン番号 or ライブラリ名') 
entry.grid(row=0,column=0, padx=5,sticky=tk.W)

# Button設定
btn = ttk.Button(frame, text="クリア",command=lambda:entry.delete(0,tk.END),width=8,style= 'MyWidget2.TButton')
btn.grid(row=0,column=1,sticky=tk.W,padx=10)

btnA = ttk.Button(frameA, text="Python確認",command=lambda:[list_text()[0].insert(tk.END,version_check()),auto_scroll()],width=8,style= 'MyWidget.TButton')
btnA.pack(padx=5,pady=3,side=tk.TOP)

btnB = ttk.Button(frameA, text="Python変更",command=lambda:[list_text()[0].insert(tk.END,version_change(str(entry.get()))),auto_scroll()],width=8,style= 'MyWidget.TButton')
btnB.pack(padx=5,pady=3,side=tk.TOP)

btnC = ttk.Button(frameA, text="pipLIST確認",command=lambda:[list_text()[0].insert(tk.END,piplist_check()),auto_scroll()],width=8,style= 'MyWidget3.TButton')
btnC.pack(padx=5,pady=3,side=tk.TOP)

btnD = ttk.Button(frameA, text="pipUPD確認",command=lambda:[list_text()[0].insert(tk.END,pipupd_check()),auto_scroll()],width=8,style= 'MyWidget3.TButton')
btnD.pack(padx=5,pady=3,side=tk.TOP)

btnE = ttk.Button(frameA, text="pipUPDATE",command=lambda:[list_text()[0].insert(tk.END,lib_update(str(entry.get()))),auto_scroll()],width=8,style= 'MyWidget3.TButton')
btnE.pack(padx=5,pady=3,side=tk.TOP)

btnF = ttk.Button(frameA, text="pipINSTALL",command=lambda:[list_text()[0].insert(tk.END,lib_install(str(entry.get()))),auto_scroll()],width=8,style= 'MyWidget4.TButton')
btnF.pack(padx=5,pady=3,side=tk.TOP)

root.mainloop()

[Python] pyenv管理アプリ v0.0.4 Textウィジェットの黒い枠線

EntryウィジェットとTextウィジェットの黒い枠線が気になるので設定を変えてみました。

Entryウィジェットはtkinter.Entryからttk.Entryに変更すると自然に枠線がなくなりました。

Textウィジェットの方はttk.Textというものが存在せず、まずborderwidth=0にしてみましたが変化なく、その後ドキュメントを読みながら色々いじってもダメで一旦あきらめかけました。その後、しばらく悪戦苦闘し質問サイトStack Overflow英語版でようやく解決法を見つけました(参考サイト参照)。

Textウィジェットの黒線は境界線ではなくハイライト(クリックすると色が変わる領域)でした。なので、highlightbackgroundで適当な色を指定するか、highlightthickness=0にして消せば解決です。

このようにtkinterは時間的学習コストが掛かりすぎるので、私のような物好き以外はPythonの他のGUIライブラリかJavaを勉強されることをお勧めします。

import subprocess,datetime,os
import tkinter as tk
import tkinter.font as font
from tkinter import ttk

# 16進数カラーコード
# #EDFFBE #C2EEFF #7B3CFF

class FrameA(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.configure(background = '#EDFFBE')
        self.grid(row=1,column=1, sticky=tk.NSEW, padx=5, pady=5)

class FrameB(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.configure(background = '#C2EEFF',borderwidth=0)
        self.grid(row=1,column=0, sticky=tk.NSEW, padx=5, pady=5)
        # Text設定
        self.text = tk.Text(self,height=20,width=45,highlightbackground="#f0f8ff",background = '#C2EEFF',foreground = '#8b0000')
        # ハイライトを消す場合
        # self.text = tk.Text(self,height=20,width=45,highlightthickness=0,background = '#C2EEFF',foreground = '#8b0000')
        
        # Y方向スクロールバーの設定
        self.scrollable_frame = ttk.Frame(self.text)
        self.scrollbar_y = ttk.Scrollbar(self, orient="vertical", command=self.text.yview,style= 'Vertical.TScrollbar')
        self.scrollbar_y.pack(side=tk.RIGHT, fill="y")

        self.text.configure(yscrollcommand=self.scrollbar_y.set)
        self.text.pack(side=tk.LEFT, fill="both", expand=True)

def version_check():
    proc = subprocess.run("pyenv versions", shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    return proc.stdout

def version_change(num):
    print(num)
    cmd = f"pyenv global {num}"
    subprocess.run(cmd, shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    proc = subprocess.run("pyenv versions", shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    return proc.stdout

def list_text():
    # frameB全ウィジェットのinfoを取得
    children = frameB.winfo_children()
    text_list = [entry for entry in children if type(entry)==tk.Text]

    return text_list

def auto_scroll():
    list_text()[0].see("end")

# ウィンドウ作成
root = tk.Tk()
root.title("PYENV MANAGER v0.0.4")
root.geometry("480x330")
root.configure(bg='#EDFFBE')
style = ttk.Style()
style.theme_use('classic')
style.configure("MyWidget.TButton", gripcount=0,
                background="#7B3CFF", foreground="#EEFFFF",darkcolor='#7B3CFF', lightcolor="LightGreen",
                troughcolor="gray", bordercolor="gray", arrowcolor="white")
style.configure("MyWidget2.TButton", gripcount=0,
                background="#ffe4b5", foreground="#556b2f",darkcolor='#ffe4b5', lightcolor="LightGreen",
                troughcolor="gray", bordercolor="gray", arrowcolor="white")
style.configure("Vertical.TScrollbar", gripcount=0,
                background="#98fb98", foreground="#98fb98", darkcolor="#f5f5dc", lightcolor="#98fb98",
                troughcolor="#EEFFFF", bordercolor="#EEFFFF", arrowcolor="#EEFFFF")

# フォント設定
my_font = font.Font(root,family="System",size=18,weight="normal")
my_font2 = font.Font(root,family="System",size=16,weight="normal")
my_font3 = font.Font(root,family="System",size=14,weight="normal")
my_font4 = font.Font(root,family="System",size=12,weight="normal")

# Frame設定
frame = tk.Frame(root,background = '#EDFFBE')
frame.grid(row=0,column=0, sticky=tk.NSEW, padx=5, pady=10)
frameA = FrameA(master=frame)
frameB = FrameB(master=frame)

# Entry設定
entry = ttk.Entry(frame,width=25,background = '#C2EEFF',foreground = '#8b0000',font=my_font3)
entry.insert(tk.END, 'バージョン番号 or ライブラリ名') 
entry.grid(row=0,column=0, padx=5,sticky=tk.W)

# Button設定
btn = ttk.Button(frame, text="クリア",command=lambda:entry.delete(0,tk.END),width=8,style= 'MyWidget2.TButton')
btn.grid(row=0,column=1,sticky=tk.W,padx=10)

btnA = ttk.Button(frameA, text="Python確認",command=lambda:[list_text()[0].insert(tk.END,version_check()),auto_scroll()],width=8,style= 'MyWidget.TButton')
btnA.pack(padx=5,pady=5,side=tk.TOP)

btnB = ttk.Button(frameA, text="Python変更",command=lambda:[list_text()[0].insert(tk.END,version_change(str(entry.get()))),auto_scroll()],width=8,style= 'MyWidget.TButton')
btnB.pack(padx=5,pady=5,side=tk.TOP)

root.mainloop()

参考サイト

[Python] pyenv管理アプリ v0.0.2_1 ScrolledText

簡易スクロールバーがついたScrolledTextというウィジェットを見つけました。もっと早く気づいていれば…

もうスクロールバーを自製してしまったので、それを引き続き使っていきます。

import subprocess,datetime,os
import tkinter as tk
import tkinter.font as font
from tkinter import ttk
from tkinter.scrolledtext import ScrolledText

# 16進数カラーコード
# #EDFFBE #C2EEFF #7B3CFF

class FrameA(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.configure(background = '#EDFFBE')
        self.grid(row=1,column=1, sticky=tk.NSEW, padx=5, pady=5)

def version_check():
    proc = subprocess.run("pyenv versions", shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    return proc.stdout

def version_change(num):
    print(num)
    cmd = f"pyenv global {num}"
    subprocess.run(cmd, shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    proc = subprocess.run("pyenv versions", shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    return proc.stdout

def auto_scroll():
    text.see("end")

# ウィンドウ作成
root = tk.Tk()
root.title("PYENV MANAGER v0.0.2")
root.geometry("430x330")
root.configure(bg='#EDFFBE')
style = ttk.Style()
style.theme_use('classic')
style.configure("MyWidget.TButton", gripcount=0,
                background="#7B3CFF", foreground="#EEFFFF",darkcolor='#7B3CFF', lightcolor="LightGreen",
                troughcolor="gray", bordercolor="gray", arrowcolor="white")
style.configure("MyWidget2.TButton", gripcount=0,
                background="#ffe4b5", foreground="#556b2f",darkcolor='#ffe4b5', lightcolor="LightGreen",
                troughcolor="gray", bordercolor="gray", arrowcolor="white")

# フォント設定
my_font = font.Font(root,family="System",size=18,weight="normal")
my_font2 = font.Font(root,family="System",size=16,weight="normal")
my_font3 = font.Font(root,family="System",size=14,weight="normal")

# Frame設定
frame = tk.Frame(root,background = '#EDFFBE')
frame.grid(row=0,column=0, sticky=tk.NSEW, padx=5, pady=10)
frameA = FrameA(master=frame)

# Entry設定
entry = tk.Entry(frame,width=25,background = '#C2EEFF',foreground = '#8b0000',font=my_font3)
entry.insert(tk.END, 'バージョン番号 or ライブラリ名') 
entry.grid(row=0,column=0,sticky=tk.W)

# Text設定
text = tk.scrolledtext.ScrolledText(frame,height=20,width=40,background = '#C2EEFF',foreground = '#8b0000')
text.grid(row=1,column=0,pady=10,sticky=tk.W)

# Button設定
btn = ttk.Button(frame, text="クリア",command=lambda:entry.delete(0,tk.END),width=8,style= 'MyWidget2.TButton')
btn.grid(row=0,column=1,sticky=tk.W,padx=10)

btnA = ttk.Button(frameA, text="Python確認",command=lambda:[text.insert(tk.END,version_check()),auto_scroll()],width=8,style= 'MyWidget.TButton')
btnA.pack(padx=5,pady=5,side=tk.TOP)

btnB = ttk.Button(frameA, text="Python変更",command=lambda:[text.insert(tk.END,version_change(str(entry.get()))),auto_scroll()],width=8,style= 'MyWidget.TButton')
btnB.pack(padx=5,pady=5,side=tk.TOP)

root.mainloop()

[Python] pyenv管理アプリ v0.0.3 スクロールバー

TextウィジェットにY方向のスクロールバーを配置しました。自動スクロールもできるようにしています。

スクロールバー配置は2回目なのでスムーズでした。バー各部位の色設定について把握しました。

あとはpipによるインストールやアップデート位でしょうか。

import subprocess,datetime,os
import tkinter as tk
import tkinter.font as font
from tkinter import ttk

# 16進数カラーコード
# #EDFFBE #C2EEFF #7B3CFF

class FrameA(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.configure(background = '#EDFFBE')
        self.grid(row=1,column=1, sticky=tk.NSEW, padx=5, pady=5)

class FrameB(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.configure(background = '#EDFFBE')
        self.grid(row=1,column=0, sticky=tk.NSEW, padx=5, pady=5)
        # Text設定
        self.text = tk.Text(self,height=20,width=45,background = '#C2EEFF',foreground = '#8b0000')
        # Y方向スクロールバーの設定
        self.scrollable_frame = ttk.Frame(self.text)
        self.scrollbar_y = ttk.Scrollbar(self, orient="vertical", command=self.text.yview,style= 'Vertical.TScrollbar')
        self.scrollbar_y.pack(side=tk.RIGHT, fill="y")

        self.text.configure(yscrollcommand=self.scrollbar_y.set)
        self.text.pack(side=tk.LEFT, fill="both", expand=True)

def version_check():
    proc = subprocess.run("pyenv versions", shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    return proc.stdout

def version_change(num):
    print(num)
    cmd = f"pyenv global {num}"
    subprocess.run(cmd, shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    proc = subprocess.run("pyenv versions", shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    return proc.stdout

def list_text():
    # frameB全ウィジェットのinfoを取得
    children = frameB.winfo_children()
    text_list = [entry for entry in children if type(entry)==tk.Text]

    return text_list

def auto_scroll():
    list_text()[0].see("end")

# ウィンドウ作成
root = tk.Tk()
root.title("PYENV MANAGER v0.0.2")
root.geometry("480x330")
root.configure(bg='#EDFFBE')
style = ttk.Style()
style.theme_use('classic')
style.configure("MyWidget.TButton", gripcount=0,
                background="#7B3CFF", foreground="#EEFFFF",darkcolor='#7B3CFF', lightcolor="LightGreen",
                troughcolor="gray", bordercolor="gray", arrowcolor="white")
style.configure("MyWidget2.TButton", gripcount=0,
                background="#ffe4b5", foreground="#556b2f",darkcolor='#ffe4b5', lightcolor="LightGreen",
                troughcolor="gray", bordercolor="gray", arrowcolor="white")
style.configure("Vertical.TScrollbar", gripcount=0,
                background="#98fb98", foreground="#98fb98", darkcolor="#f5f5dc", lightcolor="#98fb98",
                troughcolor="#EEFFFF", bordercolor="#EEFFFF", arrowcolor="#EEFFFF")

# フォント設定
my_font = font.Font(root,family="System",size=18,weight="normal")
my_font2 = font.Font(root,family="System",size=16,weight="normal")
my_font3 = font.Font(root,family="System",size=14,weight="normal")

# Frame設定
frame = tk.Frame(root,background = '#EDFFBE')
frame.grid(row=0,column=0, sticky=tk.NSEW, padx=5, pady=10)
frameA = FrameA(master=frame)
frameB = FrameB(master=frame)

# Entry設定
entry = tk.Entry(frame,width=25,background = '#C2EEFF',foreground = '#8b0000',font=my_font3)
entry.insert(tk.END, 'バージョン番号 or ライブラリ名') 
entry.grid(row=0,column=0,sticky=tk.W)

# Button設定
btn = ttk.Button(frame, text="クリア",command=lambda:entry.delete(0,tk.END),width=8,style= 'MyWidget2.TButton')
btn.grid(row=0,column=1,sticky=tk.W,padx=5)

btnA = ttk.Button(frameA, text="Python確認",command=lambda:[list_text()[0].insert(tk.END,version_check()),auto_scroll()],width=8,style= 'MyWidget.TButton')
btnA.pack(padx=5,pady=5,side=tk.TOP)

btnB = ttk.Button(frameA, text="Python変更",command=lambda:[list_text()[0].insert(tk.END,version_change(str(entry.get()))),auto_scroll()],width=8,style= 'MyWidget.TButton')
btnB.pack(padx=5,pady=5,side=tk.TOP)

root.mainloop()

[Python] pyenv管理アプリ v0.0.2 Pythonバージョン変更

pyenvのPythonバージョン変更機能を追加しました。

Entryウィジェットなどにデフォルトではプレースホルダー(入力を誘導するための薄い字)を置けないそうです。そのうちプレースホルダー作成クラスを書きたいです。

なるべくキーボードではなくマウスで処理したい派なのでこのようなコマンドを覚えずに済むツールを作るのは実に楽しいです。

次はスクロールバーの追加になります。

import subprocess,datetime,os
import tkinter as tk
import tkinter.font as font
from tkinter import ttk

# 16進数カラーコード
# #EDFFBE #C2EEFF #7B3CFF

class FrameA(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.configure(background = '#EDFFBE')
        self.grid(row=1,column=1, sticky=tk.NSEW, padx=5, pady=5)

def version_check():
    proc = subprocess.run("pyenv versions", shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    return proc.stdout

def version_change(num):
    print(num)
    cmd = f"pyenv global {num}"
    subprocess.run(cmd, shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    proc = subprocess.run("pyenv versions", shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
    return proc.stdout

# ウィンドウ設定
root = tk.Tk()
root.title("PYENV MANAGER v0.0.2")
root.geometry("415x330")
root.configure(bg='#EDFFBE')
style = ttk.Style()
style.theme_use('classic')
style.configure("MyWidget.TButton", gripcount=0,
                background="#7B3CFF", foreground="#EEFFFF",darkcolor='#7B3CFF', lightcolor="LightGreen",
                troughcolor="gray", bordercolor="gray", arrowcolor="white")
style.configure("MyWidget2.TButton", gripcount=0,
                background="#ffe4b5", foreground="#556b2f",darkcolor='#ffe4b5', lightcolor="LightGreen",
                troughcolor="gray", bordercolor="gray", arrowcolor="white")

# フォント設定
my_font = font.Font(root,family="System",size=18,weight="normal")
my_font2 = font.Font(root,family="System",size=16,weight="normal")
my_font3 = font.Font(root,family="System",size=14,weight="normal")

# Frame設定
frame = tk.Frame(root,background = '#EDFFBE')
frame.grid(row=0,column=0, sticky=tk.NSEW, padx=5, pady=10)
frameA = FrameA(master=frame)

# Entry設定
entry = tk.Entry(frame,width=25,background = '#C2EEFF',foreground = '#8b0000',font=my_font3)
entry.insert(tk.END, 'バージョン番号 or ライブラリ名') 
entry.grid(row=0,column=0,sticky=tk.W)

# Text設定
text = tk.Text(frame,height=20,width=40,background = '#C2EEFF',foreground = '#8b0000')
text.grid(row=1,column=0,pady=10,sticky=tk.W)

# Button設定
btn = ttk.Button(frame, text="クリア",command=lambda:entry.delete(0,tk.END),width=8,style= 'MyWidget2.TButton')
btn.grid(row=0,column=1,sticky=tk.W,padx=5)

btnA = ttk.Button(frameA, text="Python確認",command=lambda:text.insert(tk.END,version_check()),width=8,style= 'MyWidget.TButton')
btnA.pack(padx=5,pady=5,side=tk.TOP)

btnB = ttk.Button(frameA, text="Python変更",command=lambda:text.insert(tk.END,version_change(str(entry.get()))),width=8,style= 'MyWidget.TButton')
btnB.pack(padx=5,pady=5,side=tk.TOP)

root.mainloop()

[Python] pyenv管理アプリ v0.0.1 標準出力取得

pyenvをGUIで管理するアプリを作っていきます。

まずはpyenvでインストールしているPythonのバージョンを取得し、Textウィジェットに反映させます。

試してみたらsubprocess.PIPEはPopen​()関数だけでなく、run()関数でも使えました。

import subprocess,datetime,os
import tkinter as tk
import tkinter.font as font
from tkinter import ttk

# 色
# #EDFFBE #C2EEFF #7B3CFF

proc = subprocess.run("pyenv versions", shell=True, stdout= subprocess.PIPE, stderr = subprocess.PIPE)
print(proc.stdout)
# こちらでも可能
# proc = subprocess.check_output("pyenv versions", shell=True)
# print(proc)

# ウィンドウ設定
root = tk.Tk()
root.title("PYENV MANAGER v0.0.1")
root.geometry("345x300")
root.configure(bg='#EDFFBE')
style = ttk.Style()
style.theme_use('classic')
style.configure("MyWidget.TButton", gripcount=0,
                background="#7B3CFF", foreground="#EEFFFF",darkcolor='#7B3CFF', lightcolor="LightGreen",
                troughcolor="gray", bordercolor="gray", arrowcolor="white")

# フォント設定
my_font = font.Font(root,family="System",size=18,weight="normal")
my_font2 = font.Font(root,family="System",size=16,weight="normal")

# Frame設定
frame = tk.Frame(root,background = '#EDFFBE')
frame.grid(row=0,column=0, sticky=tk.NSEW, padx=5, pady=10)

# Text設定
text = tk.Text(frame,height=20,width=30,background = '#C2EEFF',foreground = '#8b0000')
text.grid(row=0,column=0,sticky=tk.W)

# Button設定
btnA = ttk.Button(frame, text="Python確認",command=lambda:text.insert(tk.END,proc.stdout),width=8,style= 'MyWidget.TButton')
btnA.grid(row=0,column=1,padx=10,sticky=tk.N)

root.mainloop()

[Python] ファイル削除ツール アップデートv0.0.3 スクロールバー他

[macOS Catalina 10.15.7]

アップデート内容はXY方向のスクロールバー配置、自動スクロール、無限ループ、処理完了時刻表示です。

スクロールバーを付けるだけでしんどい思いをしました。FrameBクラスの記述ではいいサンプルコードを見つけるまでは七転八倒状態でした。

JavaでのGUI作成の快適さを知ってしまうと、tkinterで作るモチベーションがかなり低下します。

JavaからPythonによるスクレイピング のモジュールを呼び出せるのであればGUIについては全てJavaで作成するのですが、Python3は一切融通できないところがツラいです。

今後ツール類はJavaで書くかもしれません。ただJavaはJavaでコード量が増えるデメリットがあるので、後継言語とされているScalaでどうなるか試してみる可能性もあります。

このツールだけはtkinterでとことんやってみようかとも思っています。

※ 21/07/06追記 あとでわかったことですがTextウィジェットはスクロールバーがなくてもスクロールできます。ただ表示されている部分の全体における位置が分からないのでスクロールバーがある方が見やすいです。

import glob,os,shutil,sys,datetime
import tkinter as tk
import tkinter.font as font
from tkinter import ttk

# 色
# '#98fb98','#8b0000','#808000','#FF7F50','#00FA9A','#9370DB','#40E0D0','#FFFACD'

def list_text():
    # frameB全ウィジェットのinfoを取得
    children = frameB.winfo_children()
    text_list = [entry for entry in children if type(entry)==tk.Text]

    return text_list

class FrameA(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.configure(background = '#FFFACD')
        self.grid(row=1,column=1, sticky=tk.NSEW, padx=5, pady=5)

class FrameB(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.configure(background = '#FFFACD')
        # テキストウィジェットの設定
        self.msg_box = tk.Text(self,width=30,height=5,background = '#98fb98',foreground = '#8b0000',font=my_font2,wrap=tk.NONE)
        # スクロールするフレームをテキストウィジェットに設定
        self.scrollable_frame = ttk.Frame(self.msg_box)
        # FrameBのグリッド位置を設定
        self.grid(row=2,column=1, sticky=tk.NSEW, padx=5, pady=5)

        # Y方向スクロールバーの設定
        self.scrollbar_y = ttk.Scrollbar(self, orient="vertical", command=self.msg_box.yview)
        self.scrollbar_y.pack(side=tk.RIGHT, fill="y")
        self.msg_box.configure(yscrollcommand=self.scrollbar_y.set)
        
        # X方向スクロールバーの設定
        self.scrollbar_x = ttk.Scrollbar(self, orient="horizontal", command=self.msg_box.xview)
        self.scrollbar_x.pack(side=tk.BOTTOM, fill="x")
        self.msg_box.configure(xscrollcommand=self.scrollbar_x.set)
        self.msg_box.pack(side=tk.LEFT, fill="both", expand=True)

# Macのデスクトップ通知
def notification1():
    os.system("osascript -e 'display notification \"フォルダ削除\n処理完了\"'")

def notification2():
    os.system("osascript -e 'display notification \"ファイル削除\n処理完了\"'")

root = tk.Tk()
root.title("FILE REMOVER v0.0.3")
root.geometry("460x210")
root.configure(bg='#FFFACD')
style = ttk.Style()
style.theme_use('clam')
style.configure("Vertical.TScrollbar", gripcount=0,
                background="Green", darkcolor="DarkGreen", lightcolor="LightGreen",
                troughcolor="gray", bordercolor="gray", arrowcolor="white")
style.configure("Horizontal.TScrollbar", gripcount=0,
                background="Green", darkcolor="DarkGreen", lightcolor="LightGreen",
                troughcolor="gray", bordercolor="gray", arrowcolor="white")

# フォント設定
my_font = font.Font(root,family="System",size=18,weight="normal")
my_font2 = font.Font(root,family="System",size=16,weight="normal")

# フレームの作成・配置
frame = tk.Frame(root,background = '#FFFACD')
frame.grid(row=0,column=0, sticky=tk.NSEW, padx=5, pady=10)
frameA = FrameA(master=frame)
frameB = FrameB(master=frame)

# ラベルの作成・配置
label = tk.Label(frame,text='PATH',background = '#FFFACD',foreground = '#8b0000',font=my_font)
label.grid(row=0, column=0)

# パス入力エントリの作成・配置
entry = tk.Entry(frame,width=25,background = '#98fb98',foreground = '#8b0000',font=my_font)
entry.grid(row=0,column=1,sticky=tk.W)

# 実行ボタンの作成・配置
var_act = tk.IntVar()
btn = tk.Button(frame, text="削除",command=lambda:var_act.set(1),width=2,font=my_font)
btn.grid(row=0,column=2,padx=2)

# ラジオボタンの作成・配置
var = tk.IntVar()
rb1 = ttk.Radiobutton(frameA,text='フォルダ',value=1,variable=var)
rb1.pack(padx=30,side=tk.LEFT)

rb2 = ttk.Radiobutton(frameA,text='ファイル',value=2,variable=var)
rb2.pack(padx=30,side=tk.LEFT)

# クリアボタンの作成・配置
btn2 = tk.Button(frame, text="クリア",command=lambda:[entry.delete(0,tk.END),text_list[0].delete('1.0',tk.END)],width=2,font=my_font)
btn2.grid(row=1,column=2,padx=2)

text_list = list_text()
print(text_list)

while True:
    # フォルダパスの入力を待機
    btn.wait_variable(var_act)

    # 入力したフォルダパスを取得
    dirpath = entry.get()

    # ラジオボタンのvalueを取得
    v = var.get()

    if v == 1:
        path = dirpath + '/*' # 直下のフォルダを削除
        for f in glob.glob(path,recursive=True):
            if os.path.isdir(f):
                shutil.rmtree(f)

        datetime_now = datetime.datetime.now()
        datetime_now_str = datetime_now.strftime('%y/%m/%d %H:%M:%S')

        text_list[0].insert(tk.END,dirpath+'\n')
        text_list[0].insert(tk.END,f'フォルダ削除完了 {datetime_now_str}\n')
        text_list[0].see("end")
        notification1()
    else:
        path = dirpath + '/**' # 再帰的処理により下層ファイルも削除
        for f in glob.glob(path, recursive=True):
            if os.path.isfile(f):
                os.remove(f)

        datetime_now = datetime.datetime.now()
        datetime_now_str = datetime_now.strftime('%y/%m/%d %H:%M:%S')

        text_list[0].insert(tk.END,dirpath+'\n')
        text_list[0].insert(tk.END,f'ファイル削除完了 {datetime_now_str}\n')
        text_list[0].see("end")
        notification2()

root.mainloop()

参考サイト

[Python] ファイル削除ツール アップデートv0.0.2 処理完了表示

[macOS Catalina 10.15.7]

結構重宝しているファイル削除ツールをアップデートしました。

削除するファイル数が多いと処理完了のタイミングがわからないので、GUI上にメッセージを表示すると同時にデスクトップ通知を送るようにしました。

簡単なツールやスクレイピング関連アプリはこれまで通りPythonのtkinterで、 MySQL関連など本格的に仕上げたい場合はJavaのSwingあるいはJavaFXで作成するつもりです。

tkinterで作成するGUIは今ひとつモダンな感じがしませんが、ツール類はともかくスクレイピングのコードをJavaで書くのはかなりの難行と思われるため大人しくtkinterにしておきます。

import glob,os,shutil,sys
import tkinter as tk
import tkinter.font as font
from tkinter import ttk

def list_text():
    # フレーム内全ウィジェットのinfoを取得
    children = frame.winfo_children()
    text_list = [entry for entry in children if type(entry)==tk.Text]
    print(f'textのみ抽出\n{text_list}\n')

    return text_list

class FrameB(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.configure(background = '#FFFACD')
        self.grid(row=1,column=1, sticky=tk.NSEW, padx=5, pady=5)

# Macのデスクトップ通知
def notification1():
    os.system("osascript -e 'display notification \"フォルダ削除\n処理完了\"'")

def notification2():
    os.system("osascript -e 'display notification \"ファイル削除\n処理完了\"'")

# 色
# '#98fb98','#8b0000','#808000','#FF7F50','#00FA9A','#9370DB','#40E0D0','#FFFACD'

root = tk.Tk()
root.title("FILE REMOVER v0.0.2")
root.geometry("420x200")
root.configure(bg='#FFFACD')

# 設定
my_font = font.Font(root,family="System",size=18,weight="normal")
my_font2 = font.Font(root,family="System",size=16,weight="normal")

# フレームの作成・配置
frame = tk.Frame(root,background = '#FFFACD')
frame.grid(row=0,column=0, sticky=tk.NSEW, padx=5, pady=10)
frameB = FrameB(master=frame)

# ラベルの作成・配置
label = tk.Label(frame,text='PATH',background = '#FFFACD',foreground = '#8b0000',font=my_font)
label.grid(row=0, column=0)

# パス入力エントリの作成・配置
entry = tk.Entry(frame,width=25,background = '#98fb98',foreground = '#8b0000',font=my_font)
entry.grid(row=0,column=1,sticky=tk.W)

# 実行ボタンの作成・配置
var_act = tk.IntVar()
btn = tk.Button(frame, text="削除",command=lambda:var_act.set(1),width=2,font=my_font)
btn.grid(row=0,column=2,padx=2)

# ラジオボタンの作成・配置 FrameBにパックして左に詰め込む
var = tk.IntVar()
rb1 = ttk.Radiobutton(frameB,text='フォルダ',value=1,variable=var)
rb1.pack(padx=30,side=tk.LEFT)

rb2 = ttk.Radiobutton(frameB,text='ファイル',value=2,variable=var)
rb2.pack(padx=30,side=tk.LEFT)

# メッセージボックスの作成・配置
msg_box = tk.Text(frame,width=25,height=5,background = '#98fb98',foreground = '#8b0000',font=my_font)
msg_box.grid(row=2,column=1)

# クリアボタンの作成・配置
btn2 = tk.Button(frame, text="クリア",command=lambda:[entry.delete(0,tk.END),text_list[0].delete('1.0',tk.END)],width=2,font=my_font)
btn2.grid(row=1,column=2,padx=2)

text_list = list_text()
print(text_list)

for i in range(100): # 100回処理可能
    # フォルダパスの入力を待機
    btn.wait_variable(var_act)

    # 入力したフォルダパスを取得
    dirpath = entry.get()

    # ラジオボタンのvalueを取得
    v = var.get()

    if v == 1:
        path = dirpath + '/*' # 直下のフォルダを削除
        for f in glob.glob(path,recursive=True):
            if os.path.isdir(f):
                shutil.rmtree(f)
        text_list[0].delete('1.0',tk.END)
        text_list[0].insert(tk.END,dirpath+'\n')
        text_list[0].insert(tk.END,'フォルダ削除完了')
        notification1()
    else:
        path = dirpath + '/**' # 再帰的処理により下層ファイルも削除
        for f in glob.glob(path, recursive=True):
            if os.path.isfile(f):
                os.remove(f)
        text_list[0].delete('1.0',tk.END)
        text_list[0].insert(tk.END,dirpath+'\n')
        text_list[0].insert(tk.END,'ファイル削除完了')
        notification2()

root.mainloop()