公開した、はてなブログに掲載する写真や画像を変換して、アップロードして、画像のURLをコピーできるアプリの作り方を紹介します。
公開している独自ライブラリの使い方などを説明します。
📔 はてなブログ向け画像ツール【フリー】 🔗
目次
- ◆アプリのサンプル画像
- ◆アプリの機能・特長
- ◆アプリが参照しているライブラリなど
- ◆タブに画像一覧を格納
- ◆設定項目(TOMLユーティリティの使い方)
- ◆画像変換(ImageUIクラスの使い方)
- ◆画像のアップロード(HatenaFotolifeUIクラスの使い方)
- ◆アップロードされた画像のURLをコピー
- ◆別記事で説明している内容
- ◆Tkinterの使い方
- ◆必要なパッケージ
- ◆ソースの取得
- ◆さいごに
そのため、本記事はそれらを説明した記事の参照が多くなります。
既存の記事の中で詳しく説明している内容については、本記事であらためて説明していません。
本記事では、既存の記事の内容と異なる部分について説明します。
読者の方にはご不便をおかけしますが、ご理解いただければ幸いです。
◆アプリのサンプル画像
▽アプリ起動後の画面
上部のボタンで操作します。
設定項目が画面の右に表示されます。
◆アプリの機能・特長
- 画像のサイズ変更
- 画像のミラー反転
- 画像を左または右に回転
- 画像に文字透かしを挿入
- 画像を はてなフォトライフにアップロード
- アップロードした画像のURLをコピー
- 設定の保存
◆アプリが参照しているライブラリなど
本アプリでは、次の記事で説明したものをライブラリとして使用しています。
- 各種、画像変換
📔 画像サイズを変更し文字透かしを入れるアプリの作り方【Python】 🔗 - 画像のアップロード
📔 はてなフォトライフへ画像をアップロードするアプリの作り方(AtomAPIの使い方)【Python】 🔗 - 画面右側の設定項目の作成
📔 ◆設定画面作成メソッド(TomlFileUtilクラス) - TOMLで設定ファイルを扱うユーティリティ【Python】 🔗
こちらはライブラリとしていませんが、画像一覧の表示など画面の基本的な構成はそのまま利用しています。
- 読み込んだ画像の表示、処理対象画像の選択、変換後の画像の表示などのベース
📔 ScrolledFrameとwrapped_gridで作る画像一覧の作り方【Python】 🔗
◆タブに画像一覧を格納
画像一覧を表示する部分は、すでに作成したものがあったのでそれをベースにしました。
▶画像一覧の作り方については こちらの記事を参照してください。
📔 ◆画像一覧の作り方 - ScrolledFrameとwrapped_gridで作る画像一覧の作り方【Python】 🔗
参考記事との違いは、本アプリでは、画像一覧をタブに格納していることです。
したがって、参考記事でも使用している全フレームを保存する frame_children
は、タブ名をキーにした辞書型にしてタブごとに管理します。
選択された画像の持ち方
タブに入れる ScrolledFrame オブジェクトの parent_frame
属性にセット型の checked_image_paths
属性を追加しています。
これは、選択された画像のパスを保持します。
画像変換などの処理を選択された画像に対して実行するためです。
こちらも参考記事とは違い、カレントタブの ScrolledFrame オブジェクトを対象にします。
カレントタブのウィジェットの取得
複数タブから現在選択中のタブを処理対象にするために nametowidget()
メソッドを使用して対象のウィジェットを取得します。
▽現在選択しているタブのウィジェットを取得する処理
w = self.note.nametowidget(self.note.select()) # get current tab widget
ScrolledFrame クラスの変更
タブの中に ScrolledFrame オブジェクトを入れています。
元々 ScrolledFrame オブジェクトのトップウィジェットは Canvas でした。
しかし、タブの中に Canvas ウィジェットは入れられません。
そこで、Frame ウィジェットを用意してそこに Canvas ウィジェットを格納しました。
これでトップウィジェットを Frame にしています。
更に、作成した Frame ウィジェットを parent_frame
属性としてタブ追加できるようにしました。
スクロール
ScrolledFrame クラスは、スクロールに対応しています。
画像を読み直した時に、先頭にスクロールするよう処理を追加しています。
理由は、新しく画像を読み直した時に、以前の表示状態に依り画像が表示されないことがあるためです。 1
▽先頭にスクロール
# 画像の数が少なくなるとcanvasが大きいままなのでスクロール位置を戻す parent.parent_canvas.yview_moveto(0.0)
【コード】
▽タブの作成処理
# タブの作成 self.note = ttk.Notebook(self.b_frame) self.note.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 新しいタブの追加 self.frame4images = self.create_frame4images(self.note, tab_name=self.tab1_name)
▽ScrolledFrame オブジェクトの作成処理
def create_frame4images(self, parent:ttk.Notebook, tab_name:str) -> ScrolledFrame: """ 画像用ScrolledFrameを作成し、parentにタブを追加 Args: Any: 親ウィジェット(Notebook) str: タブ名 """ scrolled_frame = ScrolledFrame(parent) # チェックされた画像用セットを初期化 scrolled_frame.parent_frame.checked_image_paths = set() # bind scrolled_frame.bind_class("Checkbutton", "<Double 3>", self.preview_image) # マウスを右ダブルクリックしたときの動作 # wrapped_gridのbind self.frame_children[tab_name] = [] scrolled_frame.parent_canvas.bind("<Configure>", lambda event: TkinterLib.wrapped_grid( scrolled_frame.parent_canvas, *self.frame_children.get(tab_name), event=event, flex=False), add=True) self.note.add(scrolled_frame.parent_frame, text=tab_name) # タブを追加 return scrolled_frame
▶ScrolledFrame クラスの使い方については、こちらの記事を参照してください。
📔 ◆ScrolledFrame-クラスの使い方 - ScrolledFrameとwrapped_gridで作る画像一覧の作り方【Python】 🔗
▶タブの使い方については、こちらの記事を参照してください。
📔 ■タブの実装(Notebookウィジェットの使い方) - CSV viewerアプリの作り方(ドラッグアンドドロップ)【Python】 🔗
◆設定項目(TOMLユーティリティの使い方)
事前に準備した(作成方法は次にあります) TOML ファイルを読んで画面(ウィジェット)を作成します。
設定項目は、次の記事で説明している create_frame_from_toml_dict()
メソッドを使用して作成します。
▶設定項目の主要な処理は、こちらの記事を参照してください。
📔 ◆設定画面作成メソッド(TomlFileUtilクラス) - TOMLで設定ファイルを扱うユーティリティ【Python】 🔗
設定項目数が思いのほか多くなったので、親ウィジェットを ScrolledFrame にしました。
何もコードを書かなくてスクロールバーが付くので便利です。
【処理】
- exe の場所を取得
- TOML ファイルのパスを作成
- TomlFileUtil クラスのインスタンスを作成
- TOML ファイルの読み込み
- TOML ファイルが読めない時のエラー処理
- TOML ファイルを基にした設定画面の作成
設定項目の辞書が返るのでself.var_dict
に設定 - 保存ボタンにメソッドの割り付け 2
- 画面の更新
- 親は ScrolledFrame オブジェクトなので parent_canvas の幅を更新
- 読み込んだ TOML データの
USER
セクションを辞書に設定
【コード】
▽親フレームの作成
self.r_frame = ScrolledFrame(master, width=50, bg="lightblue") # 背景色を付けるとセクションの境に色が出る self.r_frame.parent_frame.pack(side="right", fill="y") # 設定用フレーム内の作成 設定値をself.var_dictに設定 self.create_config_frame(self.r_frame)
▽設定項目画面作成
def create_config_frame(self, parent): """ 設定項目用画面の作成 self.var_dictの作成 """ # pyinstallerで作成したexeか判断してexeの場所を特定する if getattr(sys, 'frozen', False): exe_path = os.path.dirname(sys.executable) else: exe_path = sys.prefix my_path = os.path.join(exe_path, r"blog_image_tool.toml") toml = TomlFileUtil() result = toml.read_toml(my_path) if not result: self.master.withdraw() # トップレベルウィンドウを出さない messagebox.showerror("使用上のエラー", f"{my_path}\nファイルが見つかりません") sys.exit() self.var_dict = toml.create_frame_from_toml_dict(parent, True) toml.btn_save.config(command=lambda path=my_path: toml.save_toml(path, "USER")) parent.update() if parent.parent_canvas.winfo_width() > parent.winfo_width(): parent.parent_canvas.config(width=parent.winfo_width()) toml.set_toml2var_dict("USER")
◇TOMLファイルの作成
設定項目をTOMLファイルで作成します。
下記は、DEFINITIONについてだけ載せています。
TOML ファイル全体は、ソースの取得にあるリンク先から取得できます。
▶設定項目用TOMLファイルの仕様は、こちらの記事を参照してください。
📔 ◇要件にあったTOMLファイル - TOMLで設定ファイルを扱うユーティリティ【Python】 🔗
[DEFINITION."画像変換"] do_resize = ["サイズ変更する","bool",] dest_path = ["保存先フォルダ","str",] width = ["幅","int",] height = ["高さ","int",] overwrite = ["新しいファイルをいくつも作らない","bool",] exif = ["Exif情報出力","bool",] mirror = ["ミラー反転","bool",] rotate270 = ["右回転","bool",] rotate90 = ["左回転","bool",] [DEFINITION."透かし"] is_water_mark = ["透かし文字付加","bool",] water_mark = ["透かし文字","str",] wm_font_name = ["フォントファイル名","str",] wm_size = ["フォントサイズ","int",] wm_f_color = ["文字色","str",] wm_b_color = ["縁取りの色","str",] wm_stroke = ["縁取りの幅(px)","int",] wm_padx = ["右下からの隙間(横)","int",] wm_pady = ["右下からの隙間(縦)","int",] [DEFINITION."アップロード"] use_hatena_folder = ["デフォルトフォルダを使用","bool",] folder = ["フォトライフのフォルダ","str",] [DEFINITION."画像URLのコピー"] foto = ["fotolife記法で出力","bool",] only_url = ["画像番号またはURLだけを出力","bool",] add_title = ["ファイル名をタイトルとして付加","bool",] add_options = ["オプション","str",] [DEFINITION."表示設定"] width4thumbnail = ["サムネイル画像の幅","int",] geometry = ["アプリのサイズと位置","str",]
◆画像変換(ImageUIクラスの使い方)
画像変換は、以前に作成したコードを呼び出して使用しています。
▶画像変換の主要な処理は、こちらの記事を参照してください。
📔 ❖ライブラリとして使用する場合の仕様 - 画像サイズを変更し文字透かしを入れるアプリの作り方【Python】 🔗
基本的には、ImageUI
クラスの convert_image_from_dialog_or_args()
メソッドを実行するだけです。
画像変換の結果は新しいタブを作成して画面に表示します。
変換後の画像をアップロードするためです。
【処理】
▽画像変換処理
- 選択された画像があるか確認
無ければメッセージを出して戻る - 画面の設定項目(ウィジェット変数の辞書)を辞書に変換
- 画像変換
convert_image_from_dialog_or_args()
メソッドを実行 - 新しいタブを作成して変換後の画像を表示する
create_new_tab()
を実行 - エラーメッセージがあれば設定
▽新しいタブの作成処理
- タブ名の初期値を設定
- 既に同名のタブ名があったら末尾に数字(2~)を足してタブ名を作成
- 画像用 ScrolledFrame オブジェクトを作成し NoteBook ウィジェットに追加
create_frame4images()
を実行 - 追加したタブを表示
- image_paths のパスからファイル情報、画像サムネイルを作成し
ScrolledFrame オブジェクトに画像を含んだ Frame を追加
【コード】
▽画像変換処理
def convert_images(self, event=None): """ 選択された画像の変更(リサイズ、回転、反転、Exif除去、透かし) 画像変更し、保存し、新しいタブに表示する """ self.msg.set("") w = self.note.nametowidget(self.note.select()) # get current tab widget paths = list(w.checked_image_paths) if not paths: self.msg.set("選択された画像がありません") return settings_dict = {key: value.get() for key, value in self.var_dict.items()} # variableの辞書を値の辞書に変換 self.resized_paths, err_msg = self.image_ui.convert_image_from_dialog_or_args(settings_dict, paths=paths) self.create_new_tab(self.resized_paths) if err_msg: self.msg.set(err_msg)
▽新しいタブの作成処理
def create_new_tab(self, image_paths:set): tab_name_resize = "リサイズ画像" # 複数の結果に対応するためタブ名を変える x = 2 tab_name = tab_name_resize while tab_name in self.frame_children: tab_name = f"{tab_name_resize}{x}" x += 1 self.frame4images2 = self.create_frame4images(self.note, tab_name) self.note.select(len(self.note.tabs()) - 1) # 追加したタブを表示 self.open_files_get_images_set2frame(parent=self.frame4images2 , file_paths_=tuple(image_paths) , tab_name=tab_name)
◆画像のアップロード(HatenaFotolifeUIクラスの使い方)
アップロードは、以前に作成したコードを呼び出して使用しています。
▶アップロードの主要な処理は、こちらの記事を参照してください。
📔 ❖ライブラリとして使用する場合の仕様 - はてなフォトライフへ画像をアップロードするアプリの作り方(AtomAPIの使い方)【Python】 🔗
基本的には、HatenaFotolifeUI
クラスの upload_image_to_hatena()
メソッドを実行するだけです。
アップロードに時間が掛かるので別スレッドで動かしています。
Tkinter は mainloop に戻してあげないとアプリが止まったように見えます。
また、中断ボタンを用意してアップロードを中断できるようにしましたが、アップロード処理自体を別スレッドにしないと中断ボタンが押された時の処理が動きません。
スレッドでは、一つのスレッドの中でファイルの数だけ処理を回しています。
(ファイルごとにスレッドにすると相手に負荷をかけるかもしれないのでやりませんでした)
アップロードは時間が掛かるので進行状況が分かるようにメッセージ表示するのと画像のファイ名を表示しているラベルの背景色を変えるようにしました。
【処理】
▽画像アップロード処理
- 「アップロード」ボタンを無効にし、「中断」ボタンを有効にします
- カレントタブの選択中の画像のパスを取得
self.note.nametowidget( self.note.select() ).checked_image_paths
- 未選択の場合はメッセージを出し、ボタンを戻して抜ける
- はてなフォトライフのフォルダを指定
デフォルトフォルダを使用する場合はHatena Blog
を設定 - 選択された画像分以下を処理
- 画像をアップロード
- 成功すればパス情報の辞書が返るので
uploaded_url
辞書に追加
画像のラベルの背景をlightgreen
に設定
※URL コピーの時にデータの有無の判断に使用 - アップロードの途中経過を表示
- 中断フラグを確認し、オンならメッセージを出力して中断
- ボタンの状態を戻します
▽ラベルの背景色を変える処理
処理している画像のラベルウィジェットのオブジェクトを直接参照する手段を用意していないので、該当のラベルウィジェットを探して背景色を設定します。
- カレントタブのタブ名を取得
- タブ名で指定したタブ内の子ウィジェット分以下を処理
【コード】
▽アップロード処理(ボタンにバインドしたメソッドとそこから起動するスレッド)
def upload_images(self, event=None): """ 選択された画像をアップロード """ th = threading.Thread(target=self.th_upload_images) th.start() def th_upload_images(self, event=None): """ 選択された画像をアップロード """ self.do_break = False # 中断フラグの初期化 self.btn_upload.config(state="disable") # アップロードボタンを押下不可にします self.btn_break.config(state="active") # 中断ボタンを押下可にします self.msg.set("アップロードを開始します") self.update_idletasks() self.uploaded_url = {} w = self.note.nametowidget(self.note.select()) # get current tab widget paths = list(w.checked_image_paths) if not paths: self.msg.set("選択された画像がありません") self.btn_upload.config(state="active") # アップロードボタンを押下可にします self.btn_break.config(state="disable") # 中断ボタンを押下不可にします return # はてなフォトライフのフォルダを指定 if self.var_dict["use_hatena_folder"].get(): folder_ = "Hatena Blog" # デフォルトフォルダ else: folder_ = self.var_dict["folder"].get() if not folder_: # 空の場合は folder_ = "Hatena Blog" # デフォルトフォルダ # 1画像ずつ呼び出しても複数画像で呼び出しても内部的に1画像ずつ処理するので1画像ずつ呼び出す for i, path_ in enumerate(paths): uploaded_url_1path = self.upload_ui.upload_image_to_hatena(paths=[path_], folder=folder_) if uploaded_url_1path: self.uploaded_url.update(uploaded_url_1path) # 辞書に辞書を追加 self.set_label_gb_in_frame_children(path_, "lightgreen") else: self.set_label_gb_in_frame_children(path_, "red") self.msg.set(f"{i+1} / {len(paths)} 件、アップロードが完了しました") self.update_idletasks() # 中断の確認 if self.do_break: self.msg.set(f"{i+1} / {len(paths)} 件、アップロードしたところで中断しました") break; self.btn_upload.config(state="active") # アップロードボタンを押下可にします self.btn_break.config(state="disable") # 中断ボタンを押下不可にします
▽アップロードの完了をラベルの背景色を変えて知らせる処理
def set_label_gb_in_frame_children(self, image_path:str, color:str): """ image_pathをtextオプション持つチェックボックスを含むフレームのラベルの背景を変える Args: bool: 設定値 """ tab_name = self.note.tab("current", "text") for child_ in self.frame_children.get(tab_name): # self.frame_childrenはタブごとのFrameの集まり for item_ in child_.winfo_children(): # FrameにはCheckbuttonとLabel if type(item_) == tk.Checkbutton: is_same_path = item_.cget("text") == image_path # 引数と同じパスか判断 elif type(item_) == tk.Label: label_ = item_ if is_same_path: label_.config(background = color) label_.update() # update_idletasksでは更新されない return
▽アップロード中断処理
def break_upload(self, event=None): self.btn_break.state(["pressed"]) self.do_break = True # 中断フラグをオンにする
◆アップロードされた画像のURLをコピー
はてなフォトファイルに画像をアップロードした時に返される XML から画像の URL を取得し、加工してクリップボードにコピーします。
画像の URL は次のような形式です。
- 画像のURL:
http://f.hatena.ne.jp/images/fotolife/n/naoya/XXXXXXXX/XXXXXXXXXXXXXX.jpg
- foto 記法:
f:id:naoya:XXXXXXXXXXXXXX:image
これらをこのままコピーするのと画像としての書式を付けてコピーする方法を提供します。
書式の場合は次のようになります。
- Markdownの画像:
![](http://f.hatena.ne.jp/images/fotolife/n/naoya/XXXXXXXX/XXXXXXXXXXXXXX.jpg) "タイトル"
- foto 記法:
[f:id:naoya:XXXXXXXXXXXXXX:plain:title="タイトル"]
クリップボードにコピーするために pyperclip モジュールを使用しました。
使い方は簡単で pyperclip.copy(文字列)
とするだけです。(インポートが必要です)
【処理】
▽画像のURLコピー処理
- 画面の設定項目(ウィジェット変数の辞書)を辞書に変換
- カレントタブのタブ名を取得
- カレントタブのウィジェットを取得
- タブ名で指定したタブ内の子ウィジェット分以下を処理
clip_strings
が空でなければ、クリップボードにコピー
▽はてなフォトライフの画像URL取得処理
- 設定項目辞書から設定項目を取得
- アップロードした画像の URL 情報から該当パスの情報(画像のURLとfoto記法のタプル)を取得
- 設定項目に合わせて画像のURLかfoto記法のどちらかを取得
- パスからファイル名(拡張子なし)を取得
- 設定項目でfoto記法で出力の場合以下を処理
- 「:image」を「:plain」に置換
- 設定項目でタイトルを付加ならタイトルを付加
- 設定項目でその他のオプションがあれば付加
- 設定項目で中身だけなら、中身だけにする
- 設定項目でfoto記法で出力以外の場合以下を処理
- 設定項目でタイトルを付加ならタイトルを付加
- 設定項目で中身だけなら、中身だけにする
- 結果を返す
【コード】
▽画像のURLコピー処理
def copy_image_url(self): """ Args: bool: 設定値 """ clip_strings = [] self.msg.set("") settings_dict = {key: value.get() for key, value in self.var_dict.items()} # 設定をウィジェット変数から値に変換 tab_name = self.note.tab("current", "text") w = self.note.nametowidget(self.note.select()) # get current tab widget for child_ in self.frame_children.get(tab_name): # self.frame_childrenはタブごとのFrameの集まり for item_ in child_.winfo_children(): # FrameにはCheckbuttonとLabel if type(item_) == tk.Checkbutton: selected = item_.cget("text") in w.checked_image_paths # 選択されている画像か判断 path_ = item_.cget("text") elif type(item_) == tk.Label: uploaded = item_.winfo_rgb(item_.config("background")[4]) == item_.winfo_rgb("lightgreen") if uploaded and selected: clip_strings.append(self.get_image_url(path_, settings_dict)) if clip_strings: pyperclip.copy(os.linesep.join(clip_strings)) self.msg.set("コピーしました") else: self.msg.set("コピーするものがありません")
▽はてなフォトライフの画像URL取得処理
def get_image_url(self, file_name:str, settings_dict:dict) -> str: result = "" isfoto = settings_dict.get("foto", False) istitle = settings_dict.get("add_title") isonly_url = settings_dict.get("only_url", False) isoption = settings_dict.get("add_options", "") url_or_foto = self.uploaded_url.get(file_name) # タプルが返る(url, foto) s1 = url_or_foto[isfoto] title_ = os.path.splitext(os.path.basename(file_name))[0] if isfoto: # fotolife記法の編集 # [f:id:はてなID:画像番号:title="タイトル"] s1 = s1.replace(":image", ":plain") # :imageはfotolifeが起動するので:plainに置換 if istitle: s1 = s1 + f':title="{title_}"' if isoption: s1 = s1 + isoption if not isonly_url: s1 = f"[{s1}]" else: # ![](url "タイトル") if istitle: s1 = f'{s1} "{title_}"' if not isonly_url: s1 = f"![]({s1})" result = s1 return result
◆別記事で説明している内容
◇画像表示にCheckbuttonクラスを使用
▶Checkbutton クラスの使い方については こちらの記事を参照してください。
📔 ◆画像表示にCheckbuttonクラスを使用 - ScrolledFrameとwrapped_gridで作る画像一覧の作り方【Python】 🔗
説明している内容は次の通りです。
◇ScrolledFrame クラスの使い方
▶ScrolledFrame クラスの使い方については こちらの記事を参照してください。
📔 ◆ScrolledFrame-クラスの使い方 - ScrolledFrameとwrapped_gridで作る画像一覧の作り方【Python】 🔗
説明している内容は次の通りです。
◇プレビュー処理
▶プレビュー処理については こちらの記事を参照してください。
📔 ◆プレビュー処理 - ScrolledFrameとwrapped_gridで作る画像一覧の作り方【Python】 🔗
説明している内容は次の通りです。
◆Tkinterの使い方
Tkinter(ティーキンター)は Python が標準で提供している GUI パッケージです。
他のプログラムの開発ツールで提供されているようなルック&フィールな GUI ツールではありません。
従って、画面を構成するには、すべてコードを書く必要があります。
Tkinter を使用するには import が必要です。
import tkinter as tk
慣例的に tk と別名を付けるようです。
◇Tkinterの使用ウィジェット
Tkinter がサポートする GUI の部品をウィジェット (widget) と呼びます。
ここでは次のウィジェットを使用しています。
◇使用ウィジェット
ウィジェットは基本的にインスタンスを作成し、表示テキストや表示方法などを指定して使用します。
今回使用しているウィジェットです。
パッケージ | ウィジェット名 | 用途 | 見た目 |
---|---|---|---|
tkinter | Frame フレーム |
ウィジェットの受け皿 | |
tkinter | Label ラベル |
文字の表示 | |
tkinter | Button ボタン |
押すと処理が動くボタン | |
tkinter | Checkbutton チェックボックス |
チェックでオン/オフを設定 | |
tkinter | Entry エントリー |
テキストを入力するボックス | |
tkinter.ttk | Notebook ノートブック |
タブ切り替え表示 | |
独自 | ScrolledFrame スクロールバー付フレーム |
画面をスクロール |
◇画面表示の特徴
画面表示には次の特徴があります。
- 画像変換の結果を別タブで表示
- 画像を格子状に配置(wrapped_grid() メソッドを使用)
- ウィンドウのサイズを変更すると表示する画像の列数をウィンドウの幅に合わせる(wrapped_grid() メソッドを使用)
- 縦スクロールバーを表示(ScrolledFrame クラスを使用)
- マウスホイールでスクロール(ScrolledFrame クラスを使用)
- 右側に設定項目を配置
◇画面構成 - フレーム構造
プログラムを作るにあたって、どのような画面にするか決めなくてはなりません。画面構成を考えるということですね。
画面を構成するというのは、GUI 部品(ウィジェット)を画面に配置することです。
ウィジェットは設定したテキストに合わせてサイズが決まるので、そのまま使用しても画面構成上問題ないものと、広げないと見栄えが良くないものが出てきます。
ウィジェットを広げる場合、他のウィジェットとの位置関係で思ったように配置できないことがあります。
そのようなときに Frame ウィジェットを使用して区分けをして希望する位置に配置します。
引き出しの整理に使うトレイのようなイメージでしょうか。
今回の画面構成です。
- u_frame (frame)
- btn_f_sel (Button)
- btn_resize (Button)
- btn_upload (Button)
- btn_break (ttk.Button)
- btn_copy (Button)
- btn_select_all (Button)
- btn_deselection (Button)
- btn_preview (Button)
- lbl_msg (Label)
- b_frame (frame)
- note (Notebook)
- frame4images (ScrolledFrame)
- frame1 (Frame)
- check_bon (Checkbutton)
- label_f_name (Label)
- frame1 (Frame)
- frame4images (ScrolledFrame)
- note (Notebook)
- r_frame (ScrolledFrame)
※「u_frame」などはプログラムで使用した変数名です。()内はクラス名です。
入力をする部分と出力をする部分で大きくフレームを分けています。
結果を表示する部分は余った領域をすべて使用して、できるだけ大きく表示させています。
◇ウィジェットの配置 - pack
▶説明は別記事を参照してください
📔 ◇ウィジェットの配置 - pack - SQLクライアントアプリの作り方(Tkinterで表)【Python】 🔗
◇動作をウィジェットに割り当てる - バインド
▶説明は別記事を参照してください
📔 □動作をウィジェットに割り当てる - Excel viewerアプリの作り方(Tkinterでタブと表)【Python】 🔗
◆必要なパッケージ
◇必要な Python パッケージ
- pillow
- pip install Pillow
- from PIL import Image
- TkinterDnD2
- pip install tkinterdnd2
- from tkinterdnd2 import *
- pyperclip
- pip install pyperclip
- import pyperclip
- requests
- pip install requests
- import requests
- tomli
- pip install tomli
- import tomli
- tomli-w
- pip install tomli-w
- import tomli-w
◇必要な Python 独自モジュール
- image_resize_sig.py ver 1.0.1
- fotolifeUpload.py ver 1.0.1
- toml_file_util.py ver 1.0.0
- tkinter_libs.py ver 1.0.1
◆ソースの取得
全体のソースはこちらから取得できます。
- 画像ツール
- ソース:blog_image_tool.py
- TOML ファイル:blog_image_tool.toml
- 取得先:GitHub juu7g/Python-blog-image-tool
ライブラリのソースはこちらから取得できます。
(点在してて申し訳ありません)
- 画像変換
- ソース:image_resize_sig.py
- 取得先:GitHub juu7g/Python-Image-resize-sig
- 画像アップロード
- ソース:fotolifeUpload.py
- 取得先:GitHub juu7g/Python-fotolife-Upload
- TOML設定ファイルユーティリティ
- ソース:toml_file_util.py
- ソース:toml_file_create.py (TOMLファイル作成用)
- 取得先:GitHub juu7g/Python-TOML-util
- Tkinter用ライブラリ
- ソース:tkinter_libs.py
- 取得先:GitHub juu7g/Python-tkinter-libs
- 画像ツールのソースと同じフォルダに置いて使います
◆さいごに
アプリを公開してからずいぶん経ってしまいましたが、コードの説明記事を公開します。
ライブラリとして使っているコードをパッケージとしてリリースできていれば、もう少し簡単にご利用いただけるのではないかと思います。
パッケージの作り方を勉強して、パッケージ化していけたらと思っています。
今のところ、パッケージにするほどバリバリのライブラリでもないので、優先順位を下げています。
もうしばらくは、こじんまりしたアプリを作っていこうと思っています。
こんなアプリが欲しいということがあれば教えていただけると幸いです。
◇ご注意
本記事は次のバージョンの下で動作した内容を基に記述しています。
- Python 3.8.5
- Pillow 8.3.0
- TkinterDnD2 0.3.0
- Requests 2.25.1
- pyperclip 1.8.2
◇免責事項
ご利用に際しては、『免責事項』をご確認ください。
お気づきの点がございましたら『お問い合わせ』からお問い合わせください。