"""
tkexcel.py: Excel (.xlsx, .csv, .txt) ファイルの読み書きと操作を行うためのユーティリティモジュール。
このモジュールはopenpyxlライブラリをベースに、Excelファイルのデータ操作を簡素化する機能を提供します。
暗号化されたExcelファイルの処理や、CSV/テキストファイルの読み込みにも対応しています。
また、チャートの作成やセルの書式設定といった詳細なExcel操作機能も含まれています。
:doc:`tkexcel_usage`
"""
import os
import sys
import re
import io
import csv
import openpyxl
try:
from openpyxl.chart import ScatterChart, Reference, Series
from openpyxl.styles import Font, PatternFill, Alignment
import openpyxl.reader.excel.warnings
except:
pass
try:
from chardet.universaldetector import UniversalDetector
except:
print("")
print("####################################################################")
print("####################################################################")
print(" Error in tklib.tkexcel: module [chardet] is not installed.")
print("####################################################################")
print("####################################################################")
print("")
exit()
try:
import msoffcrypto
except:
'''
print("")
print("####################################################################")
print("####################################################################")
print(" Warning in tklib.tkexcel: module [msoffcrypto] is not installed.")
print(" You cannot open encrypted Excel files protected with password")
print(" Install by pip install msoffcrypto-tool if needed")
print("####################################################################")
print("####################################################################")
print("")
'''
msoffcrypto = None
from tklib.tkobject import tkObject
from tklib.tkutils import mprint, IsDir, IsFile, SplitFilePath, modify_path, delete_file
from tklib.tkutils import pint, pfloat, pconv
from tklib.tkutils import split_file_path, replace_path
from tklib.tkfile import get_encoding
from tklib.tksci.tkmatrix import make_matrix1, make_matrix2
import tklib.tkre
from tklib.tkdatafile import tkDataFile
#=======================================
# Handling MS-Excel .xlsx files
#=======================================
[ドキュメント]
def convert_color(color):
"""
色の名前またはハッシュ文字列をOpenPyXLで使用可能な16進数カラーコードに変換します。
:param color: str: 色の名前("black", "red"など)または"#RRGGBB"形式のハッシュ文字列。
:returns: str: OpenPyXLで利用可能なRRGGBB形式の16進数カラーコード。
"""
color_dict = {
"black": "000000",
"red" : "FF0000",
"green": "00FF00",
"blue" : "0000FF",
}
if color in color_dict.keys():
return color_dict[color]
return color.lstrip("#")
[ドキュメント]
def convert_linestyle(linestyle):
"""
線のスタイル名をOpenPyXLで使用可能な形式に変換します。
:param linestyle: str: 線のスタイル名("dashed"など)。
:returns: str: OpenPyXLで利用可能な線のスタイル文字列。
"""
if linestyle == "dashed":
return "lgDash"
else:
return "solid"
[ドキュメント]
def convert_size(s):
"""
ポイント単位のサイズをEMU (English Metric Unit) に変換します。
詳細説明:
1インチ = 914400 EMU
1 pt = 1/72インチ
したがって、1 pt = 914400 / 72 = 12700 EMU となります。
:param s: int or float: ポイント単位のサイズ。
:returns: int: EMU単位に変換されたサイズ。
"""
return int(12700 * s)
[ドキュメント]
class tkExcel_sheet(openpyxl.worksheet.worksheet.Worksheet):
"""
OpenPyXLのワークシートオブジェクトをラップするクラスです。
"""
def __init__(self, sheet = None):
"""
tkExcel_sheetのコンストラクタ。
:param sheet: openpyxl.worksheet.worksheet.Worksheet, optional: ラップするOpenPyXLのワークシートオブジェクト。デフォルトはNone。
"""
super().__init__()
self.ws = sheet
def __del__(self):
"""
デストラクタ。
"""
pass
def __str__(self):
"""
オブジェクトの文字列表現を返します。
:returns: str: クラスのパス。
"""
return self.ClassPath()
[ドキュメント]
class tkExcelChart():
"""
OpenPyXLのチャート機能 (ScatterChart) をラップするクラスです。
"""
def __init__(self, style = 'scatter'):
"""
tkExcelChartのコンストラクタ。
:param style: str, optional: 作成するチャートのスタイル。現在は'scatter'のみ対応。デフォルトは'scatter'。
"""
if style == 'scatter':
self.chart = ScatterChart()
else:
self.chart = False
[ドキュメント]
def add_chart(self, ws, position = "A1"):
"""
指定されたワークシートにチャートを追加します。
:param ws: openpyxl.worksheet.worksheet.Worksheet: チャートを追加するワークシートオブジェクト。
:param position: str, optional: チャートを配置するワークシート上の開始セル位置 (例: "A1")。デフォルトは"A1"。
"""
ws.add_chart(self.chart, position)
[ドキュメント]
def set_figsize(self, width, height):
"""
チャートのサイズを設定します。
:param width: int or float: チャートの幅。
:param height: int or float: チャートの高さ。
"""
self.chart.width = width
self.chart.height = height
[ドキュメント]
def set_title(self, title):
"""
チャートのタイトルを設定します。
:param title: str: チャートのタイトル文字列。
"""
self.chart.title = title
[ドキュメント]
def set_xlabel(self, label):
"""
X軸のラベルを設定します。
:param label: str: X軸のラベル文字列。
"""
self.chart.x_axis.title = label
[ドキュメント]
def set_ylabel(self, label):
"""
Y軸のラベルを設定します。
:param label: str: Y軸のラベル文字列。
"""
self.chart.y_axis.title = label
[ドキュメント]
def set_legend(self, style = ''):
"""
凡例の表示スタイルを設定します。
:param style: str or None, optional: 凡例のスタイル。Noneを指定すると凡例を非表示にします。デフォルトは''。
"""
if style is None:
self.chart.legend = None
[ドキュメント]
def add_plot(self, ws, xrange, yrange, title_from_data = False, title = None,
width = None, color = "000000", linestyle = "-"):
"""
チャートにプロット(データ系列)を追加します。
:param ws: openpyxl.worksheet.worksheet.Worksheet: 参照するワークシートオブジェクト。
:param xrange: tuple: X軸データの範囲 (min_col, min_row, max_row)。
:param yrange: tuple: Y軸データの範囲 (min_col, min_row, max_row)。
:param title_from_data: bool, optional: データのタイトルをデータから取得するかどうか。デフォルトはFalse。
:param title: str, optional: データ系列のタイトル。title_from_dataがFalseの場合に有効。デフォルトはNone。
:param width: int or float, optional: 線の幅(ポイント単位)。デフォルトはNone。
:param color: str, optional: 線の色(16進数カラーコードまたは色の名前)。デフォルトは"000000" (黒)。
:param linestyle: str, optional: 線のスタイル("-"は実線、"dashed"は破線)。デフォルトは"-"。
"""
ls = convert_linestyle(linestyle)
cl = convert_color(color)
w = convert_size(width) if width is not None else None
x_ref = Reference(ws, min_col = xrange[0], min_row = xrange[1], max_row = xrange[2])
y_ref = Reference(ws, min_col = yrange[0], min_row = yrange[1], max_row = yrange[2])
series = Series(y_ref, x_ref, title_from_data = title_from_data, title = title)
if w is not None:
series.graphicalProperties.line.width = w
series.graphicalProperties.line.solidFill = cl
series.graphicalProperties.line.prstDash = ls
self.chart.series.append(series)
[ドキュメント]
def set_xlim(self, range):
"""
X軸の表示範囲を設定します。
:param range: tuple: X軸の最小値と最大値 (min_val, max_val)。
"""
self.chart.x_axis.scaling.min = range[0]
self.chart.x_axis.scaling.max = range[1]
[ドキュメント]
def set_ylim(self, range):
"""
Y軸の表示範囲を設定します。
:param range: tuple: Y軸の最小値と最大値 (min_val, max_val)。
"""
self.chart.y_axis.scaling.min = range[0]
self.chart.y_axis.scaling.max = range[1]
[ドキュメント]
def set_xgrid(self, style = None):
"""
X軸の主グリッド線のスタイルを設定します。
:param style: openpyxl.chart.shapes.GraphicalProperties or None, optional: グリッド線のスタイルオブジェクト。Noneで非表示。デフォルトはNone。
"""
self.chart.x_axis.majorGridlines = style
[ドキュメント]
def set_ygrid(self, style = None):
"""
Y軸の主グリッド線のスタイルを設定します。
:param style: openpyxl.chart.shapes.GraphicalProperties or None, optional: グリッド線のスタイルオブジェクト。Noneで非表示。デフォルトはNone。
"""
self.chart.y_axis.majorGridlines = style
[ドキュメント]
def set_xticks(self, style = "none"):
"""
X軸の目盛りの表示スタイルを設定します。
詳細説明:
このメソッドは、X軸の主目盛りと補助目盛りの両方を設定します。
OpenPyXLのAPIの特性上、同じメソッド名で主目盛りと補助目盛りの設定が行われています。
:param style: str, optional: 目盛りのスタイル(例: "none", "out", "in", "cross")。デフォルトは"none"。
"""
if style is None: style = "none"
self.chart.x_axis.majorTickMark = style
self.chart.x_axis.minorTickMark = style # この行は既存のコードで重複しているため、両方にDocstringを適用
[ドキュメント]
class tkExcel(tkDataFile):
"""
MS-Excel (.xlsx) ファイルの読み書きと操作を行うクラスです。
tkDataFileを継承し、Excelファイルのオープン、シート操作、データの読み書き、
パスワード付きファイルの処理などをサポートします。CSVやテキストファイルの読み込みも可能です。
"""
def __init__(self, path = None, mode = 'r', password = None, allow_no_password = False, tmp_file = None,
OpenFile = True, CloseFile = False, try_text = False, data_only = True, keep_vbas = False,
try_text_file = False, encoding = None,
split_from = 1, IsPrint = False, **args):
"""
tkExcelクラスのコンストラクタ。
Excelファイルを開き、初期設定を行います。
パスワード付きファイルの処理、CSV/テキストファイルの自動判別と読み込み、
およびデータのみの読み込みオプションを提供します。
:param path: str, optional: 開くExcelファイルのパス。デフォルトはNone。
:param mode: str, optional: ファイルのオープンモード ('r':読み込み, 'w':書き込み, 'a':追記)。デフォルトは'r'。
:param password: str, optional: 暗号化されたExcelファイルを開く際のパスワード。デフォルトはNone。
:param allow_no_password: bool, optional: パスワード指定時に開けなかった場合、パスワードなしで再試行するかどうか。デフォルトはFalse。
:param tmp_file: str, optional: 暗号化ファイルを復号する際に一時的に使用するファイル名。デフォルトは'tmp.xlsx'。
:param OpenFile: bool, optional: インスタンス生成時にファイルを自動的に開くかどうか。デフォルトはTrue。
:param CloseFile: bool, optional: インスタンス生成後にファイルを自動的に閉じるかどうか。デフォルトはFalse。
:param try_text: bool, optional: (未使用)
:param data_only: bool, optional: セルの値を数式ではなく表示されている値として読み込むかどうか。デフォルトはTrue。
:param keep_vbas: bool, optional: VBAマクロを保持するかどうか。デフォルトはFalse。
:param try_text_file: bool, optional: ファイルがExcel形式でない場合、テキストファイルとして読み込みを試みるか。デフォルトはFalse。
:param encoding: str, optional: テキストファイルまたはCSVファイルを読み込む際のエンコーディング。デフォルトはNone(自動判別)。
:param split_from: int, optional: テキストファイルまたはCSVファイルを読み込む際、何行目からデータを開始するか(1-based)。デフォルトは1。
:param IsPrint: bool, optional: 処理中にメッセージを標準出力するかどうか。デフォルトはFalse。
:param args: dict: 親クラスtkDataFileに渡される追加引数。
"""
self.fp = None
self.path = None
self.mode = None
self.password = None
self.tmp_file = None
self.try_text_file = try_text_file
self.wb = None
self.ws = None
self.cur_row = 0
self.cur_col = 0
self.labelarray = []
self.datalistarray = []
self.split_from = split_from
self.encoding = encoding
self.password = password
self.allow_no_password = allow_no_password
self.update(**args)
self.initialize(**args)
super().__init__(path = path, mode = mode, OpenFile = False, CloseFile = False, **args)
self.path = self.IfYes(path is not None, path, self.path)
self.mode = self.IfYes(mode is not None, mode, self.mode)
self.labelarray = []
self.datalistarray = []
if OpenFile and self.path is not None:
# self.open(self.path, self.mode, data_only)
self.open_encrypted(path = path, mode = mode, password = password, allow_no_password = allow_no_password,
tmp_file = tmp_file,
try_text_file = try_text_file, data_only = data_only, keep_vbas = keep_vbas,
encoding = encoding, split_from = split_from, IsPrint = IsPrint)
if CloseFile:
self.close()
def __del__(self):
"""
デストラクタ。
"""
# self.close()
pass
def __str__(self):
"""
オブジェクトの文字列表現を返します。
:returns: str: クラスのパス。
"""
return self.ClassPath()
[ドキュメント]
def initialize(self, **args):
"""
tkExcelオブジェクトの状態を初期化します。
既存のファイルやワークブックを閉じ、全ての内部状態をリセットします。
:param args: dict: 親クラスの`update`メソッドに渡される追加引数。
"""
if self.fp:
self.close()
if self.wb:
self.close()
self.wb = None
self.fp = None
self.path = None
self.mode = None
self.wb = None
self.ws = None
self.cur_row = 0
self.cur_col = 0
self.labelarray = []
self.datalistarray = []
self.update(**args)
[ドキュメント]
def open_text(self, path = None, mode = 'r', encoding = None, split_from = None, separator = ' ', IsPrint = True):
"""
テキストファイルをOpenPyXLのワークブックとして開きます。
詳細説明:
テキストファイルを指定された区切り文字で分割し、各行のデータをワークシートに書き込みます。
:param path: str, optional: 開くテキストファイルのパス。デフォルトはNone。
:param mode: str, optional: ファイルのオープンモード。デフォルトは'r'。
:param encoding: str, optional: テキストファイルのエンコーディング。Noneの場合、自動判別します。デフォルトはNone。
:param split_from: int, optional: 何行目からデータを開始するか(1-based)。Noneの場合、インスタンスの`split_from`を使用。デフォルトはNone。
:param separator: str, optional: 列を区切るためのセパレータ文字列。デフォルトは' ' (スペース)。
:param IsPrint: bool, optional: エラーメッセージを標準出力するかどうか。デフォルトはTrue。
:returns: openpyxl.workbook.workbook.Workbook or None: 開かれたOpenPyXLワークブックオブジェクト、または失敗した場合はNone。
"""
if encoding is None:
encoding = get_encoding(path, defval = None)
self.wb = openpyxl.Workbook()
self.fp = None
if self.ws is None:
print("")
print(f"Error in tkexcel.open(): Cannot open [{path}]")
print( " Would be an encrypted file")
return None
self.ws = self.wb.active
if split_from is None:
split_from = self.split_from
self.ws = self.wb.active
fp = open(path, mode = mode, encoding = encoding)
irow = 1
for i in range(split_from - 1):
line = fp.readline().strip()
self.ws.cell(irow, 1).value = line
irow += 1
while 1:
line = fp.readline()
if not line:
break
row = line.split(separator)
for icol in range(len(row)):
self.ws.cell(irow, icol + 1).value = pconv(row[icol], row[icol])
irow += 1
fp.close()
return self.wb
[ドキュメント]
def open_csv(self, path = None, mode = 'r', encoding = None, split_from = None, IsPrint = True):
"""
CSVファイルをOpenPyXLのワークブックとして開きます。
詳細説明:
CSVファイルを読み込み、その内容を新しいワークシートに書き込みます。
:param path: str, optional: 開くCSVファイルのパス。デフォルトはNone。
:param mode: str, optional: ファイルのオープンモード。デフォルトは'r'。
:param encoding: str, optional: CSVファイルのエンコーディング。Noneの場合、自動判別します。デフォルトはNone。
:param split_from: int, optional: 何行目からデータを開始するか(1-based)。Noneの場合、インスタンスの`split_from`を使用。デフォルトはNone。
:param IsPrint: bool, optional: エラーメッセージを標準出力するかどうか。デフォルトはTrue。
:returns: openpyxl.workbook.workbook.Workbook or None: 開かれたOpenPyXLワークブックオブジェクト、または失敗した場合はNone。
"""
if encoding is None:
encoding = get_encoding(path, defval = None)
self.wb = openpyxl.Workbook()
self.fp = None
if self.wb is None:
print("")
print(f"Error in tkexcel.open(): Cannot open [{path}]")
print( " Would be an encrypted file")
return None
if split_from is None:
split_from = self.split_from
self.ws = self.wb.active
fp = open(path, mode = mode, encoding = encoding)
reader = csv.reader(fp)
irow = 1
for i in range(split_from - 1):
line = fp.readline()
if not line:
break
self.ws.cell(irow, 1).value = line.strip
irow += 1
for row in reader:
for icol in range(len(row)):
self.ws.cell(irow, icol + 1).value = pconv(row[icol], row[icol])
irow += 1
fp.close()
return self.wb
[ドキュメント]
def open(self, path = None, mode = None, data_only = True, keep_vbas = False,
password = None, allow_no_password = False, tmp_file = "tmp.xlsx", try_text_file = False,
encoding = None, split_from = None, IsPrint = True):
"""
Excelファイル、またはオプションでCSV/テキストファイルを開きます。
詳細説明:
ファイルタイプを判別し、適切な方法でファイルを開きます。
書き込みモードの場合は既存ファイルを削除し、新しいワークブックを作成します。
読み込みモードの場合は、既存のワークブックをロードします。
暗号化されたファイルは、別途`open_encrypted`メソッド経由で処理されます。
:param path: str, optional: 開くファイルのパス。Noneの場合、インスタンスの`path`を使用。デフォルトはNone。
:param mode: str, optional: ファイルのオープンモード ('r':読み込み, 'w':書き込み, 'a':追記)。Noneの場合、インスタンスの`mode`を使用。デフォルトはNone。
:param data_only: bool, optional: セルの値を数式ではなく表示されている値として読み込むかどうか。デフォルトはTrue。
:param keep_vbas: bool, optional: VBAマクロを保持するかどうか。デフォルトはFalse。
:param password: str, optional: 暗号化ファイルを開く際のパスワード。指定がある場合は`open_encrypted`を呼び出す。デフォルトはNone。
:param allow_no_password: bool, optional: `open_encrypted`に渡される引数。デフォルトはFalse。
:param tmp_file: str, optional: `open_encrypted`に渡される引数。デフォルトは"tmp.xlsx"。
:param try_text_file: bool, optional: Excel形式でない場合、テキストファイルとして読み込みを試みるか。デフォルトはFalse。
:param encoding: str, optional: テキストファイルまたはCSVファイルを読み込む際のエンコーディング。デフォルトはNone。
:param split_from: int, optional: テキストファイルまたはCSVファイルを読み込む際、何行目からデータを開始するか(1-based)。Noneの場合、インスタンスの`split_from`を使用。デフォルトはNone。
:param IsPrint: bool, optional: エラーメッセージを標準出力するかどうか。デフォルトはTrue。
:returns: openpyxl.workbook.workbook.Workbook or None: 開かれたOpenPyXLワークブックオブジェクト、または失敗した場合はNone。
"""
if password is not None and password != "":
return self.open_encrypted(path = path, mode = mode, password = password, allow_no_password = allow_no_password,
tmp_file = tmp_file,
try_text_file = try_text_file, data_only = data_only,
keep_vbas = keep_vbas, encoding = encoding, split_from = split_from, IsPrint = IsPrint)
self.initialize()
self.SetPath(path, mode)
if split_from is None:
split_from = self.split_from
self.wb = None
self.ws = None
self.fp = None
if path is None:
path = self.path
dirname, basename, filebody, ext = split_file_path(path)
if ext.lower() == '.csv':
return self.open_csv(path = path, mode = mode, encoding = encoding, split_from = split_from, IsPrint = IsPrint)
elif try_text_file and ext.lower() == '.txt':
return self.open_text(path = path, mode = mode, encoding = encoding, split_from = split_from, IsPrint = IsPrint)
elif ext.lower() != ".xlsx" and ext.lower() != ".xlsm":
return None
if self.mode == 'w':
if IsFile(path) and not delete_file(path):
self.errormsg(sys._getframe().f_code.co_name, "Can not delete [{}]".format(self.path))
self.wb = openpyxl.Workbook()
elif self.mode == 'a':
# UserWarning: Data Validation extension is not supported and will be removed がでるのでWarning抑制
openpyxl.reader.excel.warnings.simplefilter('ignore')
self.wb = openpyxl.load_workbook(self.path, data_only = data_only, keep_vba = keep_vbas)
else:
if not os.path.isfile(path):
print("")
print(f"Error in tkexcel.open() (line233): [{path}] does not exist.")
return None
# UserWarning: Data Validation extension is not supported and will be removed がでるのでWarning抑制
openpyxl.reader.excel.warnings.simplefilter('ignore')
try:
self.wb = openpyxl.load_workbook(self.path, data_only = data_only)
except:
print("")
print(f"Error in tkexcel.open(): Cannot load workbook from [{path}].")
print( " Would be an encrypted file")
return None
self.ws = self.wb.active
if self.ws is None:
print("")
print(f"Error in tkexcel.open(): Cannot open [{path}]")
print( " Would be an encrypted file")
return None
# print("s=", self.path, self.mode, self.wb, self.ws)
return self.wb
[ドキュメント]
def Open(self, path = None, mode = None, data_only = True,
password = None, allow_no_password = False, tmp_file = "tmp.xlsx", try_text_file = False,
keep_vbas = False, encoding = None, IsPrint = True):
"""
`open`メソッドのエイリアスです。
:param path: str, optional: 開くファイルのパス。デフォルトはNone。
:param mode: str, optional: ファイルのオープンモード。デフォルトはNone。
:param data_only: bool, optional: セルの値を数式ではなく表示されている値として読み込むかどうか。デフォルトはTrue。
:param password: str, optional: 暗号化ファイルを開く際のパスワード。デフォルトはNone。
:param allow_no_password: bool, optional: パスワード指定時に開けなかった場合、パスワードなしで再試行するかどうか。デフォルトはFalse。
:param tmp_file: str, optional: 暗号化ファイルを復号する際に一時的に使用するファイル名。デフォルトは"tmp.xlsx"。
:param try_text_file: bool, optional: Excel形式でない場合、テキストファイルとして読み込みを試みるか。デフォルトはFalse。
:param keep_vbas: bool, optional: VBAマクロを保持するかどうか。デフォルトはFalse。
:param encoding: str, optional: テキストファイルまたはCSVファイルを読み込む際のエンコーディング。デフォルトはNone。
:param IsPrint: bool, optional: エラーメッセージを標準出力するかどうか。デフォルトはTrue。
:returns: openpyxl.workbook.workbook.Workbook or None: 開かれたOpenPyXLワークブックオブジェクト、または失敗した場合はNone。
"""
if password is not None and password != "":
return self.open_encrypted(path = path, mode = mode, password = password, allow_no_password = allow_no_password,
tmp_file = tmp_file,
try_text_file = try_text_file, data_only = data_only,
keep_vbas = keep_vbas, encoding = encoding, split_from = split_from, IsPrint = IsPrint)
return self.open(path = path, mode = mode, try_text_file = try_text_file, data_only = data_only, keep_vbas = keep_vbas,
encoding = encoding, IsPrint = IsPrint)
[ドキュメント]
def open_encrypted(self, path = None, mode = None, password = None, allow_no_password = False,
tmp_file = 'tmp.xlsx',
try_text_file = False, data_only = True, keep_vbas = False, encoding = None,
split_from = None, IsPrint = True):
"""
パスワードで保護された暗号化Excelファイルを開きます。
詳細説明:
`msoffcrypto`ライブラリを使用してファイルを復号し、一時ファイルとして保存してからOpenPyXLで開きます。
処理完了後、一時ファイルは削除されます。
:param path: str, optional: 開く暗号化Excelファイルのパス。Noneの場合、インスタンスの`path`を使用。デフォルトはNone。
:param mode: str, optional: ファイルのオープンモード。Noneの場合、インスタンスの`mode`を使用。デフォルトはNone。
:param password: str, optional: 暗号化されたExcelファイルを開く際のパスワード。デフォルトはNone。
:param allow_no_password: bool, optional: パスワード指定時に開けなかった場合、パスワードなしで再試行するかどうか。デフォルトはFalse。
:param tmp_file: str, optional: 復号したファイルを一時的に保存するファイル名。Noneの場合、エラー終了。デフォルトは'tmp.xlsx'。
:param try_text_file: bool, optional: 復号後に通常オープンする際にテキストファイルとして試行するかどうか。デフォルトはFalse。
:param data_only: bool, optional: 復号後に通常オープンする際にセル値を数式ではなく表示値として読み込むかどうか。デフォルトはTrue。
:param keep_vbas: bool, optional: VBAマクロを保持するかどうか。デフォルトはFalse。
:param encoding: str, optional: 復号後に通常オープンする際に使用するエンコーディング。デフォルトはNone。
:param split_from: int, optional: 復号後に通常オープンする際に、テキスト/CSVファイルとして読み込む場合の開始行。デフォルトはNone。
:param IsPrint: bool, optional: 処理中にメッセージを標準出力するかどうか。デフォルトはTrue。
:returns: openpyxl.workbook.workbook.Workbook or None: 開かれたOpenPyXLワークブックオブジェクト、または失敗した場合はNone。
"""
if msoffcrypto is None:
print("")
print("####################################################################")
print(" Error in tklib.tkexcel.open_encrypted(): module [msoffcrypto] is not installed.")
print(" You cannot open encrypted Excel files protected with password")
print(" Install by pip install msoffcrypto-tool if needed")
print("####################################################################")
if allow_no_password and (password is None or password == ''):
print("Try to open without password")
ret = self.open(path = path, mode = mode, try_text_file = try_text_file, data_only = data_only, keep_vbas = keep_vbas,
encoding = encoding, split_from = split_from, IsPrint = IsPrint)
return ret
else:
return None
if password is None or password == '':
ret = self.open(path = path, mode = mode, try_text_file = try_text_file, data_only = data_only, keep_vbas = keep_vbas,
encoding = encoding, split_from = split_from, IsPrint = IsPrint)
return ret
# https://news.mynavi.jp/techplus/article/zeropython-98/
if path is None:
path = self.path
if IsPrint:
print(f"パスワード付きExcelファイル {path} の読み込み")
decrypted = io.BytesIO()
with open(path, 'rb') as fp:
msfile = msoffcrypto.OfficeFile(fp)
try:
msfile.load_key(password = password)
msfile.decrypt(decrypted)
print(f" In tkexcel.open_encrypted(): Succeeded in opening encrypted file [{path}]")
except:
print()
if not allow_no_password:
print(f"Error in tkexcel.open_encrypted(): password is given but could not open [{path}]")
print()
exit()
else:
print(f"Warning in tkexcel.open_encrypted(): password is given but could not open [{path}]")
print(f" Try to open [{path}] without passwd.")
tmp_file = None
password = None
# 暗号解除した後ファイルに保存 --- (*3)
if password is not None and password != "" and tmp_file is None:
print(f"\nError in tkexcel.open_encrypted(): File is encrypted so tmp_file must be given (given None)\n")
exit()
if IsPrint:
if tmp_file:
print(f" {tmp_file} に保存")
with open(tmp_file, 'wb') as fp:
fp.write(decrypted.getbuffer())
else:
tmp_file = path
if IsPrint:
print(f" {tmp_file} を読み込み")
self.wb = self.open(path = tmp_file, mode = mode,
data_only = data_only, encoding = encoding, IsPrint = IsPrint)
if IsPrint:
print(f" {tmp_file} を削除")
os.remove(tmp_file)
return self.wb
[ドキュメント]
def close(self):
"""
開いているExcelワークブックを閉じ、変更が加えられていれば保存します。
"""
if self.wb is not None:
if self.mode == 'w' or self.mode == 'a':
self.wb.save(self.path)
# self.wb = None
[ドキュメント]
def Close(self):
"""
`close`メソッドのエイリアスです。
"""
self.close()
[ドキュメント]
def set_path(self, path = None, mode = None):
"""
Excelファイルのパスとオープンモードを設定します。
:param path: str, optional: 設定するファイルのパス。Noneの場合、現在のパスを維持。デフォルトはNone。
:param mode: str, optional: 設定するファイルのオープンモード。Noneの場合、現在のモードを維持。デフォルトはNone。
"""
self.path = self.IfYes(path is not None, path, self.path)
self.mode = self.IfYes(mode is not None, mode, self.mode)
[ドキュメント]
def SetPath(self, path = None, mode = None):
"""
`set_path`メソッドのエイリアスです。
:param path: str, optional: 設定するファイルのパス。デフォルトはNone。
:param mode: str, optional: 設定するファイルのオープンモード。デフォルトはNone。
"""
return self.set_path(path = path, mode = mode)
[ドキュメント]
def max_row(self):
"""
現在のワークシートの最大行数を取得します。
:returns: int or None: 最大行数 (1-based)。ワークシートがNoneの場合はNone。
"""
if self.ws is None:
return None
return self.ws.max_row
[ドキュメント]
def max_column(self):
"""
現在のワークシートの最大列数を取得します。
:returns: int or None: 最大列数 (1-based)。ワークシートがNoneの場合はNone。
"""
if self.ws is None:
return None
return self.ws.max_column
[ドキュメント]
def update_cell(self, cell_name, val):
"""
指定された名前付き範囲のセル値を更新します。
詳細説明:
Excelの「名前の定義」で指定されたセル範囲を検索し、その値を更新します。
:param cell_name: str: 更新するセルの名前付き範囲。
:param val: any: 設定する値。
:returns: bool: 更新が成功した場合はTrue、見つからなかった場合はFalse。
"""
wb = self.wb
lower = cell_name.lower()
for name in wb.defined_names:
if name.lower() == lower:
dn = wb.defined_names[name]
for sheet_name, cell_addr in dn.destinations:
ws = wb[sheet_name]
ws[cell_addr].value = val
return True
return False
[ドキュメント]
def set(self, row, column, val, ws = None):
"""
指定されたワークシートのセルに値を設定します (1-basedインデックス)。
:param row: int: 設定するセルの行インデックス (1-based)。
:param column: int: 設定するセルの列インデックス (1-based)。
:param val: any: 設定する値。
:param ws: openpyxl.worksheet.worksheet.Worksheet, optional: 値を設定するワークシート。Noneの場合、現在のワークシートを使用。デフォルトはNone。
"""
ws = self.get_sheet(ws)
ws.cell(row = row, column = column).value = val
[ドキュメント]
def set_val(self, irow, icol, val, ws = None):
"""
指定されたワークシートのセルに値を設定します (0-basedインデックス)。
:param irow: int: 設定するセルの行インデックス (0-based)。
:param icol: int: 設定するセルの列インデックス (0-based)。
:param val: any: 設定する値。
:param ws: openpyxl.worksheet.worksheet.Worksheet, optional: 値を設定するワークシート。Noneの場合、現在のワークシートを使用。デフォルトはNone。
"""
ws = self.get_sheet(ws)
ws.cell(row = irow + 1, column = icol + 1).value = val
[ドキュメント]
def get(self, row, column, def_val = None, ws = None):
"""
指定されたワークシートのセルの値を取得します (1-basedインデックス)。
:param row: int: 取得するセルの行インデックス (1-based)。
:param column: int: 取得するセルの列インデックス (1-based)。
:param def_val: any, optional: セルの値がNoneの場合に返すデフォルト値。デフォルトはNone。
:param ws: openpyxl.worksheet.worksheet.Worksheet, optional: 値を取得するワークシート。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:returns: any: セルの値。
"""
ws = self.get_sheet(ws)
val = ws.cell(row, column).value
if def_val is not None and val is None:
val = def_val
return val
[ドキュメント]
def get_val(self, irow, icol, def_val = None, ws = None):
"""
指定されたワークシートのセルの値を取得します (0-basedインデックス)。
:param irow: int: 取得するセルの行インデックス (0-based)。
:param icol: int: 取得するセルの列インデックス (0-based)。
:param def_val: any, optional: セルの値がNoneの場合に返すデフォルト値。デフォルトはNone。
:param ws: openpyxl.worksheet.worksheet.Worksheet, optional: 値を取得するワークシート。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:returns: any: セルの値。
"""
ws = self.get_sheet(ws)
val = ws.cell(irow+1, icol+1).value
if def_val is not None and val is None:
val = def_val
return val
[ドキュメント]
def Get(self, irow, icol):
"""
`get_val`メソッドのエイリアスです。
:param irow: int: 取得するセルの行インデックス (0-based)。
:param icol: int: 取得するセルの列インデックス (0-based)。
:returns: any: セルの値。
"""
return self.get_val(irow, icol)
[ドキュメント]
def get_labels(self, ws = None, irow_origin = 1, icol_origin = 1):
"""
指定された行からラベル(列ヘッダー)のリストを取得します。
:param ws: openpyxl.worksheet.worksheet.Worksheet, optional: ラベルを取得するワークシート。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:param irow_origin: int, optional: ラベルが存在する行インデックス (1-based)。デフォルトは1。
:param icol_origin: int, optional: ラベルが開始する列インデックス (1-based)。デフォルトは1。
:returns: list: ラベル文字列のリスト。
"""
if ws is None:
ws = self.ws
labels = []
for icol in range(icol_origin, self.max_column() + 1):
val = ws.cell(row = irow_origin, column = icol).value
labels.append(val)
return labels
[ドキュメント]
def append_data(self, data, print_level = 0):
"""
データ(辞書形式)をワークシートの最終行に追記します。
詳細説明:
辞書のキーが既存のラベル(1行目)にない場合、新しい列を追加してキーをラベルとして設定します。
:param data: dict: 追記するデータ。キーはラベル名、値は対応するデータ。
:param print_level: int, optional: ログ出力のレベル。デフォルトは0。
"""
labels = self.get_labels()
ndata = self.max_row() - 1 # データ本体の行数 (ラベル行を除く)
for key, val in data.items():
# keyが無かったら列を追加
if key not in labels:
icol = self.max_column() + 1
if print_level:
print(f"key [{key}] is not in the labels. Add [{key}] to icol={icol}")
self.set(row = 1, column = icol, val = key)
labels = self.get_labels() # ラベルを再取得
# 最終行に値を追加
self.set(row = ndata + 2, column = labels.index(key) + 1,
val = pfloat(val, val)) # pfloatで変換を試みる
[ドキュメント]
def get_column_letter(self, i):
"""
列の数値インデックス (1-based) をアルファベット表記に変換します。
:param i: int: 列の数値インデックス (1-based)。
:returns: str: 列のアルファベット表記 (例: 1 -> "A")。
"""
return openpyxl.utils.get_column_letter(i)
[ドキュメント]
def get_icol_from_letter(self, s):
"""
列のアルファベット表記を数値インデックス (1-based) に変換します。
:param s: str: 列のアルファベット表記 (例: "A")。
:returns: int: 列の数値インデックス (1-based)。
"""
return openpyxl.utils.column_index_from_string(s)
[ドキュメント]
def get_irow_from_label(self, label, column_org = 1, row_org = 1, sheet = None):
"""
指定された列内で特定のラベル(値)を持つ最初の行のインデックスを取得します。
:param label: str or int or float: 検索するラベル(値)。
:param column_org: int, optional: 検索する列のインデックス (1-based)。デフォルトは1。
:param row_org: int, optional: 検索を開始する行のインデックス (1-based)。デフォルトは1。
:param sheet: openpyxl.worksheet.worksheet.Worksheet, optional: 検索するワークシート。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:returns: int or None: ラベルが見つかった行のインデックス (1-based)。見つからなかった場合はNone。
"""
if sheet is None:
sheet = self.ws
for i in range(row_org, sheet.max_row + 1):
s = sheet.cell(row = i, column = column_org).value
if type(s) is int or type(label) is int:
if pint(s) == pint(label):
return i
elif type(s) is float or type(label) is float:
if pfloat(s) == pfloat(label):
return i
else:
if s == label:
return i
return None
[ドキュメント]
def get_icolumn_from_label_regex(self, label, column_org = 1, row_org = 1, sheet = None, flags = 0):
"""
指定された行内で正規表現に一致するラベルを持つ最初の列のインデックスを取得します。
:param label: str or int or float: 検索するラベル(値)、または正規表現パターン。
:param column_org: int, optional: 検索を開始する列のインデックス (1-based)。デフォルトは1。
:param row_org: int, optional: 検索する行のインデックス (1-based)。デフォルトは1。
:param sheet: openpyxl.worksheet.worksheet.Worksheet, optional: 検索するワークシート。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:param flags: int, optional: `re`モジュールの正規表現フラグ (例: `re.IGNORECASE`)。デフォルトは0。
:returns: int or None: ラベルが見つかった列のインデックス (1-based)。見つからなかった場合はNone。
"""
if sheet is None:
sheet = self.ws
if sheet is None:
return None
for i in range(column_org, sheet.max_column + 1):
s = sheet.cell(row = row_org, column = i).value
if type(s) is int or type(label) is int:
if pint(s) == pint(label):
return i
elif type(s) is float or type(label) is float:
if pfloat(s) == pfloat(label):
return i
else:
# print("l=", label, s)
if s is None:
continue
if re.search(label, s, flags = flags):
return i
if label == s:
return i
return None
[ドキュメント]
def get_icolumn_from_label1(self, label, column_org = 1, row_org = 1, sheet = None):
"""
指定された行内で特定のラベル(値)を持つ最初の列のインデックスを取得します(厳密な一致)。
:param label: str or int or float: 検索するラベル(値)。
:param column_org: int, optional: 検索を開始する列のインデックス (1-based)。デフォルトは1。
:param row_org: int, optional: 検索する行のインデックス (1-based)。デフォルトは1。
:param sheet: openpyxl.worksheet.worksheet.Worksheet, optional: 検索するワークシート。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:returns: int or None: ラベルが見つかった列のインデックス (1-based)。見つからなかった場合はNone。
"""
if sheet is None:
sheet = self.ws
if sheet is None:
return None
for i in range(column_org, sheet.max_column + 1):
s = sheet.cell(row = row_org, column = i).value
if type(s) is int or type(label) is int:
if pint(s) == pint(label):
return i
elif type(s) is float or type(label) is float:
if pfloat(s) == pfloat(label):
return i
else:
if s == label:
return i
return None
[ドキュメント]
def get_icolumn_from_label(self, label, column_org = 1, row_org = 1, sheet = None):
"""
指定された行内で特定のラベル(値)またはラベルのリストを持つ列のインデックスを取得します。
詳細説明:
`label`がリストまたはタプルの場合、各要素に対して`get_icolumn_from_label1`を呼び出し、
結果のリストを返します。単一のラベルの場合は、その列インデックスを返します。
:param label: str or int or float or list or tuple: 検索するラベル(値)、またはラベルのリスト/タプル。
:param column_org: int, optional: 検索を開始する列のインデックス (1-based)。デフォルトは1。
:param row_org: int, optional: 検索する行のインデックス (1-based)。デフォルトは1。
:param sheet: openpyxl.worksheet.worksheet.Worksheet, optional: 検索するワークシート。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:returns: int or list or None: 見つかった列のインデックス (1-based)。`label`がリストの場合、インデックスのリスト。見つからなかった場合はNone。
"""
if type(label) is list or type(label) is tuple:
ilist = []
for s in label:
val = self.get_icolumn_from_label1(s, column_org = column_org, row_org = row_org, sheet = sheet)
ilist.append(val)
return ilist
else:
return self.get_icolumn_from_label1(label, column_org = column_org, row_org = row_org, sheet = sheet)
[ドキュメント]
def find_data_array(self, regexp, flags = 0, convert_type = None, def_val = None):
"""
ラベル(またはインデックス)に基づいて、指定された列のデータを配列として取得します。
詳細説明:
regexpが整数であれば列インデックスとして、文字列であれば正規表現として列を検索します。
取得したデータは指定された型に変換されます。
:param regexp: str or int: 列のラベル(正規表現可)または列インデックス (1-based)。
:param flags: int, optional: `re`モジュールの正規表現フラグ。デフォルトは0。
:param convert_type: type, optional: 取得したデータを変換する型 (例: float, int)。デフォルトはNone(変換なし)。
:param def_val: any, optional: セルの値がNoneの場合に返すデフォルト値。'input'を指定すると元の値を使用。デフォルトはNone。
:returns: tuple: (ラベル名, データリスト)。ラベルが見つからなかったり、エラーが発生した場合は (None, None)。
"""
if type(regexp) is int:
idx = regexp
else:
try:
idx = int(regexp)
except:
idx = self.get_icolumn_from_label_regex(regexp, flags = flags)
ncolumns = self.max_column()
if ncolumns is None:
print(f"\nError in tkexcel.find_data_array(): Can not get worksheet)")
return None, None
if ncolumns < idx:
print(f"\nError in tkexcel.find_data_array(): idx={idx} is not found (max_columns={ncolumns})")
return None, None
nrows = self.max_row()
labels = self.get_labels()
data_list = []
for irow in range(2, nrows + 1): # 2行目からデータ開始
v = self.get(irow, idx, def_val = def_val)
if convert_type is float and v is not None:
if def_val == 'input':
dv = v
else:
dv = None
v = pfloat(v, defval = dv)
elif convert_type is int and v is not None:
if def_val == 'input':
dv = v
else:
dv = None
v = pint(v, defval = dv)
data_list.append(v)
return labels[idx-1], data_list
[ドキュメント]
def read_data(self, idata = None, flags = '', convert_type = None, def_val = None, is_print = True):
"""
複数の列からデータを読み込み、ラベルとデータリストのペアを返します。
:param idata: list or tuple, optional: 読み込む列のインデックス (1-based) またはラベル(正規表現可)のリスト。Noneの場合、全列を読み込む。デフォルトはNone。
:param flags: int, optional: `re`モジュールの正規表現フラグ(`idata`が文字列の場合)。デフォルトは''。
:param convert_type: type, optional: 取得したデータを変換する型 (例: float, int)。デフォルトはNone。
:param def_val: any, optional: セルの値がNoneの場合に返すデフォルト値。'input'を指定すると元の値を使用。デフォルトはNone。
:param is_print: bool, optional: (未使用)
:returns: tuple: (ラベル名のリスト, データのリストのリスト)。
"""
if idata is None:
idata = [i + 1 for i in range(self.ws.max_column)]
labels = []
data_list = []
for idx in idata:
label, data = self.find_data_array(regexp = idx, flags = flags, convert_type = convert_type, def_val = def_val)
if label is None or label == '':
continue
labels.append(label)
data_list.append(data)
return labels, data_list
[ドキュメント]
def get_row_data(self, irow, def_val = None, ws = None):
"""
指定された行のデータをリストとして取得します (0-basedインデックス)。
:param irow: int: 取得する行のインデックス (0-based)。
:param def_val: any, optional: セルの値がNoneの場合に返すデフォルト値。デフォルトはNone。
:param ws: openpyxl.worksheet.worksheet.Worksheet, optional: 値を取得するワークシート。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:returns: list: 指定された行のセルの値のリスト。
"""
ws = self.get_sheet(ws)
data = []
for icol in range(1, self.ws.max_column + 1):
data.append(self.get(row = irow + 1, column = icol, def_val = def_val, ws = ws))
return data
[ドキュメント]
def get_col_data(self, icol, def_val = None, ws = None):
"""
指定された列のデータをリストとして取得します (0-basedインデックス)。
:param icol: int or str: 取得する列のインデックス (0-based) または列のラベル名。
:param def_val: any, optional: セルの値がNoneの場合に返すデフォルト値。デフォルトはNone。
:param ws: openpyxl.worksheet.worksheet.Worksheet, optional: 値を取得するワークシート。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:returns: list: 指定された列のセルの値のリスト。
"""
ws = self.get_sheet(ws)
if type(icol) is str:
# def get_icolumn_from_label_regex(self, label, column_org = 1, row_org = 1, sheet = None, flags = 0):
icol = self.get_icolumn_from_label_regex(icol)
# icol = self.get_icol_from_letter(icol)
data = []
for irow in range(2, self.ws.max_row + 1): # 2行目からデータ本体
data.append(self.get(row = irow, column = icol))
return data
[ドキュメント]
def get_specified_data(self, indexes = None, rows = None, convert_label = True):
"""
指定された列と行範囲のデータを取得します。
詳細説明:
`indexes`で指定された列と`rows`で指定された行からデータを抽出します。
`convert_label`がTrueの場合、ラベルに使用できない文字を'_'に置換します。
:param indexes: list or tuple, optional: 取得する列のインデックス (1-based) のリスト。Noneの場合、全列。デフォルトはNone。
:param rows: list or tuple, optional: 取得する行のインデックス (1-based) のリスト。Noneの場合、全行。デフォルトはNone。
:param convert_label: bool, optional: ラベルに含まれる特殊文字を変換するかどうか。デフォルトはTrue。
:returns: tuple: (ラベル名のリスト, データのリストのリスト)。
"""
if indexes is None:
indexes = range(1, self.max_column() + 1)
if rows is None:
rows = range(1, self.max_row() + 1)
nindexes = len(indexes)
nrows = len(rows)
ws = self.ws
labels = []
for i in indexes:
val1 = ws.cell(row = 1, column = i).value
# シート名に使えない文字を_に置換
if convert_label:
if val1 is None or val1 == '':
val1 = f'{i}列'
else:
val1 = re.sub(r'[\r\n\\/:]', '_', str(val1)) # val1がNoneでないことを保証するためstr()を追加
labels.append(val1)
data_list = []
for i in range(nindexes):
data_list.append([])
for i in range(nindexes):
for j in range(nrows):
data_list[i].append(None)
for i in range(nindexes):
icol = indexes[i]
for j in range(nrows):
irow = rows[j]
# print("i=", i, icol, irow, end = '')
val = ws.cell(row = irow, column = icol).value
data_list[i][j] = val
# print(f" {val}")
return labels, data_list
[ドキュメント]
def get_sheet_by_name(self, title):
"""
指定された名前のワークシートを取得します。
:param title: str: 取得するワークシートのタイトル名。
:returns: openpyxl.worksheet.worksheet.Worksheet: 指定された名前のワークシートオブジェクト。
"""
return self.wb.get_sheet_by_name(title)
[ドキュメント]
def find_isheet(self, name, reg_exp = False):
"""
ワークシートの名前(または正規表現)に基づいて、シートのインデックス、名前、およびオブジェクトを検索します。
:param name: str or int: 検索するシートの名前、またはシートのインデックス。
:param reg_exp: bool, optional: `name`が正規表現として解釈されるかどうか。デフォルトはFalse。
:returns: tuple: (シートインデックス, シート名, ワークシートオブジェクト)。見つからなかった場合は (None, None, None)。
"""
sheetnames = self.wb.sheetnames
if type(name) is not str: # int型としてインデックスが渡された場合
for i in range(len(self.wb.worksheets)):
if self.wb.worksheets[i] == self.ws:
return i, self.wb.sheetnames[i], self.wb.worksheets[i]
else:
return None, None, None
if reg_exp:
for i in range(len(sheetnames)):
if re.search(name, sheetnames[i]):
return i, sheetnames[i], self.wb.worksheets[i] # nameではなくsheetnames[i]を返すのが適切
else:
for i in range(len(sheetnames)):
if sheetnames[i] == name:
return i, name, self.wb.worksheets[i]
return None, None, None
[ドキュメント]
def create_sheet(self, index = 0, title = None):
"""
新しいワークシートを作成します。
:param index: int, optional: 新しいシートを挿入する位置のインデックス (0-based)。デフォルトは0。
:param title: str, optional: 新しいシートのタイトル。Noneの場合、自動生成されます。デフォルトはNone。
:returns: openpyxl.worksheet.worksheet.Worksheet: 作成されたワークシートオブジェクト。
"""
return self.wb.create_sheet(index = index, title = title)
[ドキュメント]
def remove_sheet(self, title):
"""
指定されたタイトルのワークシートを削除します。
:param title: str: 削除するワークシートのタイトル名。
"""
self.wb.remove_sheet(self.get_sheet_by_name(title))
[ドキュメント]
def set_sheet_name(self, title, sheet = None):
"""
ワークシートのタイトル名を変更します。
:param title: str: 新しいシートのタイトル名。
:param sheet: openpyxl.worksheet.worksheet.Worksheet, optional: 名前を変更するワークシート。Noneの場合、現在のワークシートを使用。デフォルトはNone。
"""
if sheet is None:
sheet = self.ws
sheet.title = title
[ドキュメント]
def get_sheet(self, index):
"""
インデックスまたはタイトル名に基づいてワークシートオブジェクトを取得します。
:param index: int or str or openpyxl.worksheet.worksheet.Worksheet: ワークシートのインデックス (0-based)、タイトル名、または既存のワークシートオブジェクト。
:returns: openpyxl.worksheet.worksheet.Worksheet: 取得されたワークシートオブジェクト。
"""
if index is None:
return self.ws
if type(index) is int:
# name = self.wb.get_sheet_names()[index]
return self.wb.worksheets[index]
if type(index) is str:
name = index
return self.wb.get_sheet_by_name(name)
return index
[ドキュメント]
def get_column_width(self, icolumn, sheet = None):
"""
指定された列の幅を取得します。
:param icolumn: int or str: 列のインデックス (1-based) またはアルファベット表記。
:param sheet: openpyxl.worksheet.worksheet.Worksheet, optional: ワークシートオブジェクト。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:returns: float: 列の幅。
"""
if sheet is None:
sheet = self.ws
if type(icolumn) == int:
icolumn = openpyxl.utils.get_column_letter(icolumn)
return sheet.column_dimensions[icolumn].width
[ドキュメント]
def set_column_width(self, icolumn, width = None, sheet = None):
"""
指定された列の幅を設定します。
:param icolumn: int or str: 列のインデックス (1-based) またはアルファベット表記。
:param width: float, optional: 設定する列の幅。Noneの場合、幅は設定されない。デフォルトはNone。
:param sheet: openpyxl.worksheet.worksheet.Worksheet, optional: ワークシートオブジェクト。Noneの場合、現在のワークシートを使用。デフォルトはNone。
"""
if sheet is None:
sheet = self.ws
if type(icolumn) == int:
icolumn = openpyxl.utils.get_column_letter(icolumn)
sheet.column_dimensions[icolumn].width = width
[ドキュメント]
def get_column_hidden(self, icolumn, sheet = None):
"""
指定された列の表示/非表示の状態を取得します。
:param icolumn: int or str: 列のインデックス (1-based) またはアルファベット表記。
:param sheet: openpyxl.worksheet.worksheet.Worksheet, optional: ワークシートオブジェクト。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:returns: bool: 列が非表示であればTrue、表示されていればFalse。
"""
if sheet is None:
sheet = self.ws
if type(icolumn) == int:
icolumn = openpyxl.utils.get_column_letter(icolumn)
return sheet.column_dimensions[icolumn].hidden
[ドキュメント]
def set_column_hidden(self, icolumn, hidden = None, sheet = None):
"""
指定された列の表示/非表示の状態を設定します。
:param icolumn: int or str: 列のインデックス (1-based) またはアルファベット表記。
:param hidden: bool, optional: 列を非表示にする場合はTrue、表示する場合はFalse。Noneの場合、設定は変更されない。デフォルトはNone。
:param sheet: openpyxl.worksheet.worksheet.Worksheet, optional: ワークシートオブジェクト。Noneの場合、現在のワークシートを使用。デフォルトはNone。
"""
if sheet is None:
sheet = self.ws
if type(icolumn) == int:
icolumn = openpyxl.utils.get_column_letter(icolumn)
sheet.column_dimensions[icolumn].hidden = hidden
[ドキュメント]
def set_cell_font(self, row, column, sheet = None, font_color = '000000',
italic = False, bold = False, strike = False, underline = None,
size = None, font_name = None,
vertAlign = 'baseline'):
"""
指定されたセルのフォントプロパティを設定します。
:param row: int: セルの行インデックス (1-based)。
:param column: int: セルの列インデックス (1-based)。
:param sheet: openpyxl.worksheet.worksheet.Worksheet, optional: ワークシートオブジェクト。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:param font_color: str, optional: フォントの色 (RRGGBB形式の16進数カラーコード)。デフォルトは'000000' (黒)。
:param italic: bool, optional: 斜体にするかどうか。デフォルトはFalse。
:param bold: bool, optional: 太字にするかどうか。デフォルトはFalse。
:param strike: bool, optional: 取り消し線にするかどうか。デフォルトはFalse。
:param underline: str, optional: 下線のスタイル (例: 'single', 'double', 'singleAccounting', 'doubleAccounting')。デフォルトはNone。
:param size: int or float, optional: フォントサイズ。デフォルトはNone。
:param font_name: str, optional: フォント名 (例: 'Arial')。デフォルトはNone。
:param vertAlign: str, optional: 垂直方向の配置 (例: 'baseline', 'superscript', 'subscript')。デフォルトは'baseline'。
"""
if sheet is None:
sheet = self.ws
font = Font(
color = font_color, bold = bold, italic = italic,
strike = strike, underline = underline,
size = size, name = font_name,
vertAlign = vertAlign)
cell = sheet.cell(row = row, column = column)
cell.font = font
[ドキュメント]
def set_cell_fill(self, row, column, sheet = None, fgcolor = 'FFFFFF', bgcolor = 'FFFFFF', pattern = 'solid'):
"""
指定されたセルの塗りつぶしプロパティを設定します。
:param row: int: セルの行インデックス (1-based)。
:param column: int: セルの列インデックス (1-based)。
:param sheet: openpyxl.worksheet.worksheet.Worksheet, optional: ワークシートオブジェクト。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:param fgcolor: str, optional: 前景色の色 (RRGGBB形式の16進数カラーコード)。デフォルトは'FFFFFF' (白)。
:param bgcolor: str, optional: 背景色の色 (RRGGBB形式の16進数カラーコード)。デフォルトは'FFFFFF' (白)。
:param pattern: str, optional: 塗りつぶしのパターンタイプ (例: 'solid', 'lightGrid', 'darkGrid')。デフォルトは'solid'。
"""
if sheet is None:
sheet = self.ws
fill = PatternFill(patternType = pattern, fgColor = fgcolor, bgColor = bgcolor)
cell = sheet.cell(row = row, column = column)
cell.fill = fill
[ドキュメント]
def find_val_in_row(self, val, irow, column_org = 1, sheet = None):
"""
指定された行内で特定の値を検索し、その値が見つかった列のインデックスを返します。
:param val: any: 検索する値。
:param irow: int: 検索する行のインデックス (1-based)。
:param column_org: int, optional: 検索を開始する列のインデックス (1-based)。デフォルトは1。
:param sheet: openpyxl.worksheet.worksheet.Worksheet, optional: ワークシートオブジェクト。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:returns: int or None: 値が見つかった列のインデックス (1-based)。見つからなかった場合はNone。
"""
return self.get_icolumn_from_label(val, column_org = column_org, row_org = irow, sheet = sheet)
[ドキュメント]
def find_val_in_column(self, val, icolumn, row_org = 1, sheet = None, auto_add = False):
"""
指定された列内で特定の値を検索し、その値が見つかった行のインデックスを返します。
`auto_add`がTrueの場合、見つからなければ最終行に値を追加します。
:param val: any: 検索する値。
:param icolumn: int: 検索する列のインデックス (1-based)。
:param row_org: int, optional: 検索を開始する行のインデックス (1-based)。デフォルトは1。
:param sheet: openpyxl.worksheet.worksheet.Worksheet, optional: ワークシートオブジェクト。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:param auto_add: bool, optional: 値が見つからなかった場合に、列の最終データ行の下に値を追加するかどうか。デフォルトはFalse。
:returns: int or None: 値が見つかった行のインデックス (1-based)。`auto_add`がTrueで追加された場合は追加された行のインデックス。見つからなかった場合はNone。
"""
if sheet is None:
sheet = self.ws
ilast_vald_data = None
for irow in range(row_org, sheet.max_row + 1):
s = sheet.cell(row = irow, column = icolumn).value
if s == val:
return irow
if s is not None and s != '':
ilast_vald_data = irow
if auto_add:
ilast_vald_data = (ilast_vald_data or row_org - 1) + 1 # ilast_vald_dataがNoneの場合、row_org-1から開始
sheet.cell(row = ilast_vald_data, column = icolumn).value = val
return ilast_vald_data
else:
return None
[ドキュメント]
def freeze_panes(self, cell = 'A1', sheet = None):
"""
ワークシートのペインを固定します。
:param cell: str, optional: 固定する範囲の左上のセル (例: "A1", "B2")。デフォルトは'A1' (固定なし)。
:param sheet: openpyxl.worksheet.worksheet.Worksheet, optional: ワークシートオブジェクト。Noneの場合、現在のワークシートを使用。デフォルトはNone。
"""
if sheet is None:
sheet = self.ws
sheet.freeze_panes = cell
[ドキュメント]
def store_column_widths(self, sheet = None, column_org = 1, row_org = 1):
"""
現在のワークシートの列幅を保存します。
詳細説明:
列幅をリストと辞書(ラベル名がキー)の両方で保存します。
:param sheet: openpyxl.worksheet.worksheet.Worksheet, optional: ワークシートオブジェクト。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:param column_org: int, optional: 列幅を保存する開始列 (1-based)。デフォルトは1。
:param row_org: int, optional: ラベルを取得する行 (1-based)。デフォルトは1。
:returns: tuple: (列幅のリスト, ラベル名をキーとする列幅の辞書)。
"""
if sheet is None:
sheet = self.ws
max_column = sheet.max_column
self.width_list = []
self.width_dict = {}
for icol in range(column_org, sheet.max_column+1):
w = self.get_column_width(icol, sheet = sheet)
label = sheet.cell(row = row_org, column = icol).value
self.width_list.append(w)
self.width_dict[label] = w
return self.width_list, self.width_dict
[ドキュメント]
def restore_column_widths(self, sheet = None, column_org = 1, row_org = 1, by_label = True):
"""
保存された列幅をワークシートに復元します。
:param sheet: openpyxl.worksheet.worksheet.Worksheet, optional: ワークシートオブジェクト。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:param column_org: int, optional: 列幅を復元する開始列 (1-based)。デフォルトは1。
:param row_org: int, optional: ラベルを取得する行 (1-based)。デフォルトは1。
:param by_label: bool, optional: ラベル名に基づいて列幅を復元するか、インデックスに基づいて復元するか。デフォルトはTrue。
"""
if sheet is None:
sheet = self.ws
return None
if by_label:
for label in self.width_dict.keys():
icol = self.get_icolumn_from_label(label, sheet = sheet, column_org = column_org, row_org = row_org)
if icol is not None:
self.set_column_width(icol, width = self.width_dict[label], sheet = sheet)
else:
max_column = sheet.max_column
for icol in range(column_org, sheet.max_column+1):
if icol - column_org < len(self.width_list): # 範囲チェック
self.set_column_width(icol, width = self.width_list[icol - column_org], sheet = sheet)
[ドキュメント]
def store_column_hidden(self, sheet = None, column_org = 1, row_org = 1):
"""
現在のワークシートの列の表示/非表示状態を保存します。
詳細説明:
列の表示/非表示状態をリストと辞書(ラベル名がキー)の両方で保存します。
:param sheet: openpyxl.worksheet.worksheet.Worksheet, optional: ワークシートオブジェクト。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:param column_org: int, optional: 状態を保存する開始列 (1-based)。デフォルトは1。
:param row_org: int, optional: ラベルを取得する行 (1-based)。デフォルトは1。
:returns: tuple: (表示/非表示状態のリスト, ラベル名をキーとする表示/非表示状態の辞書)。
"""
if sheet is None:
sheet = self.ws
max_column = sheet.max_column
self.hidden_list = []
self.hidden_dict = {}
for icol in range(column_org, sheet.max_column+1):
h = self.get_column_hidden(icol, sheet = sheet)
label = sheet.cell(row = row_org, column = icol).value
# print("icol=", icol, label, h)
self.hidden_list.append(h)
self.hidden_dict[label] = h
return self.hidden_list, self.hidden_dict
[ドキュメント]
def restore_column_hidden(self, sheet = None, column_org = 1, row_org = 1, by_label = True):
"""
保存された列の表示/非表示状態をワークシートに復元します。
:param sheet: openpyxl.worksheet.worksheet.Worksheet, optional: ワークシートオブジェクト。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:param column_org: int, optional: 状態を復元する開始列 (1-based)。デフォルトは1。
:param row_org: int, optional: ラベルを取得する行 (1-based)。デフォルトは1。
:param by_label: bool, optional: ラベル名に基づいて状態を復元するか、インデックスに基づいて復元するか。デフォルトはTrue。
"""
if sheet is None:
sheet = self.ws
return None
if by_label:
for label in self.hidden_dict.keys():
icol = self.get_icolumn_from_label(label, sheet = sheet, column_org = column_org, row_org = row_org)
if icol is not None:
self.set_column_hidden(icol, hidden = self.hidden_dict[label], sheet = sheet)
else:
max_column = sheet.max_column
for icol in range(column_org, sheet.max_column+1):
if icol - column_org < len(self.hidden_list): # 範囲チェック
self.set_column_hidden(icol, hidden = self.hidden_list[icol - column_org], sheet = sheet)
[ドキュメント]
def insert_rows(self, irow):
"""
指定された行の前に新しい行を挿入します。
:param irow: int: 行を挿入する位置のインデックス (1-based)。
"""
self.ws.insert_rows(irow)
[ドキュメント]
def insert_cols(self, icol, sheet = None, label = None,
check_exist = False, return_if_exist = False,
column_org = 1, row_org = 1,
font = None, alignment = None, fill = None, border = None,
width = None, restore_width = True, restore_hidden = True):
"""
指定された列の前に新しい列を挿入し、必要に応じて書式を適用します。
詳細説明:
`check_exist`がTrueの場合、`label`が既存の列に存在するか確認し、存在すれば挿入せずにその列のインデックスを返します。
既存の列の書式をコピーしたり、新しい書式を適用したりすることができます。
列幅や非表示状態を挿入前後に保存/復元するオプションもあります。
:param icol: int: 列を挿入する位置のインデックス (1-based)。
:param sheet: openpyxl.worksheet.worksheet.Worksheet, optional: ワークシートオブジェクト。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:param label: str, optional: 挿入する列のヘッダーラベル。`check_exist`がTrueの場合に使用。デフォルトはNone。
:param check_exist: bool, optional: 挿入前に`label`が既存列に存在するか確認するかどうか。デフォルトはFalse。
:param return_if_exist: bool, optional: `check_exist`がTrueの場合、存在すればその列のインデックスを返すか、Falseなら挿入処理を続行するか。デフォルトはFalse。
:param column_org: int, optional: 列幅/非表示状態の保存・復元を開始する列。デフォルトは1。
:param row_org: int, optional: ラベルを取得する行。デフォルトは1。
:param font: openpyxl.styles.fonts.Font or int or None, optional: 設定するフォントオブジェクト、または書式をコピーする元列のインデックス。デフォルトはNone。
:param alignment: openpyxl.styles.alignment.Alignment or int or None, optional: 設定する配置オブジェクト、または書式をコピーする元列のインデックス。デフォルトはNone。
:param fill: openpyxl.styles.fills.PatternFill or str or int or None, optional: 設定する塗りつぶしオブジェクト、色コード文字列、または書式をコピーする元列のインデックス。デフォルトはNone。
:param border: openpyxl.styles.borders.Border or int or None, optional: 設定する罫線オブジェクト、または書式をコピーする元列のインデックス。デフォルトはNone。
:param width: float, optional: 挿入する列の幅。デフォルトはNone。
:param restore_width: bool, optional: 挿入前後の列幅を復元するかどうか。デフォルトはTrue。
:param restore_hidden: bool, optional: 挿入前後の列の表示/非表示状態を復元するかどうか。デフォルトはTrue。
:returns: int: 挿入された(または既存の)列のインデックス (1-based)。
"""
if sheet is None:
sheet = self.ws
if restore_width:
self.store_column_widths(sheet = sheet, column_org = column_org, row_org = row_org)
if restore_hidden:
self.store_column_hidden(sheet = sheet, column_org = column_org, row_org = row_org)
idx = None
if check_exist and label is not None:
idx = self.get_icolumn_from_label(label, sheet = sheet, column_org = column_org, row_org = row_org)
if idx is None:
sheet.insert_cols(icol)
sheet.cell(row = row_org, column = icol).value = label
idx = icol # 挿入された新しい列のインデックス
elif return_if_exist:
if restore_width:
self.restore_column_widths(sheet = sheet,
column_org = column_org, row_org = row_org, by_label = True)
if restore_hidden:
self.restore_column_hidden(sheet = sheet,
column_org = column_org, row_org = row_org, by_label = True)
return idx
elif label is not None and not check_exist: # check_existがFalseでもlabelがあれば一旦挿入して設定
sheet.insert_cols(icol)
sheet.cell(row = row_org, column = icol).value = label
idx = icol
elif label is None: # labelが無くてもinsert_colsが呼ばれた場合はicolがidxとなる
sheet.insert_cols(icol)
idx = icol
if idx is None: # ここに到達することは通常無いが、念のため
idx = icol
if width is not None:
self.set_column_width(idx, width = width, sheet = sheet)
max_row = sheet.max_row
if font is not None:
if type(font) is int:
isrc = font
if font >= icol and idx >= icol: # 挿入位置が元列の後にあればインデックスを調整
isrc = font + 1
elif font >= icol and idx < icol: # 挿入位置が元列の前にあればそのまま
isrc = font
for irow in range(row_org, max_row+1):
fo = sheet.cell(row = irow, column = isrc).font.copy()
sheet.cell(row = irow, column = idx).font = fo
else:
for irow in range(row_org, max_row+1):
sheet.cell(row = irow, column = idx).font = font
if alignment is not None:
if type(alignment) is int:
isrc = alignment
if alignment >= icol and idx >= icol:
isrc = alignment + 1
elif alignment >= icol and idx < icol:
isrc = alignment
for irow in range(row_org, max_row+1):
al = sheet.cell(row = irow, column = isrc).alignment.copy()
sheet.cell(row = irow, column = idx).alignment = al
else:
for irow in range(row_org, max_row+1):
sheet.cell(row = irow, column = idx).alignment = alignment
if fill is not None:
if type(fill) is int:
isrc = fill
if fill >= icol and idx >= icol:
isrc = fill + 1
elif fill >= icol and idx < icol:
isrc = fill
for irow in range(row_org, max_row+1):
fi = sheet.cell(row = irow, column = isrc).fill.copy()
sheet.cell(row = irow, column = idx).fill = fi
elif type(fill) is str:
for irow in range(row_org, max_row+1):
self.set_cell_fill(irow, idx, sheet = sheet, fgcolor = fill, bgcolor = fill, pattern = 'solid')
else:
for irow in range(row_org, max_row+1):
sheet.cell(row = irow, column = idx).fill = fill
if border is not None:
if type(border) is int:
isrc = border
if border >= icol and idx >= icol:
isrc = border + 1
elif border >= icol and idx < icol:
isrc = border
for irow in range(row_org, max_row+1):
bo = sheet.cell(row = irow, column = isrc).border.copy()
sheet.cell(row = irow, column = idx).border = bo
else:
for irow in range(row_org, max_row+1):
sheet.cell(row = irow, column = idx).border = border
if restore_width:
self.restore_column_widths(sheet = sheet,
column_org = column_org, row_org = row_org, by_label = True)
if restore_hidden:
self.restore_column_hidden(sheet = sheet,
column_org = column_org, row_org = row_org, by_label = True)
return idx
[ドキュメント]
def delete_rows(self, irow, irow2 = None):
"""
指定された範囲の行を削除します。
:param irow: int: 削除を開始する行のインデックス (1-based)。
:param irow2: int, optional: 削除する行の数。Noneの場合、`irow`から1行のみ削除。デフォルトはNone。
"""
if irow2 is None:
self.ws.delete_rows(irow)
else:
self.ws.delete_rows(irow, amount = irow2 - irow + 1) # amountは削除する行数なので計算
[ドキュメント]
def delete_cols(self, idx, amount = None):
"""
指定された列を削除します。
:param idx: int: 削除を開始する列のインデックス (1-based)。
:param amount: int, optional: 削除する列の数。Noneの場合、`idx`から1列のみ削除。デフォルトはNone。
"""
if amount is None:
self.ws.delete_cols(idx)
else:
self.ws.delete_cols(idx, amount = amount)
[ドキュメント]
def get_row(self, irow, ws = None):
"""
指定された行のセルオブジェクトのタプルを取得し、現在の行/列のカーソルを更新します (0-basedインデックス)。
:param irow: int: 取得する行のインデックス (0-based)。
:param ws: openpyxl.worksheet.worksheet.Worksheet, optional: ワークシートオブジェクト。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:returns: tuple: 指定された行のセルオブジェクトのタプル。
"""
ws = self.get_sheet(ws)
self.cur_row = irow + 1
self.cur_col = 0
return ws[irow+1]
[ドキュメント]
def copy_worksheet2(self, ws_source = None, title = None):
"""
別のワークブックから現在のワークブックにシートをコピーします(手動コピー方式)。
詳細説明:
OpenPyXLの`copy_worksheet`が同じワークブック内でのコピーに限定されるため、
異なるワークブックからシートをコピーする際に各セルを個別にコピーします。
このメソッドは内部的に`copy_worksheet`が失敗した場合に呼び出されることを想定しています。
:param ws_source: openpyxl.worksheet.worksheet.Worksheet, optional: コピー元のワークシートオブジェクト。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:param title: str, optional: 新しいシートのタイトル。Noneの場合、ソースシートのタイトルを使用。デフォルトはNone。
:returns: openpyxl.worksheet.worksheet.Worksheet: コピーされた新しいワークシートオブジェクト。
"""
for icol in range(ws_source.max_column):
icol += 1
for irow in range(ws_source.max_row):
irow += 1
self.copy_cell_format(ws_source = ws_source, irow_source = irow, icol_source = icol,
ws_target = self.ws, irow_target = irow, icol_target = icol,
format = 'value|fill|font|border')
self.ws.title = ws_source.title if title is None else title
return self.ws
[ドキュメント]
def copy_worksheet(self, ws_source = None, title = None):
"""
ワークシートをコピーします。同じワークブック内であればOpenPyXLの機能を使用し、
失敗した場合は手動コピー(`copy_worksheet2`)にフォールバックします。
:param ws_source: openpyxl.worksheet.worksheet.Worksheet, optional: コピー元のワークシートオブジェクト。Noneの場合、現在のワークシートを使用。デフォルトはNone。
:param title: str, optional: 新しいシートのタイトル。Noneの場合、ソースシートのタイトルを使用。デフォルトはNone。
:returns: openpyxl.worksheet.worksheet.Worksheet: コピーされた新しいワークシートオブジェクト。
"""
prev_ws = self.ws
ws_source = self.get_sheet(ws_source)
try:
ws_target = self.wb.copy_worksheet(ws_source)
except:
return self.copy_worksheet2(ws_source = ws_source, title = title)
if title is not None:
ws_target.title = title
else:
ws_target.title = ws_source.title
self.ws = prev_ws
return ws_target
[ドキュメント]
def append(self, datas):
"""
ワークシートに新しい行のデータを追加します。
:param datas: list or tuple: ワークシートに追加するデータのリストまたはタプル。
"""
self.ws.append(datas)
self.cur_row = self.max_row() + 1
self.cur_col = 0
[ドキュメント]
def Append(self, datas):
"""
`append`メソッドのエイリアスです。
:param datas: list or tuple: ワークシートに追加するデータのリストまたはタプル。
"""
self.append(datas)
[ドキュメント]
def save(self, path = None, workbook = None):
"""
現在のワークブックを指定されたパスに保存します。
:param path: str, optional: 保存先のファイルパス。Noneの場合、インスタンスの`path`を使用。デフォルトはNone。
:param workbook: openpyxl.workbook.workbook.Workbook, optional: 保存するワークブックオブジェクト。Noneの場合、インスタンスの`wb`を使用。デフォルトはNone。
:returns: bool or None: 保存が成功した場合はTrue、失敗した場合はNone。
"""
if path is None:
path = self.path
if workbook is None:
workbook = self.wb
self.SetPath(path, mode = 'w')
try:
return workbook.save(path)
except Exception as e:
print(f"\nError in tkexcel.save(): Could not write to [{path}]")
print(f" Check if the file is opend, or check write permission: {e}")
return None
[ドキュメント]
def write(self, irow, icol, val):
"""
指定されたセルの値と、オプションで書式(数値形式、水平位置合わせ)を設定します (0-basedインデックス)。
詳細説明:
`val`が辞書の場合、`"val"`, `"number_format"`, `"horizontal_allignment"`
のキーを使用して、値と書式を設定できます。
:param irow: int: 設定するセルの行インデックス (0-based)。
:param icol: int: 設定するセルの列インデックス (0-based)。
:param val: any or dict: 設定する値、または値と書式設定を含む辞書。
"""
if type(val) is dict:
self.ws.cell(irow + 1, icol + 1).value = val["val"]
if val.get("number_format", None) is not None:
self.ws.cell(irow + 1, icol + 1).number_format = val["number_format"]
if val.get("horizontal_allignment", None) is not None:
if val["horizontal_allignment"] == '<':
self.ws.cell(irow + 1, icol + 1).alignment = Alignment(horizontal = "left") # leftContinuousではなくleftが正しい可能性
elif val["horizontal_allignment"] == '^':
self.ws.cell(irow + 1, icol + 1).alignment = Alignment(horizontal = "center") # centerContinuousではなくcenterが正しい可能性
elif val["horizontal_allignment"] == '>':
self.ws.cell(irow + 1, icol + 1).alignment = Alignment(horizontal = "right") # rightContinuousではなくrightが正しい可能性
else:
self.ws.cell(irow + 1, icol + 1).value = val
[ドキュメント]
def Write(self, irow, icol, val):
"""
`write`メソッドのエイリアスです。
:param irow: int: 設定するセルの行インデックス (0-based)。
:param icol: int: 設定するセルの列インデックス (0-based)。
:param val: any or dict: 設定する値、または値と書式設定を含む辞書。
"""
self.write(irow, icol, val)
[ドキュメント]
def print(self, *vals, end = '\n'):
"""
現在のカーソル位置からワークシートに値を書き込みます。
詳細説明:
`vals`がリストまたはタプルの場合、その要素を現在の行に順次書き込みます。
それ以外の場合は、各`vals`要素を現在の行に順次書き込みます。
`end`が`\n`の場合、次の行にカーソルを移動します。
:param vals: any: ワークシートに書き込む値。複数の引数を取ります。
:param end: str, optional: 各`print`呼び出しの後にカーソルを移動する方法を指定します。`\n`は次の行へ、それ以外は現在の行の次の列へ。デフォルトは`\n`。
"""
if type(vals[0]) is list or type(vals[0]) is tuple:
for v in vals[0]:
self.Write(self.cur_row, self.cur_col, v)
self.cur_col += 1
else:
for i in range(len(vals)):
self.Write(self.cur_row, self.cur_col, vals[i])
self.cur_col += 1
if end == '\n':
self.cur_row += 1
self.cur_col = 0
# print("r=", self.cur_row, self.cur_col)
else:
pass
# print("r=", self.cur_row, self.cur_col)
[ドキュメント]
def Print(self, *vals, end = '\n'):
"""
`print`メソッドのエイリアスです。
:param vals: any: ワークシートに書き込む値。複数の引数を取ります。
:param end: str, optional: 各`print`呼び出しの後にカーソルを移動する方法を指定します。デフォルトは`\n`。
"""
self.print(*vals, end)
[ドキュメント]
def load_workbook(self, path = None):
"""
指定されたパスからワークブックをロードし、現在のワークブックとして設定します。
:param path: str, optional: ロードするExcelファイルのパス。Noneの場合、インスタンスの`path`を使用。デフォルトはNone。
:returns: openpyxl.workbook.workbook.Workbook: ロードされたワークブックオブジェクト。
"""
self.SetPath(path = path, mode = 'w')
# UserWarning: Data Validation extension is not supported and will be removed がでるのでWarning抑制
openpyxl.reader.excel.warnings.simplefilter('ignore')
book = openpyxl.load_workbook(self.path)
self.wb = book
return book
[ドキュメント]
def sheetnames(self, path = None):
"""
指定されたExcelファイルのシート名のリストを取得します。
:param path: str, optional: Excelファイルのパス。Noneの場合、インスタンスの`path`を使用。デフォルトはNone。
:returns: list: シート名のリスト。
"""
# UserWarning: Data Validation extension is not supported and will be removed がでるのでWarning抑制
openpyxl.reader.excel.warnings.simplefilter('ignore')
book = self.load_workbook(path)
return book.sheetnames
[ドキュメント]
def read_sheet(self, path = None, sheet_name = None, mode = 'r', data_only = True,
password = None, allow_no_password = False,
irow_origin = 1, icol_origin = 1):
"""
指定されたシートのセルオブジェクトの二次元リストを読み込みます。
詳細説明:
パスワード保護されたファイルや、特定の行/列範囲からの読み込みに対応しています。
:param path: str, optional: 読み込むExcelファイルのパス。Noneの場合、インスタンスの`path`を使用。デフォルトはNone。
:param sheet_name: str or int, optional: 読み込むシートの名前またはインデックス (0-based)。Noneの場合、アクティブシートを読み込む。デフォルトはNone。
:param mode: str, optional: ファイルのオープンモード。デフォルトは'r'。
:param data_only: bool, optional: セルの値を数式ではなく表示されている値として読み込むかどうか。デフォルトはTrue。
:param password: str, optional: 暗号化ファイルを開く際のパスワード。デフォルトはNone。
:param allow_no_password: bool, optional: パスワード指定時に開けなかった場合、パスワードなしで再試行するかどうか。デフォルトはFalse。
:param irow_origin: int, optional: データを読み込む開始行のインデックス (1-based)。デフォルトは1。
:param icol_origin: int, optional: データを読み込む開始列のインデックス (1-based)。デフォルトは1。
:returns: list: セルオブジェクトの二次元リスト (例: `[[cell1, cell2], [cell3, cell4]]`)。
"""
self.SetPath(path = path, mode = mode)
if self.wb is None:
wb = self.open(self.path, mode = mode, data_only = data_only, password = password, allow_no_password = allow_no_password)
was_opened = False
else:
was_opened = True
if sheet_name is not None:
try:
idx = int(sheet_name)
ws = self.wb.worksheets[idx]
except:
ws = self.wb[sheet_name]
else:
if self.wb is None or self.wb.active is None: return None
ws = self.wb.active
max_row = ws.max_row
max_column = ws.max_column
cells = []
irow = 0
for _irow in range(irow_origin, max_row + 1):
# for row in ws:
# row = ws[_irow]
cells.append([])
icol = 0
for _icol in range(icol_origin, max_column + 1):
# for cell in row:
# cell = row[_icol]
cell = ws.cell(row = _irow, column = _icol)
cells[irow].append(cell)
# if _irow < 10:
# print(f" {_irow}行{_icol}列: [{cell.value}]")
icol += 1
irow += 1
if was_opened and self.mode != 'a': # インスタンスが元々開いていなかった場合のみ閉じる
self.Close()
self.ws = ws
return cells
[ドキュメント]
def Read_sheet(self, path = None, sheet_name = None, mode = 'r', data_only = True):
"""
`read_sheet`メソッドのエイリアスです。
:param path: str, optional: 読み込むExcelファイルのパス。デフォルトはNone。
:param sheet_name: str or int, optional: 読み込むシートの名前またはインデックス。デフォルトはNone。
:param mode: str, optional: ファイルのオープンモード。デフォルトは'r'。
:param data_only: bool, optional: セルの値を数式ではなく表示されている値として読み込むかどうか。デフォルトはTrue。
:returns: list: セルオブジェクトの二次元リスト。
"""
return self.read_sheet(path = path, sheet_name = sheet_name, mode = mode, data_only = data_only)
[ドキュメント]
def get_list_from_cells(self, cells = None, debug = False):
"""
セルオブジェクトの二次元リストからヘッダーとデータリスト(列ごとのリストのリスト)を取得します。
:param cells: list, optional: セルオブジェクトの二次元リスト。Noneの場合、処理は継続できない。デフォルトはNone。
:param debug: bool, optional: デバッグ情報を出力するかどうか。デフォルトはFalse。
:returns: tuple: (ヘッダーリスト, データリスト)。
"""
header = []
if cells is None or not cells:
return [], [] # cellsがNoneまたは空の場合のハンドリング
if not cells[0]:
return [], [] # ヘッダー行が空の場合
for cell in cells[0]:
header.append(cell.value)
nrow = len(cells)
ncol = len(cells[0])
if debug:
print("get_list_from_cells(): len(cells)=", len(cells))
print("get_list_from_cells(): len(cells[0])=", len(cells[0]))
datalist = make_matrix2(ncol, nrow - 1, defval = None) # ヘッダー行を除く
for irow in range(1, nrow): # ヘッダー行の次から開始
# row = self.get_row(irow+1)
# vals = []
for icol in range(ncol):
val = cells[irow][icol].value
# val = self.get_val(irow + 1, icol)
datalist[icol][irow-1] = val
# datalist.append(vals)
if debug:
print("Read_sheet_list: len(datalist)=", len(datalist))
print("Read_sheet_list: len(datalist[0])=", len(datalist[0]))
# print("datalist=", datalist)
# for i in range(nrow):
# for j in range(ncol):
# print(datalist[i][j], " ", end = '')
# print("")
return header, datalist
[ドキュメント]
def read_sheet_list(self, path= None, sheet_name = None, mode = 'r', data_only = True, password = None, allow_no_password = False,
irow_origin = 1, icol_origin = 1, debug = False):
"""
指定されたシートからヘッダーとデータリスト(列ごとのリストのリスト)を読み込みます。
:param path: str, optional: 読み込むExcelファイルのパス。デフォルトはNone。
:param sheet_name: str or int, optional: 読み込むシートの名前またはインデックス。デフォルトはNone。
:param mode: str, optional: ファイルのオープンモード。デフォルトは'r'。
:param data_only: bool, optional: セルの値を数式ではなく表示されている値として読み込むかどうか。デフォルトはTrue。
:param password: str, optional: 暗号化ファイルを開く際のパスワード。デフォルトはNone。
:param allow_no_password: bool, optional: パスワード指定時に開けなかった場合、パスワードなしで再試行するかどうか。デフォルトはFalse。
:param irow_origin: int, optional: ヘッダー行の開始インデックス (1-based)。デフォルトは1。
:param icol_origin: int, optional: ヘッダー列の開始インデックス (1-based)。デフォルトは1。
:param debug: bool, optional: デバッグ情報を出力するかどうか。デフォルトはFalse。
:returns: tuple: (ヘッダーリスト, データリスト)。
"""
cells = self.read_sheet(path = path, sheet_name = sheet_name, mode = mode, data_only = data_only,
password = password, allow_no_password = allow_no_password,
irow_origin = irow_origin, icol_origin = icol_origin)
header, datalist = self.get_list_from_cells(cells = cells, debug = debug)
return header, datalist
[ドキュメント]
def Read_sheet_list(self, path = None, sheet_name = None, mode = 'r', data_only = True, debug = False):
"""
`read_sheet_list`メソッドのエイリアスです。
:param path: str, optional: 読み込むExcelファイルのパス。デフォルトはNone。
:param sheet_name: str or int, optional: 読み込むシートの名前またはインデックス。デフォルトはNone。
:param mode: str, optional: ファイルのオープンモード。デフォルトは'r'。
:param data_only: bool, optional: セルの値を数式ではなく表示されている値として読み込むかどうか。デフォルトはTrue。
:param debug: bool, optional: デバッグ情報を出力するかどうか。デフォルトはFalse。
:returns: tuple: (ヘッダーリスト, データリスト)。
"""
return self.read_sheet_list(path = path, sheet_name = sheet_name, mode = mode, data_only = data_only, debug = debug)
[ドキュメント]
def get_dict_from_datalist(self, datalist = None, keys = None, irow_origin = 1, icol_origin = 1, allow_overlapped_keys = True):
"""
データリストとキーリストから辞書形式のデータを生成します。
詳細説明:
データリスト(列ごとのリストのリスト)と、それに対応するキー(ラベル)リストを組み合わせて、
キーをフィールド名とする辞書を作成します。各キーの値は、その列のデータリストとなります。
:param datalist: list, optional: 列ごとのデータのリストのリスト。Noneの場合、処理は継続できない。デフォルトはNone。
:param keys: list, optional: データリストの各列に対応するキー(ラベル)のリスト。Noneの場合、処理は継続できない。デフォルトはNone。
:param irow_origin: int, optional: データの読み込みを開始する行インデックス (1-based)。デフォルトは1。
:param icol_origin: int, optional: データの読み込みを開始する列インデックス (1-based)。デフォルトは1。
:param allow_overlapped_keys: bool, optional: キーが重複している場合、重複を許可するかどうか。Falseの場合、最初のキーのみが使用される。デフォルトはTrue。
:returns: dict: キーをフィールド名とする辞書。各キーの値は、対応するデータリスト。
"""
if datalist is None or not datalist or keys is None or not keys:
return {} # Noneまたは空のdatalistやkeysの場合のハンドリング
nrow = len(datalist[0]) # assumes datalist is [col1_data, col2_data, ...]
ncol = len(keys)
data_dict = {}
# initialize data_dict with empty lists
for key in keys:
if allow_overlapped_keys or key not in data_dict:
data_dict[key] = []
red_field = {} # 処理済みのキーを記録
for icol_idx in range(ncol): # 0-based index for datalist
field_name = keys[icol_idx]
if not allow_overlapped_keys and red_field.get(field_name, None) is not None:
continue
# irow_originとicol_originはget_dict_from_datalist内ではdatalistが既に読み込まれた後なので、
# datalist自体のインデックスに対するoffsetとして解釈する。
# しかし、現在のコードではirow_originとicol_originがdatalistの0-basedインデックスに
# そのまま適用されているため、混乱を避けるため、datalistは既に適切な範囲で
# 読み込まれているものと仮定し、irow_originとicol_originは無視するか、
# 0-basedでそのまま利用すると解釈する。
# 既存のコードは1-basedのirow_origin/icol_originをdatalistの0-basedインデックスに
# 変換して使っているので、それに合わせる。
# ここではdatalistが既にirow_originとicol_originを考慮して読み込まれていると仮定し、
# datalistの0-basedインデックスを使う。
# したがって、ループはirow_origin, icol_originを無視して0から始まる。
for irow_idx in range(nrow): # 0-based index for row in a column
data_dict[field_name].append(datalist[icol_idx][irow_idx])
red_field[field_name] = 1 # 処理済みとしてマーク
return data_dict
[ドキュメント]
def read_sheet_dict(self, path = None, sheet_name = None, allow_overlapped_keys = True,
irow_origin = 1, icol_origin = 1, debug = False):
"""
指定されたシートからヘッダー、データリスト、シート名を辞書形式で読み込みます。
:param path: str, optional: 読み込むExcelファイルのパス。デフォルトはNone。
:param sheet_name: str or int, optional: 読み込むシートの名前またはインデックス。デフォルトはNone。
:param allow_overlapped_keys: bool, optional: キーが重複している場合、重複を許可するかどうか。デフォルトはTrue。
:param irow_origin: int, optional: ヘッダー行の開始インデックス (1-based)。デフォルトは1。
:param icol_origin: int, optional: ヘッダー列の開始インデックス (1-based)。デフォルトは1。
:param debug: bool, optional: デバッグ情報を出力するかどうか。デフォルトはFalse。
:returns: tuple: (データ辞書, シート名リスト, キーリスト, データリスト)。
"""
keys, datalist = self.read_sheet_list(path = path, sheet_name = sheet_name,
irow_origin = irow_origin, icol_origin = icol_origin, debug = debug)
# sheetnames = self.sheetnames()
sheetnames = self.wb.sheetnames
if debug:
print("Read_sheet_dict: len(datalist)=", len(datalist))
if datalist:
print("Read_sheet_dict: len(datalist[0])=", len(datalist[0]))
# exit() # これはデバッグ用なのでコメントアウト
data_dict = self.get_dict_from_datalist(datalist = datalist, keys = keys, irow_origin = irow_origin, icol_origin = icol_origin,
allow_overlapped_keys = allow_overlapped_keys)
return data_dict, sheetnames, keys, datalist
[ドキュメント]
def Read_sheet_dict(self, path = None, sheet_name = None, irow_origin = 1, icol_origin = 1, allow_overlapped_keys = True, debug = False):
"""
`read_sheet_dict`メソッドのエイリアスです。
:param path: str, optional: 読み込むExcelファイルのパス。デフォルトはNone。
:param sheet_name: str or int, optional: 読み込むシートの名前またはインデックス。デフォルトはNone。
:param irow_origin: int, optional: ヘッダー行の開始インデックス (1-based)。デフォルトは1。
:param icol_origin: int, optional: ヘッダー列の開始インデックス (1-based)。デフォルトは1。
:param allow_overlapped_keys: bool, optional: キーが重複している場合、重複を許可するかどうか。デフォルトはTrue。
:param debug: bool, optional: デバッグ情報を出力するかどうか。デフォルトはFalse。
:returns: tuple: (データ辞書, シート名リスト, キーリスト, データリスト)。
"""
return self.read_sheet_dict(path = path, sheet_name = sheet_name, irow_origin = irow_origin, icol_origin = icol_origin,
allow_overlapped_keys = allow_overlapped_keys, debug = debug)
[ドキュメント]
def get_matrix_from_cells(self, cells = None):
"""
セルオブジェクトの二次元リストからヘッダーとデータの行列を取得します。
詳細説明:
このメソッドは現在実装が不完全です。`get_list_from_cells`と重複する可能性があります。
:param cells: list, optional: セルオブジェクトの二次元リスト。Noneの場合、`read_sheet`を呼び出す。デフォルトはNone。
:returns: tuple: (ヘッダーリスト, データ行列)
"""
if cells is None:
cells = self.read_sheet()
header = []
for cell in cells[0]:
header.append(cell.value)
# print("header=", header)
matrix = []
for irow in range(1, len(cells)):
row = self.get_row(irow) # get_rowは0-basedなので、cells[irow]と対応させるには調整が必要
vals = []
for cell in row:
vals.append(cell.value)
matrix.append(vals)
# print("matrix=", matrix)
return header, matrix # 実際には返り値が欠落していたので追加
[ドキュメント]
def read_sheet_matrix(self, sheet_name = None):
"""
指定されたシートからヘッダーとデータの行列を読み込みます。
:param sheet_name: str or int, optional: 読み込むシートの名前またはインデックス。デフォルトはNone。
:returns: tuple: (ヘッダーリスト, データ行列)。
"""
cells = self.read_sheet(sheet_name = sheet_name)
header, matrix = self.get_matrix_from_cells(cells = cells)
return header, matrix
[ドキュメント]
def Read_sheet_matrix(self, sheet_name = None):
"""
`read_sheet_matrix`メソッドのエイリアスです。
:param sheet_name: str or int, optional: 読み込むシートの名前またはインデックス。デフォルトはNone。
:returns: tuple: (ヘッダーリスト, データ行列)。
"""
return self.read_sheet_matrix(sheet_name = sheet_name)
[ドキュメント]
def read_minimum_matrix(self, close_fp = False, force_numeric = True):
"""
ワークシートから最小限のデータを読み込み、ラベルとデータリスト(列ごとのリスト)を取得します。
詳細説明:
1行目をラベルとして読み込み、それ以降の行をデータとして読み込みます。
`force_numeric`がTrueの場合、データは数値型 (`float`) に強制変換されます。
:param close_fp: bool, optional: 読み込み後にファイルを閉じるかどうか。デフォルトはFalse。
:param force_numeric: bool, optional: データを強制的に数値型に変換するかどうか。デフォルトはTrue。
:returns: tuple: (ラベルリスト, データリスト)。
"""
labels = []
row = self.get_row(0) # 0-basedで1行目を取得
for cell in row:
val = cell.value
if val is None:
break
if type(val) is str:
val.strip()
labels.append(val)
ncol = len(labels)
datalist = make_matrix1(ncol, type = 'list') # make_matrix1は列数と型を受け取る
irow = 1 # データ本体は2行目から (0-basedで1から)
while 1:
row = self.get_row(irow)
if row is None or row[0].value is None:
break
for icol in range(ncol):
try:
if force_numeric:
val = pfloat(row[icol].value)
else:
val = pfloat(row[icol].value, defval = row[icol].value) # floatに変換できない場合は元の値を保持
except:
val = None
datalist[icol].append(val)
irow += 1
if close_fp:
self.Close()
self.labelarray = labels
self.datalistarray = datalist
return labels, datalist
[ドキュメント]
def Read_minimum_matrix(self, close_fp = False, force_numeric = True):
"""
`read_minimum_matrix`メソッドのエイリアスです。
:param close_fp: bool, optional: 読み込み後にファイルを閉じるかどうか。デフォルトはFalse。
:param force_numeric: bool, optional: データを強制的に数値型に変換するかどうか。デフォルトはTrue。
:returns: tuple: (ラベルリスト, データリスト)。
"""
return self.read_minimum_matrix(close_fp = close_fp, force_numeric = force_numeric)
[ドキュメント]
def main():
"""
tkExcelクラスの基本的な使用例を示すメイン関数です。
ファイルの作成、書き込み、読み込み、変更、およびPrintメソッドの使用方法を実演します。
"""
path = "a.xlsx"
xls = tkExcel(path, 'w')
xls.Write(0, 0, 'a00')
xls.Write(1, 1, 'a11')
xls.Close()
xls = tkExcel(path, 'a')
xls.Write(0, 1, 'a01')
xls.Write(1, 1, 'a22')
xls.Close()
xls = tkExcel(path, 'r')
print("00=", xls.Get(0, 0))
print("01=", xls.Get(0, 1))
print("10=", xls.Get(1, 0))
print("11=", xls.Get(1, 1))
xls.Close()
path = "b.xlsx"
xls = tkExcel(path, 'w')
xls.print([0, 1, 2, 'aa'])
xls.print([3, 0, 1, 2, 'bb'])
xls.print([3, 'a&'], end = '')
xls.print([3, 5])
xls.Close()
if __name__ == "__main__":
main()