【Python】enumerate()関数で配列の要素とインデックス番号を取得

Pythonで配列の各要素をfor文で取得する場合は、以下のようにします。

colors = ['black', 'white', 'red', 'green', 'blue', 'yellow', 'magenta', 'cyan']

for c in colors:
    print(c)

(実行結果)

ここに、インデックス番号付きで各要素をする場合は

colors = ['black', 'white', 'red', 'green', 'blue', 'yellow', 'magenta', 'cyan']

i = 0
for c in colors:
    print(i, c)
    i += 1

(実行結果)

とも書けますが、Pythonのenumerate()関数を使うと

colors = ['black', 'white', 'red', 'green', 'blue', 'yellow', 'magenta', 'cyan']

for i, c in enumerate(colors):
    print(i, c)

(実行結果)

となります。

さらに開始値を0以外にしたい場合は、enumerate()関数の引数に開始値を追加し、

colors = ['black', 'white', 'red', 'green', 'blue', 'yellow', 'magenta', 'cyan']

for i, c in enumerate(colors, 11):
    print(i, c)

(実行結果)

とします。

新型コロナワクチン予約できない!予約取得のコツ

新型コロナワクチンの予約が全く取れず、3回(3週)連続で予約の取得し失敗しました。

そうこうしている間に、コロナワクチン予約の対象年齢の範囲が広がっていき、もう、ずっと予約が取れないんじゃないか?と不安に思っていました。

そこで、4回目はこれまでの反省を生かし、どうすれば予約が取れるのか?自分なりに考えて予約をしたところ、ようやく予約が取れました!

実際に私がどのようにしたら予約が取れたのか?を紹介します。

ただし、市町村によっては、予約方法が異なります。
ここでの記事は https://jump.mrso.jp/123456/ のサイトを使って予約を行う場合です。
数字の部分は市町村によって異なります。

事前準備

接種会場候補を決める

接種会場は各市町村のホームページやワクチン接種のクーポン券に同封されている資料に記載されていると思います。

最初は近所のクリニックでの予約にこだわっていたのですが、ワクチン接種の実施日そのものも少なく、予約の取得は困難です。

そのため、公民館などで実施されている集団接種会場を優先的に選んだ方が予約が取りやすくなります。また、接種日時も分かる場合は、当然、多い方が予約が取りやすいです。

そうして接種会場の候補を3会場ぐらい決めておくとよいと思います。

都合の悪い日をメモしておく

予約する日から3週間以内ぐらい(予約可能な期間)で、都合の付かない日をメモしておく事をおススメします。

実際に予約する時に予定を確認していると、その間に予約が埋まってしまいます。

また、自治体によっては、1回目の予約の3週間後に自動的に2回目の予約が入る場合もあるので、2回目の予約日も考慮しておきます。

予約方法を確認しておく

ワクチン予約は時間との勝負です。

実際の予約サイトで、何がどのように表示されているのか?接種会場の選択はどのようにするのか?自分の候補の会場はどの辺に表示されているのか?を確認しておきます。

コロナワクチンの予約方法

コロナワクチンの予約は電話もしくはネットで出来ますが、私はネットで行いました。
また、予約方法は市町村によって、異なるようです。

ここでは、https://jump.mrso.jp/123456/ (赤字部分は市町村により異なります)のサイトで予約を行う場合です。

ネットもPCかスマホ(LINE)で出来ますが、PCを使うと同時に複数のタブでページを開く事ができるので、おそらくPCの方が予約が取りやすいと思います。
以下はPCの場合です。

(参考)
https://www.mrso.jp/portal/covid19-vaccine/web-reserve.html

1.予約ページへアクセス

ワクチンの予約は、https://jump.mrso.jp/123456/ のサイトへアクセスします。

リンク先へ行くと、別のページ(https://www.covid19-vaccine.mrso.jp/123456/VisitNumbers/visitnoAuth/)へ飛び、以下のような画面が表示されます。(赤字部分は市町村により異なります。)

ここで、市区町村コード(事前に入力されています)と接種券番号、生年月日を入力し、認証をクリックします。

2.お客様情報の入力

はじめてページにアクセスしたときは、名前、生年月日などを入力する必要がありますが、2回目以降は、入力が省略されるので、予約が開始される前に予約ページにアクセスし、各種情報を入力しておくことをおススメします。

上図の予約を進めるをクリックします。

3.接種会場の選択

表示されている会場一覧の中から、希望の会場の部分に表示されている 詳細・予約 ボタンをクリックします。

画面のキャプチャを忘れましたが、予約可能な接種会場がある場合は、会場一覧が表示されます。

4.接種希望日の選択

会場を一覧を選択すると、会場ごとの予約ページが表示されるので、予約カレンダーを見るをクリックし、カレンダーの中から、予約希望日をまたはが表示されている部分をクリックします。×はクリックしても何も表示されません。

※画面をキャプチャするのを忘れました。(正確には、そんな余裕がありませんでした)

5.希望時間帯を選択

選択した接種日の予約可能な時間帯が表示されるので、この中から希望時間をクリックします。

時間帯を選択後、予約内容の確認→予約するのボタンをクリックし、予約が完了します。

予約時のポイント

ここからが重要です。

予約開始時間前の30分~1時間ぐらい?前に、上記予約方法の1~3まで

1.予約ページへアクセス

2.お客様情報の入力

3.接種会場の選択

のページまで移動し、接種会場の画面で予約開始時間まで待機します。
(※地域によっては、予約開始前に最初のページから次に進めない場合もあるようです。)

何分程度前から待機した方がいいか?は、市町村や予約の混雑状況により異なると思いますが、私は1時間前から待機しました。

ただし、この会場選択のページを表示したまま放置しておくと、途中で接続が解除されてしまうため、接種会場一覧の部分をクリックするか、ブラウザの更新ボタンを定期的(1分間隔ぐらい?)にクリックし、切断されないようにクリックを続けます。
私は接種会場一覧の部分を定期的にクリックしました。

予約開始時間になると、接種会場の一覧が表示されますが、ここからは時間との勝負です。

接種会場の選択→接種希望日の選択→希望時間帯の選択→予約内容の確認→予約

までの一連の操作は、1秒たりとも無駄にしないつもりで、間髪入れずに操作します。

私は希望時間帯の選択の部分で、何時がいいだろう??と30秒ぐらい考えてしまったせいで、3回目の予約の挑戦は失敗しました。

最初のページでアクセスできない場合

最初のページ(https://jump.mrso.jp/123456/ )に接続したとき、予約開始時間直前にアクセスすると、アクセス集中でその先に進めません。

このアクセス集中画面が表示された状態からは、ほぼ予約ができなく、接続できたと思ったら予約枠がほぼ埋まった状態になっているかと思います。

それでも僅かな可能性に賭けて、ブラウザのタブで予約ページをいくつも表示しておくと、もしかすると予約できるかも?しれません。

他のサイトでは、ブラウザの更新をした方が良いような情報もありますが、基本的に放置で大丈夫な気がします。かと言って、本当に放置で大丈夫なの?とも思うので、数個のタブだけ更新をして他は放置が良いと思います。

アクセス集中の画面から、予約の画面に切り替わると、タブのアイコンが切り替わるので、速攻で予約しましょう。

まとめ

予約のポイントは

●接種会場の候補を複数決めておく
●予定の付かない日をメモしておく
●予約ページへは、予約開始時間前にアクセスし、接種会場一覧のページで待機する
●予約日、時間帯の選択で悩まない(時間をかけない)

となります。

ここまでしないと予約するのが困難でした。。

2021年8月現在では、ワクチン接種を促すCMとか流れていますけど、ワクチン接種をしたくない人より、ワクチン接種したくても予約が取れない人の方が圧倒的に多いと思うんですよね~
もう少し予約が取りやすくなってくれないですかね?!

【Windows11】プリンターの追加

Windows11において、プリンターを追加する方法を紹介します。

まず、設定を表示します。

設定を表示するには、スタートボタンをクリックし設定をクリックします。

もしくは、スタートボタンを右クリックし、表示されたメニューの設定をクリックします。

表示された設定画面の左側のBluetoothとデバイスを選択し、プリンターとスキャナーをクリックします。

この時点でプリンタの電源は入れた状態にして、接続できるようにしておきます。

プリンターまたはスキャナーを追加しますの右側のデバイスの追加をクリックします。

すると、接続可能なプリンタが表示されるので、デバイスの追加をクリックします。

すると、プリンターのインストールが始まります。

プリンタが追加されると、プリンタとスキャナーに接続されたプリンタが表示され、これで設定は完了です。

【Python】特殊メソッド

Python以外でクラスを使ったプログラムを作成すると、クラスをインスタンスした時にはコンストラクタが呼ばれ、クラスオブジェクトを解放した時にはデストラクタが呼ばれます。

コンストラクタやデストラクタのように、ある特定の操作をクラスオブジェクトに対して行った時に呼ばれるメソッドをPythonでは特殊メソッド(Special Method)といいます。

Pythonではインスタンスした時や解放した時以外にも様々な特殊メソッドがあります。

(参考)

https://docs.python.org/ja/3/reference/datamodel.html?highlight=__init__#special-method-names

特殊メソッドはアンダーバー2つ(__)で前後が挟まれたメソッド名となります。

 

Deep Learningのプログラムでは比較的この特殊メソッドが使われるので、実際に使った事のある特殊メソッドを紹介します。

__init__(self [, …])

コンストラクタ

クラスがインスタンスされた時に呼ばれます。

__del__(self)

デストラクタ

del object のように、クラスが解放される時に呼ばれます。

ただし、呼ばれない場合もあります。

どちらかというと、挙動はファイナライザに近いです。

__call__(self [, …])

object(a, b, c)のように、クラスオブジェクトに引き数を追加し、クラスオブジェクトをメソッドのように呼び出します。

__len__(self)

len(object)のようにした時に、要素数(int型)を返します。

何の要素数か?は特に規定はなくユーザー次第となります。

__getitem__(self, key)

object[1]のように、クラスオブジェクトに角カッコ付きで配列のように呼び出した時に要素を返します。

__setitem__(self, key, value)

object[1] = 3のように、クラスオブジェクトに角カッコ付きで配列のように、要素を設定します。

__delitem__(self, key)

del object[key] のようにクラスオブジェクトに角カッコ付きで配列の削除のように呼び出した時に要素を削除します。

サンプル

y = a0 + a1*x + a2*x^2+ a3*x^3 +… となるようなn次関数の係数をコンストラクタで設定し、xの値を__call__メソッドで呼び出し、yの値を取得するサンプルです。

class TestClass:
    '''
    y = a0 + a1*x + a2*x^2+ a3*x^3 +・・・の計算
    '''
    def __init__(self, a):
        ''' コンストラクタ '''
        print("__init__")
        self.coeff = a

    def __del__(self):
        ''' デストラクタ '''
        print("__del__")

    def __call__(self, x):
        ''' 呼び出し可能オブジェクト '''
        print("__call__")

        sum = 0
        for i in range(len(self.coeff)):
            sum += self.coeff[i] * x**i

        return sum

    def __len__(self):
        ''' 要素数 '''
        print("__len__")
        return len(self.coeff)

    def __getitem__(self, index):
        ''' 指定した番号の要素取得 '''
        print("__getitem__")
        return self.coeff[index]

    def __setitem__(self, index, value):
        ''' 指定した番号の要素設定 '''
        print("__setitem__")
        self.coeff[index] = value

    def __delitem__(self, index):
        ''' 指定した番号の要素削除 '''
        print("__delitem__")
        del self.coeff[index]

#------------------------------------------------------

# クラスのインスタンス(__init__メソッドの呼び出し)
object = TestClass([2, 1.5, 3]) # y = 2 + 1.5*x + 3*x**2

# __call__メソッドの呼び出し
print(object(3.5))

# 要素数(__len__メソッドの呼び出し)
print(len(object))

# 要素の取得(__getitem__メソッドの呼び出し)
print(object[1])

# 要素の設定(__setitem__メソッドの呼び出し)
object[1] = 0.5
print(object[1])

# 要素の削除(__delitem__メソッドの呼び出し)
del object[2]
print(len(object))

# クラスの解放
del object

(実行結果)

まとめ

個人的には特殊メソッドはコンストラクタ以外は、あまり使う事はないのですが、例えば画像処理で特殊メソッドを駆使するとすると、ガウシアンフィルタのような畳み込みフィルタでは、コンストラクタでカーネルの係数を設定し、__call__メソッドで画像データを引き数で渡して、フィルタ後の画像データを取得するような使い方ができると思います。

特殊メソッドの呼び出され方は決まっていますが、実際にどのようにつかうか?はユーザー次第なので、うまく使えば、キレイなコードを書く事ができると思います。

【Python】変数がNoneかどうか確認する方法

OpenCVで画像ファイルを開く場合では、ファイルがみつからない、日本語パスのためファイルが読み込めないなどのエラーが起きやすいため、画像の変数がNoneかどうかエラーチェックをしたくなります。

そのため、エラーチェックをしたプログラムを書いてみます。

(ダメな例)

import cv2

# OpenCVで画像ファイルを開く
img = cv2.imread("image.bmp", cv2.IMREAD_UNCHANGED)

if img == None:
    print("Load Error")
else:
    cv2.imshow("Image", img)
    cv2.waitKey(0)

このプログラムは一見良さそうで、実際に img == None のときは、正しく動作します。

しかしながら、 img != None のとき(画像ファイルが正しく読み込めたとき)は以下のような例外が発生します。

The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

例外が発生しないようにするには、 == を使うのではなく、 is を使います。

import cv2

# OpenCVで画像ファイルを開く
img = cv2.imread("image.bmp", cv2.IMREAD_UNCHANGED)

if img is None:
    print("Load Error")
else:
    cv2.imshow("Image", img)
    cv2.waitKey(0)

他にも

if img != None:

としたい場合は

if img is not None:

のように != ではなく、 is not を使います。

【Python/tkinter】LabelFrame(ラベルフレーム、グループボックス)

複数のウィジェットをラベル付きの枠線で囲うウィジェットをLabelFrame(ラベルフレーム)と言います。

tkinter以外ではグループボックスなどと言われます。

このラベルフレームは何かの設定値など、共通の目的を持ったウィジェットを一つにまとめるときに用いられます。

基本的な使い方はFrameに似ていますが、Frameに枠線とラベルが追加されています。

(ラベルフレームの実行例)

(サンプルプログラム)

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.title("ラベルフレームの作成")     # ウィンドウタイトル
        self.master.geometry("300x200")       # ウィンドウサイズ(幅x高さ)

        #--------------------------------------------------------
        # ラベルフレーム1の作成
        self.labelframe1 = tk.LabelFrame(self.master, text = "フレーム1")
        
        # ラジオボタンに設定する値
        self.radio_value1 = tk.IntVar(value = 0)

        # ラジオボタンの作成
        self.radio1 = tk.Radiobutton(self.labelframe1, text = "項目1", variable = self.radio_value1, value = 0)
        self.radio2 = tk.Radiobutton(self.labelframe1, text = "項目2", variable = self.radio_value1, value = 1)
        self.radio3 = tk.Radiobutton(self.labelframe1, text = "項目3", variable = self.radio_value1, value = 2)
        self.radio1.pack()
        self.radio2.pack()
        self.radio3.pack()

        # ラベルの配置
        self.labelframe1.pack()

        #--------------------------------------------------------
        # ラベルフレーム2の作成
        self.labelframe2 = tk.LabelFrame(self.master, text = "フレーム2", labelanchor = "n", width = 200, height = 100)
        self.labelframe2.propagate(False) # 幅と高さを指定する場合はこの設定が必要

        # ラジオボタンに設定する値
        self.radio_value2 = tk.IntVar(value = 1)

        # ラジオボタンの作成
        self.radio4 = tk.Radiobutton(self.labelframe2, text = "項目1", variable = self.radio_value2, value = 0)
        self.radio5 = tk.Radiobutton(self.labelframe2, text = "項目2", variable = self.radio_value2, value = 1)
        self.radio6 = tk.Radiobutton(self.labelframe2, text = "項目3", variable = self.radio_value2, value = 2)
        self.radio4.pack(anchor = tk.W)
        self.radio5.pack(anchor = tk.W)
        self.radio6.pack(anchor = tk.W)

        # ラベルの配置
        self.labelframe2.pack()
        #--------------------------------------------------------


if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

構文

オブジェクト = tk.LabelFrame(親ウィジェット, オプション1 = 設定値, オプション2 = 設定値,・・・)

オプション

オプション名 説明
bd 枠線の太さを指定します。(borderwidthと同じ)
borderwidth bdと同じ
bg 背景色を指定します。(backgroundと同じ)
background bgと同じ
cursor ウィジェット上にマウスポインタがある際のカーソルの種類を指定します。
(参考)https://tkdocs.com/shipman/cursors.html
font ラベルのフォントを指定します。
height フレームの高さを画素数で指定します。
highlightbackground
highlightcolor
highlightthickness
labelanchor ラベルの表示位置を指定します。
【設定値】e, en, es, n, ne, nw, s, se, sw, w, wn, ws
labelwidget ラベルの代わりにButtonなどのウィジェットを指定します。
padx フレームの内側に配置するウィジェットまでの横方向の隙間を指定します。
【初期値】0
pady フレームの内側に配置するウィジェットまでの縦方向の隙間を指定します。
【初期値】0
relief フレームのスタイルを指定します。
【設定値】tk.RAISED, tk.GROOVE, tk.SUNKEN, tk.RIDGE, tk.FLAT
takefocus
text ラベルの文字列を指定します。
width フレームの幅を画素数で指定します。

補足説明

ラベルフレームのサイズ(幅と高さ)指定

widthとheightの値を指定しても、デフォルト状態ではLabelFrameのサイズは変わらず、LabelFrameに配置したウィジェットの大きさに合わせて自動調整されます。

この自動調整を無効にするには ラベルフレームオブジェクト.propagate(False) を実行します。

labelanchorについて

labelanchorでラベルの位置を指定します。

実際にラベルが表示される位置は下図を参照してください。

labelwidgetについて

labelwidgetはラベルの代わりにウィジェットを配置することができます。

下図はラベルの代わりにButtonを配置した例になります。

labelwidget = ウィジェットのオブジェクト

のように指定します。

【Python/tkinter】ウィジェットの配置(place)

ウィジェットを配置するには、pack,grid,placeの3つのメソッドがありますが、ここではplaceについて説明します。

placeではウィジェットの位置を座標を指定して配置します。

初期状態では下記のように親(配置先)の左上を原点として、ウィジェットの左上の座標(初期状態の場合、anchorにより変更可)を指定します。

以下に簡単なサンプルを示します。

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.title("ウィジェットの配置(place)")     # ウィンドウタイトル
        self.master.geometry("300x200")       # ウィンドウサイズ(幅x高さ)

        #--------------------------------------------------------
        # ボタンの作成
        button = tk.Button(self.master, text = "ボタン")
        # 座標を指定して配置
        button.place(x = 100, y = 50)
        #--------------------------------------------------------

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(実行画面)

構文

ウィジェット.place(オプション1 = 設定値, オプション2 = 設定値,・・・)

オプション

オプション名 説明
x 配置するX座標を指定します。
y 配置するY座標を指定します。
relx 配置先の座標を配置先の幅に対して相対的な位置を0.0~1.0の値で指定します。
0.0:左端、1.0:右端
rely 配置先の座標を配置先の高さに対して相対的な位置を0.0~1.0の値で指定します。
0.0:上端、1.0:下端
anchor ウィジェットを配置する座標の基準位置を指定します。
【設定値】tk.N, tk.NE, tk.E, tk.SE, tk.S, tk.SW, tk.W, tk.NW, tk.CENTER
【初期値】tk.NW
width ウィジェットの幅を画素数で指定します。
height ウィジェットの高さを画素数で指定します。
relwidth ウィジェットの幅を配置先の幅に対して相対的な値(0.0~1.0)で指定します。
relheight ウィジェットの高さを配置先の高さに対して相対的な値(0.0~1.0)で指定します。

anchor

anchorの基準位置はウィジェットに対して以下のようになります。

サンプル

各種オプション設定を使ったサンプルを示します。

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.title("ウィジェットの配置(place)")     # ウィンドウタイトル
        self.master.geometry("300x400")       # ウィンドウサイズ(幅x高さ)

        #--------------------------------------------------------
        # ボタンの作成
        button1 = tk.Button(self.master, text = "ボタン1")
        button1.place(x = 30, y = 20)

        button2 = tk.Button(self.master, text = "ボタン2")
        button2.place(x = 80, y = 70, anchor = tk.CENTER) # 指定座標の基準位置変更

        button3 = tk.Button(self.master, text = "ボタン3")
        button3.place(x = 30, y = 90, width = 100, height = 40) # ウィジェットのサイズ指定

        button4 = tk.Button(self.master, text = "ボタン4")
        button4.place(relx = 0.5, rely = 0.4, anchor = tk.CENTER) # 相対座標指定

        button5 = tk.Button(self.master, text = "ボタン5")
        button5.place(relx = 0.5, relwidth = 0.8, y = 200, anchor = tk.CENTER) # ウィジェットのサイズを相対的に指定
        #--------------------------------------------------------
if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(実行画面)

【Python/tkinter】ウィジェットの配置(grid)

ウィジェットを配置するには、pack,grid,placeの3つのメソッドがありますが、ここではgridについて説明します。

gridでウィジェットを配置するのは、どことなくエクセルのセルにウィジェットを配置するようなイメージに似ています。

こんなイメージ↓

ただし、gridでは行番号(row)、列番号(column)は0(ゼロ)から始まります。

また、エクセルにはセルを結合して中央揃えという機能がありますが、gridにも同様の機能があり、rowspancolumnspanを使ってセルを結合します。

最初のエクセルのイメージで配置したプログラムがこちら↓(ウィジェットの範囲が分かり易いように背景色をつけました)

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.title("ウィジェットの配置(grid)")     # ウィンドウタイトル
        self.master.geometry("300x180")       # ウィンドウサイズ(幅x高さ)

        #--------------------------------------------------------
        # ラベルの作成
        label1 = tk.Label(self.master, text = "ラベル1", bg = 'cyan1')
        label2 = tk.Label(self.master, text = "ラベル2", bg = 'green1')
        label3 = tk.Label(self.master, text = "ラベル3", bg = 'yellow1')
        label4 = tk.Label(self.master, text = "ラベル4", bg = 'pink1')
        label5 = tk.Label(self.master, text = "ラベル5", bg = 'MediumPurple1')
        label6 = tk.Label(self.master, text = "***ラベル6***", bg = 'LightSteelBlue1')

        #--------------------------------------------------------
        # gridでウィジェットの配置
        label1.grid(row = 0, column = 1, columnspan = 3, sticky = tk.W+tk.E)
        label2.grid(row = 0, column = 0, rowspan = 5, sticky = tk.N+tk.S)
        label3.grid(row = 1, column = 1)
        label4.grid(row = 1, column = 3)
        label5.grid(row = 2, column = 2)
        label6.grid(row = 3, column = 1, columnspan = 3)
        #--------------------------------------------------------

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

構文

ウィジェット.grid(オプション1 = 設定値, オプション2 = 設定値,・・・)

オプション

オプション名 説明
column ウィジェットを配置する列番号(0始まり)を指定します。
columnspan グリッドを横方向に結合する数を指定します。
ipadx ウィジェットの内側の横方向の隙間を設定します。
ipady ウィジェットの内側の縦方向の隙間を設定します。
padx ウィジェットの外側の横方向の隙間を設定します。
pady ウィジェットの外側の縦方向の隙間を設定します。
row ウィジェットを配置する行番号(0始まり)を指定します。
rowspan グリッドを縦方向に結合する数を指定します。
sticky グリッド内のウィジェットを配置する位置
アンカーの機能にも似ていますが、例えば上下(tk.N+ tk.S)を指定すると、ウィジェットが上下方向にグリッド内いっぱいに広がります。【設定値】tk.N, tk.S, tk.W, tk.E, tk.NW, tk.NE, tk.SW, tk.SE, tk.NSEW
および上記組み合わせ(tk.N+ tk.Sなど)

ウィンドウのリサイズに合わせて行、列の幅、高さを調整する

gridでウィジェットを配置すると、行や列方向の最大の高さ、幅に合わせてグリッド状に配置されますが、ウィンドウをリサイズしても幅や高さが変わる事がありません。

↓ウィンドウのリサイズ

そこで、ウィンドウに合わせて行の高さを調整するにはgrid_rowconfigure()メソッドを、列の幅を調整するにはgrid_columnconfigure()メソッドを用います。

 

構文

親ウィジェット.grid_columnconfigure(列番号, オプション)
親ウィジェット.grid_rowconfigure(行番号, オプション)

オプション

オプション名 説明
weight 1以上の整数を指定すると、幅/高さが調整されます。
複数の行もしくは列に対してweightを指定すると、指定したweightの値の比率で幅/高さが調整されます。
minsize 最小の幅/高さを画素単位で指定します。
pad 列/高さの隙間を画素単位で指定します。

 

サンプル

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.title("ウィジェットの配置(grid)")     # ウィンドウタイトル
        self.master.geometry("300x180")       # ウィンドウサイズ(幅x高さ)

        #--------------------------------------------------------
        # ラベルの作成
        lbl_00 = tk.Label(self.master, text = "row,col")

        lbl_col1 = tk.Label(self.master, text = "col1")
        lbl_col2 = tk.Label(self.master, text = "col2")
        lbl_col3 = tk.Label(self.master, text = "col3")

        lbl_row1 = tk.Label(self.master, text = "row1")
        lbl_row2 = tk.Label(self.master, text = "row2")
        lbl_row3 = tk.Label(self.master, text = "row3")

        #--------------------------------------------------------
        # Entry(テキストボックス)の作成
        entry1 = tk.Entry(self.master, width = 20)
        entry2 = tk.Entry(self.master, width = 20)
        entry3 = tk.Entry(self.master, width = 20)

        #--------------------------------------------------------
        # ボタンの作成
        button1 = tk.Button(self.master, text = "...")
        button2 = tk.Button(self.master, text = "...")
        button3 = tk.Button(self.master, text = "...")

        #--------------------------------------------------------
        # gridでウィジェットの配置
        lbl_00.grid(row = 0, column = 0)

        lbl_col1.grid(row = 0, column = 1)
        lbl_col2.grid(row = 0, column = 2)
        lbl_col2.grid(row = 0, column = 3)

        lbl_row1.grid(row = 1, column = 0)
        lbl_row2.grid(row = 2, column = 0)
        lbl_row3.grid(row = 3, column = 0)

        entry1.grid(row = 1, column = 1, sticky=tk.EW) # 幅に合わせて大きくする
        entry2.grid(row = 2, column = 1, sticky=tk.EW) # 幅に合わせて大きくする
        entry3.grid(row = 3, column = 1, sticky=tk.EW) # 幅に合わせて大きくする

        button1.grid(row = 1, column = 3)
        button2.grid(row = 2, column = 3)
        button3.grid(row = 3, column = 3)

        #--------------------------------------------------------
        # ウィンドウのリサイズに合わせてEntryの幅(column=1)を広げる
        self.master.grid_columnconfigure(1, weight=1) # 列の調整
        #self.master.grid_rowconfigure(1, weight=1) # 行の調整

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

実行結果

↓ウィンドウのリサイズ

gridを使うサンプル

個人的にはウィンドウ内にFrameを配置し、その中にpackでウィジェットを配置する場合が多いのですが、gridはラベルテキストボックス(Entry)を並べて配置する場合に使っています。

以下は、ラベルとテキストボックスを並べたサンプルです。

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.title("ウィジェットの配置(grid)")     # ウィンドウタイトル
        self.master.geometry("300x180")       # ウィンドウサイズ(幅x高さ)

        #--------------------------------------------------------
        # ラベルの作成
        label0 = tk.Label(self.master, text = "設定値")

        label1 = tk.Label(self.master, text = "項目1")
        label1_1 = tk.Label(self.master, text = "項目1_1")
        label1_2 = tk.Label(self.master, text = "項目1_2")
        label1_3 = tk.Label(self.master, text = "項目1_3")

        label2 = tk.Label(self.master, text = "項目2")
        label2_1 = tk.Label(self.master, text = "項目2_1")
        label2_2 = tk.Label(self.master, text = "項目2_2")
        label2_3 = tk.Label(self.master, text = "項目2_3")

        # テキストボックス(Entry)の作成
        self.entry1_1 = tk.Entry(self.master, justify = tk.RIGHT)
        self.entry1_2 = tk.Entry(self.master, justify = tk.RIGHT)
        self.entry1_3 = tk.Entry(self.master, justify = tk.RIGHT)

        self.entry2_1 = tk.Entry(self.master, justify = tk.RIGHT)
        self.entry2_2 = tk.Entry(self.master, justify = tk.RIGHT)
        self.entry2_3 = tk.Entry(self.master, justify = tk.RIGHT)

        #--------------------------------------------------------
        # gridでウィジェットの配置
        label0.grid(row = 0, column = 0, rowspan = 8)

        label1.grid(row = 0, column = 1, columnspan = 2)
        label1_1.grid(row = 1, column = 1); self.entry1_1.grid(row = 1, column = 2)
        label1_2.grid(row = 2, column = 1); self.entry1_2.grid(row = 2, column = 2)
        label1_3.grid(row = 3, column = 1); self.entry1_3.grid(row = 3, column = 2)

        label2.grid(row = 4, column = 1, columnspan = 2)
        label2_1.grid(row = 5, column = 1); self.entry2_1.grid(row = 5, column = 2)
        label2_2.grid(row = 6, column = 1); self.entry2_2.grid(row = 6, column = 2)
        label2_3.grid(row = 7, column = 1); self.entry2_3.grid(row = 7, column = 2)
        #--------------------------------------------------------

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(実行画面)

【Python/tkinter】Label(ラベル)

tkinterでラベルの作成のサンプルを以下に示します。

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.title("ラベルの作成")     # ウィンドウタイトル
        self.master.geometry("300x100")       # ウィンドウサイズ(幅x高さ)

        #--------------------------------------------------------
        # ラベルの作成
        self.label = tk.Label(self.master, text = "ラベルの文字")
        # ラベルの配置
        self.label.pack()
        #--------------------------------------------------------

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(実行画面)

構文

ラベルオブジェクト = tk.Label(親ウィジェット, オプション1 = 設定値, オプション2 = 設定値,・・・)

オプション

オプション名 説明
activebackground クリックされたときの背景色を指定します。
activeforeground クリックされたときの文字色を指定します。
anchor 文字の配置位置を指定します。
【設定値】tk.N, tk.S, tk.W, tk.E, tk.NW, tk.NE, tk.SW, tk.SE, tk.CENTER
background 背景色を指定します。
bd 枠線の太さを指定します。
ただし、初期状態では枠線が表示されていないため、reliefで枠線のスタイルを指定する必要があります。(borderwidthと同じ)
bg 通常時の背景色を指定します。
bitmap モノクロのBitmapを指定します。
(参考)https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/bitmaps.html
borderwidth bdと同じ
compound 文字と画像の両方を表示する際に、文字に対して画像の表示位置を指定します。
【設定値】tk.LEFT, tk.RIGHT, tk.BOTTOM, tk.TOP, tk.CENTER
cursor ウィジェット上にマウスポインタがある際のカーソルの種類を指定します。
(参考)https://tkdocs.com/shipman/cursors.html
disabledforeground stateオプションで無効(DISABLED)に設定している際の文字色を設定します。
fg 表示する文字色を指定します。(foregroundと同じ)
font 表示する文字のフォントを指定します。
foreground fgと同じ
height ラベルの高さを文字数で指定します。
画像を配置した時は、画素数の指定になります。
highlightbackground
highlightcolor
highlightthickness
image 表示する画像を指定します。
justify 複数行の文字のときの、文字寄せ方向を指定します。
【設定値】左寄せ(tk.LEFT), 中央寄せ(tk.CENTER), 右寄せ(tk.RIGHT)
padx 文字の両側の隙間を指定します。
pady 文字の上下の隙間を指定します。
relief ラベルの枠線のスタイルを指定します。
【設定値】tk.RAISED, tk.GROOVE, tk.SUNKEN, tk.RIDGE, tk.FLAT
state
takefocus
text 表示する文字を指定します。
textvariable StringVarクラスオブジェクトを指定し、ラベルの文字列を指定します。
underline 指定した順番(先頭から0始まり)の文字にアンダーラインを付加します。
width ラベルの幅を文字数で指定します。
wraplength 文字の折り返し幅を指定します。

 

ラベルはプログラムの各種情報を表示するのに、プログラムの実行中に文字列を変更する場合が多いかと思います。

ラベルの文字を変更する方法は主に下記の2通りあります。

  • textvariableオプションのStringVarを指定し変更する方法
  • ラベルのtextオプションを直接変更する方法

 

textvariableオプションのStringVarを指定し変更する方法

以下のサンプルでは、LabeltextvariableオプションにStringVarを指定し、ボタンのクリック時にラベルの文字列を変更しています。

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.title("ラベルの作成")     # ウィンドウタイトル
        self.master.geometry("300x100")       # ウィンドウサイズ(幅x高さ)

        #--------------------------------------------------------
        # ラベルの作成
        self.label_text = tk.StringVar(value = "ラベルの文字") # valueは初期値
        self.label = tk.Label(self.master, textvariable = self.label_text)
        # ラベルの配置
        self.label.pack()
        #--------------------------------------------------------
        # ボタンの作成
        self.button = tk.Button(self.master, text = "ボタン", command = self.button_click)
        # ボタンの配置
        self.button.pack()
        #--------------------------------------------------------

    def button_click(self):
        '''ボタンがクリックされたとき'''
        # ラベル文字の変更方法1
        # StringVarの値を設定する
        self.label_text.set("ボタンがクリックされた")

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(プログラム起動時)

(ボタンクリック後)

 

ラベルのtextオプションを直接変更する方法

以下のサンプルでは、ボタンのクリック時にラベルのtextオプションを直接変更しています。

ただし、textオプションとtextvariableオプションを両方指定することは出来ないので注意してください。

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.title("ラベルの作成")     # ウィンドウタイトル
        self.master.geometry("300x100")       # ウィンドウサイズ(幅x高さ)

        #--------------------------------------------------------
        # ラベルの作成
        self.label = tk.Label(self.master, text = "ラベルの文字")
        # ラベルの配置
        self.label.pack()
        #--------------------------------------------------------
        # ボタンの作成
        self.button = tk.Button(self.master, text = "ボタン", command = self.button_click)
        # ラベルの配置
        self.button.pack()
        #--------------------------------------------------------

    def button_click(self):
        '''ボタンがクリックされたとき'''
        # ラベル文字の変更方法2
        # ラベルのプロパティを直接設定する。ただし、textvariableプロパティは設定しないこと
        self.label["text"] = "ボタンがクリックされた"

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

(プログラム起動時)

(ボタンクリック後)

【OpenCV/Python】日本語の画像ファイル読込・保存

OpenCVで画像ファイルを開くとき、ファイル名やパスに日本語が含まれていると、画像ファイルを開いてくれません。

試しに以下のようなコードを実行すると、エラーが起き実行できません。

import cv2

# OpenCVで画像ファイルを開く(ファイル名が日本語)
img = cv2.imread("画像ファイル.bmp", cv2.IMREAD_UNCHANGED)

cv2.imshow("Image", img)
cv2.waitKey(0)

エラー情報

Message=OpenCV(4.5.3) C:\Users\runneradmin\AppData\Local\Temp\pip-req-build-sn_xpupm\opencv\modules\imgproc\src\color.cpp:182: 
error: (-215:Assertion failed) !_src.empty() in function 'cv::cvtColor'

エラーそのものはimread関数で画像ファイルを開くのに失敗し、戻り値の画像データ(img)が空(None)になっているため、エラーが発生しています。

日本語の画像ファイルを開くためには、PillowもしくはNumPyで画像ファイルを開いてOpenCVの画像データであるNumPyのndarray形式に変換すれば、OpenCVで日本語の画像ファイルを扱えるようになります。

Pillowで画像ファイルを開き、OpenCV(NumPyのndarray)に変換する

import cv2
import numpy as np
from PIL import Image

# Pillowで画像ファイルを開く
pil_img = Image.open("画像ファイル.bmp")
# PillowからNumPyへ変換
img = np.array(pil_img)
# カラー画像のときは、RGBからBGRへ変換する
if img.ndim == 3:
    img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)

cv2.imshow("Image", img)
cv2.waitKey(0)

Pillowで画像ファイルを開くと、カラー画像の場合、RGBの順でデータが格納されるので、cvtColorを用いて、OpenCVの形式(BGR)へ変換します。

PillowからNumPyの画像データへ変換する方法は下記ページを参照ください。

【Python】画像データ(NumPy,Pillow(PIL))の相互変換

NumPyで画像ファイルを開き、OpenCV(NumPyのndarray)に変換する

import cv2
import numpy as np
from PIL import Image

import time

start = time.perf_counter()

# NumPyで画像ファイルを開く
buf = np.fromfile("画像ファイル.bmp", np.uint8)
img = cv2.imdecode(buf, cv2.IMREAD_UNCHANGED)

print((time.perf_counter() - start) * 1000)#, "msec")

cv2.imshow("Image", img)
cv2.waitKey(0)

NumPyのfromfileで画像ファイルをバイナリで開き、ファイルの中身をメモリ(buf)に格納します。

OpenCVのimdecodeでメモリ上の画像データをOpenCVの画像データ(NumPyのndarray)に変換します。

OpenCVからPillowへ変換し画像ファイルに保存する

import cv2
from PIL import Image

# OpenCVで画像ファイルを開く(ファイル名に日本語が無い場合)
img = cv2.imread("image_file.bmp", cv2.IMREAD_UNCHANGED)

# カラー画像のときは、BGRからRGBへ変換する
if img.ndim == 3:
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# NumPyからPillowへ変換
pil_image = Image.fromarray(img)
# Pillowで画像ファイルへ保存
pil_image.save("画像ファイル_pillow.bmp")

Pillowで画像ファイルを開いたときの逆の事をすると、日本語のファイル名でも画像ファイルに保存することができます。

OpenCVの画像データ(ndarray)を画像形式に変換しファイルに保存する

import cv2
import numpy as np

# OpenCVで画像ファイルを開く(ファイル名に日本語が無い場合)
img = cv2.imread("image_file.bmp", cv2.IMREAD_UNCHANGED)

# 画像データを画像ファイル形式のメモリ変換する
ret, buf = cv2.imencode(".bmp", img)
# NumPyで画像ファイルへ保存
with open("画像ファイル_numpy.bmp", mode='w+b') as f:
    buf.tofile(f)

OpenCVの画像データ(NumPyのndarray)をimencodeで画像ファイル形式にメモリ上で変換し、NumPyのtofileで画像ファイルに書き込みます。

まとめ

日本語の画像ファイルの読込・保存をするのにPillowおよびNumPyを介して処理を行いましたが、それぞれの処理時間を比較しました。

処理時間は8192×8192画素のカラー画像を5回処理したときの平均時間です。

方法 平均処理時間(msec)
Pillowで画像ファイルを開く 513
NumPyで画像ファイルを開く 210
Pillowで画像ファイルへ保存 337
NumPyで画像ファイルへ保存 1106

これを見ると、日本語ファイル名の画像を開くときは、NumPy、保存するときはPillowを使った方が速い結果になりました。

NumPyで保存するときが極端に遅かったので、関数ごとの処理時間を見てみたところ、imencodeに約100msec、tofileに約1000msecという時間でした。

画像の読込と保存でPillowとNumPyを使い分けるのは、少々面倒ですが、処理時間もそれなりに違うので、画像の読込はNumPy、保存はPillowを使った方がいいかもしれません。

【Python】指定フォルダ内のファイル、フォルダ一覧を取得

フォルダに格納された画像ファイルの一覧を取得する場合など、フォルダのパスを指定してファイルの一覧を取得したい場合があります。

その場合に、Pythonでは主に3通りの方法があります。

  • glob.glob
    検索条件を指定してファイル、フォルダ一覧を取得
  • os.listdir
    フォルダ内のファイル、フォルダ一覧を取得
  • os.scandir
    フォルダ内のファイル、フォルダ一覧をファイルか?フォルダか?の属性付きで取得

 

試しに以下のようなファイル、フォルダ構成の時に、どのようにファイル、フォルダを取得できるのか?をみていきたいと思います。

└─images
    ├─annotation.csv
    ├─0
    │ ├─img0001.bmp
    │ ├─img0001.png
    │ ├─img0002.png
    │ └─img0003.png
    ├─1
    │ ├─img0101.png
    │ ├─img0102.png
    │ └─img0103.png
    └─2
      ├─img0201.png
      ├─img0202.png
      └─img0203.png

glob.glob

指定したフォルダ内において、検索条件を指定してファイル、フォルダ一覧を取得。

最も使いやすいと思います。

(サンプル)

import glob

files = glob.glob("./images/*")
print(files)
# ['./images\\0', './images\\1', './images\\2', './images\\annotation.csv']

files = glob.glob("./images/*.*")
print(files)
# ['./images\\annotation.csv']

files = glob.glob("./images/0/*.png")
print(files)
# ['./images/0\\img0001.png', './images/0\\img0002.png', './images/0\\img0003.png']

files = glob.glob("./images/**", recursive=True)
print("recursive", files)
# ['./images\\', './images\\0', './images\\0\\img0001.bmp', './images\\0\\img0001.png', './images\\0\\img0002.png', './images\\0\\img0003.png', './images\\1', './images\\1\\img0101.png', './images\\1\\img0102.png', './images\\1\\img0103.png', './images\\2', './images\\2\\img0201.png', './images\\2\\img0202.png', './images\\2\\img0203.png', './images\\annotation.csv']# 

検索条件に * を指定すると、フォルダ内のファイル、フォルダ全てを取得します。

検索条件に *.* を指定すると、フォルダ内のファイルを取得します。ただし、フォルダ名に . が含まれる場合は、そのフォルダも取得します。

検索条件に *.png のように指定すると、指定した拡張子のファイルを取得します。

検索条件に ** を指定し、さらに、recursive=True と指定すると、指定したフォルダ以下(子のフォルダ内を含む)のファイル、フォルダ全てを取得します。

詳細はこちら↓のページを参照ください。

https://docs.python.org/ja/3/library/glob.html?highlight=glob#glob.glob

os.listdir

指定したフォルダ内のファイル、フォルダを取得します。

(サンプル)

import os
files = os.listdir("./images")
print(files)
# ['0', '1', '2', 'annotation.csv']

files = os.listdir("./images/0")
print(files)
# ['img0001.bmp', 'img0001.png', 'img0002.png', 'img0003.png']

os.listdirではファイル、フォルダの区別なく、指定したフォルダ内のファイル、フォルダの一覧を取得します。

取得した一覧からファイルか?フォルダか?を判断するにはos.path.isfile()メソッドos.path.isdir()メソッドを使って以下のように行うことも可能です。

import os

files = os.listdir("./images")

for f in files:
    path = os.path.join("./images", f)
    if os.path.isfile(path):
        # ファイルの場合
        print("[file  ]", f)
    if os.path.isdir(path):
        # ファイルの場合
        print("[folder]", f)

# [Dir ] 0
# [Dir ] 1
# [Dir ] 2
# [File] annotation.csv

詳細はこちら↓のページを参照ください。

https://docs.python.org/ja/3/library/os.html?highlight=os%20listdir#os.listdir

os.scandir

指定したフォルダ内のファイル、フォルダをファイルか?フォルダか?の属性付きで取得します。

os.listdirではファイル、フォルダの一覧を取得後にファイルか?フォルダか?を判断しましたが、os.scandirでは、取得時に属性付きで取得します。

(サンプル)

import os

with os.scandir("./images") as it:
    for entry in it:
        if entry.is_file():
            print("[file   ]", entry.name)
        elif entry.is_dir():
            print("[folder ]", entry.name)

# [Dir ] 0
# [Dir ] 1
# [Dir ] 2
# [File] annotation.csv

あまり使用する機会が少ないのですが、フォルダ構成のまま、ファイルを取得した場合など、再帰的に呼び出すとフォルダ構成を取得できると思います。

詳細はこちら↓のページを参照ください。

https://docs.python.org/ja/3/library/os.html?highlight=os%20listdir#os.scandir

【Windows11】EdgeのIEモードの設定

Windows11ではIE(Internet Explorer)がインストールされていません。
(正確にはIEはインストールされていますが、IEを実行してもChromeが実行されてしまします。)

普段、Webページを参照する分にはChromeなどを使っている人も多いと思うので、IEが無くても困る事は少ないのですが、唯一困るのが、会社でExcelで作られた台帳などへのリンクが、ローカルのhtmlファイルからリンクが張られているファイルがいくつかあり、これらのhtmlファイルをIEで開き、Excelへのリンクをクリックすると、Excelファイルが開くのですが、IE以外のブラウザで開くとExcelファイルのダウンロード(保存)になってしまいます。

そこで、とりあえずの対応ですが、htmlファイルをEdgeのIEモードで開く方法を紹介します。

まず、Edgeを開き、右上の をクリックします。

すると設定のメニューが表示されるので、設定をクリックします。

表示された設定ウィンドウの左側の既定のブラウザを選択し、右側のInternet Explorerモードでサイトの再読み込みを許可の部分を右側にあるプルダウンメニューの許可を選択します。

この状態で、IEモードで開きたいページをEdgeで開きます。

再度、Edgeの右上のをクリックし、Internet Explorerモードで再度読み込むをクリックします。

すると、下のような画面が表示されるので、今回開いたページを毎回、IEモードで開きたい場合は次回、このページをInternet Explorerモードで開くのチェックを有効にし、完了をクリックします。

すると、アドレス表示部分の左側のアイコンがIEのアイコンに切り替わり、目的だった、Excelファイルへのリンクをクリックしたときにファイルを開くボタンが表示されるので、これをクリックするとExcelファイルが開きます。

 

ただし、このIEモードの設定はページごとの設定となります。
厳密には、IEモードを設定したページからリンク先へ移動したページもIEモードになりますが、直接リンク先のページへ移動すると、IEモードにはなりません。

このIEモードでしばらくの間は時間稼ぎができると思いますが、IEでないと動かないシステムが会社にはいくつかあり、今後、どうやって対応していけばいいのやら??(私が担当ではありませんが。)