polysan_開発メモ

xlwingsでシートの値をDataframeとして取得する

xlwingsを使って、Excelファイルの値をpandasのDataframeの形式で読み込みます。

環境

python : 3.12.3
Excel:2412
xlwings:0.32.1

Excelファイル

読み込み元のExcelファイルを用意します。



このデータをDataframe型で読み込んでみます。

コード

import pandas as pd
import xlwings as xw
from xlwings import Sheet

with (
    xw.App() as app,
    xw.books.open(
        r"xxxxx\test.xlsx"
    ) as wb,
):
    sheet: Sheet = wb.sheets["dataframe_sample"]
    df: pd.DataFrame = (
        sheet.range("A1:F4").options(pd.DataFrame, index=False, header=True).value
    )
    print("type(df):", type(df))
    print("df:", df)
    print("df.columns:", df.columns)
    # 出力
    # type(df): <class 'pandas.core.frame.DataFrame'>
    # df:   header header header header header header
    # 0    りんご    りんご    りんご    りんご    りんご    りんご
    # 1    ぶどう    ぶどう    ぶどう    ぶどう    ぶどう    ぶどう
    # 2      梨      梨      梨      梨      梨      梨
    # df.columns: Index(['header', 'header', 'header', 'header', 'header', 'header'], dtype='object')

説明

sheet.rangeで範囲を取得した後、optionsメソッドを呼び出します。
optionsの第一引き数にpd.DataFrameを指定することで、Dataframeとしてセルの値を取得できます。
optionsの引数indexにFalseを指定することで、取得したDataframe型のデータにindexを設定できます。
optionsの引数headerにTrueを指定することで、指定範囲の1行目がheaderとして取得されます。
これら引数の値はBoolean以外にint型でも指定できます。(1がTrue、0がFalse)
また、header = 2 と指定すると、指定範囲の2行目までがヘッダーとして取得されます。

    df: pd.DataFrame = (
        sheet.range("A1:F4").options(pd.DataFrame, index=0, header=True).value
    )

import time
import tkinter as tk


def setup_after_display():
    # ウィンドウが表示された直後に実行される処理
    label.config(text="ウィンドウが完全に表示されました!")


root = tk.Tk()
label = tk.Label(root, text="初期状態")
time.sleep(5)
label.pack()

# ウィンドウの描画が完了したら setup_after_display を呼び出す
root.after_idle(setup_after_display)

root.mainloop()

xlwings.utilsで使える関数一覧

xlwingsのライブラリxlwings.utilsで使用できる関数を使ってみます。


目次

環境

python : 3.12.3
Excel:2412
xlwings:0.32.1

int_to_rgb、rgb_to_int、hex_to_rgb、rgb_to_hex

int_to_rgb、rgb_to_int、hex_to_rgb、rgb_to_hexは別の記事で紹介しているため、割愛します。

kankisenkowasuo.hatenablog.com

get_duplicates

get_duplicatesはリストやタプルの内の重複した値を検出できるメソッドです。

下記のExcelファイルを読み込ませて、A列の重複した値を検出します。

import xlwings as xw
from xlwings.utils import get_duplicates

with (
    xw.App() as app,
    xw.books.open(
        r"xxxx\test.xlsx"
    ) as wb,
):
    sheet: xw.Sheet = wb.sheets["Sheet1"]

    # A列のデータを取得
    data = sheet.range("A1:A10").value
    # 重複データを取得
    duplicates: set = get_duplicates(data)

    print(duplicates)
    # 出力 {'りんご', 'いちご'}

    wb.save()

重複値であるりんごといちごが検出できました。

np_datetime_to_datetime

np_datetime_to_datetimeはNumPyのdatetime64 型をPythonのdatetime型に変換するための関数です。
datetime型に変換することでPythonの標準的な日付時刻オブジェクトとして扱えるようになります。

また、numpy.datetime64をそのままExcelに書き込むとエラーが発生する可能性がありますが、
datetimeに変換してから Excel に書き込むことで、Excel の日付データとして正しく認識されます。

import datetime
import numpy as np
import xlwings as xw
from xlwings.utils import np_datetime_to_datetime

with (
    xw.App() as app,
    xw.books.open(
        r"xxx\test.xlsx"
    ) as wb,
):
    sheet: xw.Sheet = wb.sheets["Sheet1"]

    np_date = np.datetime64("2024-01-29T15:30:00")  # NumPyの datetime64
    python_date: datetime = np_datetime_to_datetime(np_date)  # Pythonの datetime に変換

    print(type(python_date))
    print(python_date)
    # 出力
    # <class 'datetime.datetime'>
    # 2024-01-29 15:30:00

    sheet.range("A1").value = python_date # Excelに出力

    wb.save()

出力結果

xlserial_to_datetime

xlserial_to_datetimeは、Excelのシリアル値(Excelの日付形式をPythonのdatetime型に変換するメソッドです。

シリアル値

Excelでは日付を 「1900年1月1日を 1.0 とするシリアル値」として管理しています。  

  • 1.0 → 1900年1月1日 00:00:00
  • 2.0 → 1900年1月2日 00:00:00

xlserial_to_datetimeはシリアル値をdatetimeに変換することで適切な形で日付データを扱えます。

読み込みファイル

import xlwings as xw
from xlwings.utils import xlserial_to_datetime

with (
    xw.App() as app,
    xw.books.open(r"xxxx\test.xlsx") as wb,
):
    sheet: xw.Sheet = wb.sheets["Sheet1"]

    # A1 にある Excel の日付シリアル値を取得し、Python の datetime に変換
    excel_serial = sheet.range("A1").value
    python_datetime = xlserial_to_datetime(excel_serial)

    print(python_datetime)
    # 出力
    # 2023-02-06 00:00:00

    wb.save()

datetime_to_xlserial

datetime_to_xlserialは、xlserial_to_datetimeの逆の処理を行う関数で、
Pythonのdatetimeオブジェクトを Excel のシリアル値(数値形式)に変換するために使用します。

import datetime
from xlwings.utils import datetime_to_xlserial

dt_now = datetime.datetime.now()
xlserial: float = datetime_to_xlserial(dt_now)

print(dt_now)
print(xlserial)
# 出力
# 2025-02-11 13:11:44.101634
# 45699.54981599114

col_name

col_nameは下記の記事で説明しているため、割愛します。

kankisenkowasuo.hatenablog.com

address_to_index_tuple

address_to_index_tupleExcelのセルアドレス(例: "B2")を (行, 列) のタプル形式に変換するメソッドです。

コードの例では"B2"という文字列を(2,2)のタプルに変換しています。

from xlwings.utils import address_to_index_tuple

cell = "B2"
row, col = address_to_index_tuple(cell)

print(row, col)
# 出力
# 2 2

column_to_number

column_to_numberは、Excelの列名(例: "A", "Z", "AA", "IV")を列番号(数値)に変換するメソッドです。

from xlwings.utils import column_to_number

print(column_to_number("A"))  # 1
print(column_to_number("Z"))  # 26
print(column_to_number("AA"))  # 27
print(column_to_number("IV"))  # 256
print(column_to_number("ZZ"))  # 702
print(column_to_number("AAA"))  # 703

a1_to_tuples

a1_to_tuplesは、ExcelのA1形式(例: "A1", "B2:D5")を (行, 列),(行, 列) のタプル形式に変換するメソッドです。

範囲が単一セルの場合は、(行,列),Noneで表示されます。

from xlwings.utils import a1_to_tuples

print(a1_to_tuples("A1"))  # 単一セル ((1, 1), None)
print(a1_to_tuples("C5"))  # 単一セル ((5, 3), None)
print(a1_to_tuples("B2:D5"))  # 矩形範囲 ((2, 2), (5, 4))
print(a1_to_tuples("a:b"))  # 列範囲   ((1, 1), (1048576, 2))
print(a1_to_tuples("1:2"))  # 行範囲      ((1, 1), (2, 16384))

try_parse_int

作成中

process_image

作成中

fspath

作成中

read_config_sheet

作成中

read_user_config

作成中

get_cached_user_config

作成中

exception

作成中

chunk

作成中

query_yes_no

作成中

prepare_sys_path

作成中

fullname_url_to_local_path

作成中

to_pdf

作成中

get_url_to_mount

作成中

search_local_sharepoint_path

作成中

excel_update_picture

作成中

determine_columns_or_rows

作成中

xlwingsで条件付き書式を削除する

xlwingsを使って、Excelファイルに設定された条件付き書式を削除します。

環境

python : 3.12.3
Excel:2412
xlwings:0.32.1

Excelファイル

条件付き書式が設定されたExcelを用意します。

背景色が条件付き書式で設定されています。
「りんご」と書かれたセルは赤色、「ばなな」は黄色、「めろん」は緑色となっています。

この状態で全ての条件付き書式を削除します。

コード

import xlwings as xw

with (
    xw.App() as app,
    xw.books.open(
        r"xxxx\sample.xlsx"
    ) as wb,
):
    sheet: xw.Sheet = wb.sheets["Sheet1"]

    # 条件付き書式を削除
    sheet.used_range.api.FormatConditions.Delete()

    wb.save()

説明

対象シートのrangeオブジェクトから、条件付き書式を削除する範囲を取得します。
今回は全ての条件付き書式を削除するので、used_rangeパラメータを使用します。

rangeオブジェクトにapi.FormatConditions.Delete()を実行することで条件付き書式を削除できます。

    # 条件付き書式を削除
    sheet.used_range.api.FormatConditions.Delete()

コードを実行するとExcelファイルから条件付き書式が削除されていることが確認できます。


xlwingsでシートに設定された条件付き書式を取得する

xlwingsを使って、Excelファイルに設定された条件付き書式を取得します。

環境

python : 3.12.3
Excel:2412
xlwings:0.32.1

Excelファイル

条件付き書式が設定されたExcelを用意します。

「りんご」が設定されたセルには下記の条件付き書式が設定されています。
セルの値が「りんご」の場合、背景色が赤色に設定されます。


「ばなな」、「めろん」も背景色の設定だけ変えて同じように設定します。
この状態でコードを実行します。

コード

import xlwings as xw

with (
    xw.App() as app,
    xw.books.open(
        r"C:\xxxx\sample.xlsx"
    ) as wb,
):
    sheet: xw.Sheet = wb.sheets["Sheet1"]

    # 条件付き書式を取得
    for format in sheet.used_range.api.FormatConditions:
        # 条件付き書式の種類
        format_type = format.Type
        operator = format.Operator
        formula1 = format.Formula1
        address = format.AppliesTo.Address  # 適用範囲

        # 書式のプロパティを取得
        interior = format.Interior  # 背景色
        font = format.Font  # フォント

        # 背景色を取得(RGB値)
        color = int(interior.Color)  # 色の数値
        red = color & 0xFF  # R成分
        green = (color >> 8) & 0xFF  # G成分
        blue = (color >> 16) & 0xFF  # B成分

        # フォントの書式
        font_color = int(font.Color)  # フォント色
        font_red = font_color & 0xFF  # R成分
        font_green = (font_color >> 8) & 0xFF  # G成分
        font_blue = (font_color >> 16) & 0xFF  # B成分
        bold = font.Bold  # 太字
        italic = font.Italic  # 斜体

        print("--------------start---------------------")
        print(f"format_type: {format_type}")
        print(f"operator: {operator}")
        print(f"formula1: {formula1}")
        print(f"範囲: {address}")
        print(f"背景色: {color}")
        print(f"背景色 (RGB): ({red}, {green}, {blue})")
        print(f"フォント色: {font_color}")
        print(f"フォント色(RGB): ({font_red}, {font_green}, {font_blue})")
        print(f"太字: {bold}, 斜体: {italic}")
        print("--------------end-----------------------")
        # --------------start---------------------
        # format_type: 1
        # operator: 3
        # formula1: ="りんご"
        # 範囲: $C$2
        # 背景色: 13551615
        # 背景色 (RGB): (255, 199, 206)
        # フォント色: 393372
        # フォント色(RGB): (156, 0, 6)
        # 太字: None, 斜体: None
        # --------------end-----------------------
        # --------------start---------------------
        # format_type: 1
        # operator: 3
        # formula1: ="ばなな"
        # 範囲: $C$4
        # 背景色: 10284031
        # 背景色 (RGB): (255, 235, 156)
        # フォント色: 22428
        # フォント色(RGB): (156, 87, 0)
        # 太字: None, 斜体: None
        # --------------end-----------------------
        # --------------start---------------------
        # format_type: 1
        # operator: 3
        # formula1: ="めろん"
        # 範囲: $C$6
        # 背景色: 13561798
        # 背景色 (RGB): (198, 239, 206)
        # フォント色: 24832
        # フォント色(RGB): (0, 97, 0)
        # 太字: None, 斜体: None
        # --------------end-----------------------

説明

sheetのrangeオブジェクトからapi.FormatConditionsで条件付き書式の一覧が取得できます。

    # 条件付き書式を取得
    for format in sheet.used_range.api.FormatConditions:
        # 条件付き書式の種類
        format_type = format.Type
        operator = format.Operator
        formula1 = format.Formula1
        address = format.AppliesTo.Address  # 適用範囲

Typeでは条件付き書式で何を元に比較するかを取得します。
Operatorは条件付き書式をどのように比較するかを取得します。
Formula1で数式を取得します。
AppliesTo.Addressで条件付き書式の適用範囲を取得します。
Type、Operator、Formula1はExcelの下記赤枠に該当します。

出力結果はType=1,Oparator=3となっています。
TypeとOparatorの値が何を表しているかはMicrosoftのwebページで確認できます。

【Type】

learn.microsoft.com

【Operator】

learn.microsoft.com

背景色はinterior.Colorで取得します。
interior.Colorの値を0xFFとビット演算で論理積と計算することでRGBの値を取得できます。
G成分、B成分はそれぞれ8ビット、16ビット右シフト後に論理積を求めます。

        # 背景色を取得(RGB値)
        color = int(interior.Color)  # 色の数値
        red = color & 0xFF  # R成分
        green = (color >> 8) & 0xFF  # G成分
        blue = (color >> 16) & 0xFF  # B成分

フォントはFontで取得します。Font.Colorでフォントの色を取得します。
背景色と同様、0xFFと論理積を求めてRGB値を取得します。

        # フォントの書式
        font_color = int(font.Color)  # フォント色
        font_red = font_color & 0xFF  # R成分
        font_green = (font_color >> 8) & 0xFF  # G成分
        font_blue = (font_color >> 16) & 0xFF  # B成分
        bold = font.Bold  # 太字 
        italic = font.Italic  # 斜体 

Font.Boldで太字を、Font.Italicで斜体か取得できます。
両方設定した場合で取得してみます。

--------------start---------------------
format_type: 1
operator: 3
範囲: $C$10
背景色: 0
背景色 (RGB): (0, 0, 0)
太字: True, 斜体: True
--------------end-----------------------

太字・斜体の場合、Trueが返ってきます。

【tkinter】ttk.entryでバリデーションを設定する

ttk.entryのテキストボックスにバリデーションを設定します。
半角英数字のみ受け付けるよう設定します。

環境

python : 3.12.3
tkinter:8.6

コード

import re
import tkinter as tk
from tkinter import ttk

# 検証関数
def validate_half_width_alphanumeric(d, i, P, s, S, v, V, W) -> bool:
    print(f"d:{d}, i:{i}, P:{P}, s:{s}, S:{S}, v:{v}, V:{V}, W:{W}")
    # 空の場合も許可
    if P == "":
        return True
    # 半角英数字かチェック
    return bool(re.match("^[a-zA-Z0-9]+$", P))


# メインウィンドウの作成
root = tk.Tk()
root.title("Validation Example")
root.geometry("300x100")

# コールバック関数を登録
validate_command = (
    root.register(validate_half_width_alphanumeric),
    "%d",
    "%i",
    "%P",
    "%s",
    "%S",
    "%v",
    "%V",
    "%W",
)

# ttk.Entryの作成とバリデーションの設定
entry = ttk.Entry(root, validate="key", validatecommand=validate_command)
entry.pack(pady=10, padx=10)

# ウィンドウを表示
root.mainloop()

説明

ttk.Entryウィジェットを作成してvalidateに"key"を、validatecommandにコールバック関数validate_commandを渡します。

# ttk.Entryの作成とバリデーションの設定
entry = ttk.Entry(root, validate="key", validatecommand=validate_command)

validateではバリデーションのトリガーを指定でき、下記の値を設定できます。

説明
"none" バリデーション無効
"focus" ウィジェットにフォーカスが当たったとき、またはフォーカスが外れたとき
"focusin" ウィジェットにフォーカスが当たったとき
"focusout" ウィジェットからフォーカスが外れたとき
"key" キーボード入力
"all" 全てのトリガー("focusin"、"focusout"、"key")


validatecommandにはコールバック関数を設定します。
コールバック関数validate_commandは、第一引数に親ウィジェットのメソッドregisterを使用し、検証関数を登録します。今回はrootが親ウィジェットです。

# コールバック関数を登録
validate_command = (
    root.register(validate_half_width_alphanumeric),
    "%d",
    "%i",
    "%P",
    "%s",
    "%S",
    "%v",
    "%V",
    "%W",
)


第二引数には特殊引数を渡します。設定できるのは下記の値です。

説明
"%d" 変更の種類(0は削除、1は挿入)
"%i" 挿入または削除が発生する文字インデックス(0から始まる)
"%P" エントリウィジェットに設定される新しい値(入力後の内容)
"%s" 現在のエントリウィジェットの値(入力前の内容)
"%S" 挿入または削除される文字
"%v" validateオプションの現在の値(例: key, focusなど)
"%V" バリデーションイベントの種類(例: key, focusin, focusoutなど)
"%W" エントリウィジェットの名前(例: .entry1)

今回は"%P"を使用して、テキストボックスに入力した値に対してバリデーションを行います。



バリデーション関数です。空文字もしくは半角英数字の場合のみ受け付けるようにしています。

# 検証関数
def validate_half_width_alphanumeric(d, i, P, s, S, v, V, W) -> bool:
    print(f"d:{d}, i:{i}, P:{P}, s:{s}, S:{S}, v:{v}, V:{V}, W:{W}")
    # 空の場合も許可
    if P == "":
        return True
    # 半角英数字かチェック
    return bool(re.match("^[a-zA-Z0-9]+$", P))

1つ注意が必要で、バリデーション関数はbool型を返さないと正常に動きません。

例えば上記のreturnを「re.match("^[a-zA-Z0-9]+$", P)」にするとバリデーションが働かず、全角かなを入力できてしまいます。(re.matchはマッチオブジェクト、またはNoneを返す)

動作確認をしてみます。
半角英数字のみ受け付けるテキストボックスになっています。

この状態でQを入力してみるとバリデーションで弾かれて入力されません。
Qを入力したときの特殊変数は以下となります。
d:1
i:6
P:1111ssQ
s:1111ss
S:Q
v:key
V:key
W:.!entry

特殊変数Pで「1111ssQ 」が取得され、Qが全角のためにバリデーションにかかることで入力されていないことが確認できます。

xlwingsでExcelシートに改ページプレビューを設定する

xlwingsを使ってExcelシートを改ページプレビューで表示します。
印刷範囲の設定も併せて実施します。

環境

python : 3.12.3
xlwings:0.32.1

コード

import xlwings as xw

# Excelアプリケーションを起動
with (
    xw.App(visible=False) as app,
    xw.books.open(
        r"xxxxxxxx\xxxxxxxxx\sample.xlsx"
    ) as wb,
):
    # 対象シートを取得
    sheet = wb.sheets["test"]

    # 印刷範囲を指定(例: A1からM34を範囲に設定)
    sheet.api.PageSetup.PrintArea = "$A$1:$M$34"

    # 改ページプレビューを有効にする
    sheet.api.Parent.Windows(1).View = 2

    # 保存して閉じる
    wb.save()

説明

APIVBAを利用して印刷範囲を設定します。 ここではA1からM34で指定します。

    # 印刷範囲を指定(例: A1からM34を範囲に設定)
    sheet.api.PageSetup.PrintArea = "$A$1:$M$34"

ここでもVBAAPIを利用して改ページプレビューに設定します。
パラメータ「2」が改ページプレビューです。

    # 改ページプレビューを有効にする
    sheet.api.Parent.Windows(1).View = 2

このパラメータはmicrosoftのwebページで確認できます。

learn.microsoft.com

ソースを実行すると改ページプレビューの状態で表示できました。

改ページの区切り点線を無くし、印刷範囲を1ページに収めたい場合は下記のコードを実行します。

import xlwings as xw

# Excelアプリケーションを起動
with (
    xw.App(visible=False) as app,
    xw.books.open(
        r"xxxxxx\xxxxxxx\sample.xlsx"
    ) as wb,
):
    # 対象シートを取得
    sheet = wb.sheets["test"]

    # 印刷範囲を指定(例: A1からM34を範囲に設定)
    sheet.api.PageSetup.PrintArea = "$A$1:$M$34"

# 追加----------------------------------------------------------------
    # スケールを指定(例: 幅を1ページ、縦を1ページに収める)
    sheet.api.PageSetup.Zoom = False
    sheet.api.PageSetup.FitToPagesWide = 1  # 幅を1ページに収める
    sheet.api.PageSetup.FitToPagesTall = 1  # 高さを1ページに収める
# --------------------------------------------------------------------

    # 改ページプレビューを有効にする
    sheet.api.Parent.Windows(1).View = 2

    wb.save()

sheet.api.PageSetup.Zoom = False で印刷ページの自動スケーリングを無効化します。
「False」にすることでパラメータで印刷範囲の区切り幅と高さを設定できるようになります。
sheet.api.PageSetup.FitToPagesWideでは幅を、sheet.api.PageSetup.FitToPagesTallでは高さを設定でき、どちらも「1」を設定することでページ区切りを1ページに収めることができます。

ページ区切りが1ページに収まりました。

ページレイアウト

ページレイアウトでの表示もパラメータを変えるだけで簡単に設定できます。
sheet.api.Parent.Windows(1).Viewに「3」を設定します。

    # ページレイアウトを有効にする
    sheet.api.Parent.Windows(1).View = 3

ページレイアウトで表示できました。