忍者ブログ
2024/04/20

pythonからExcelを操作するクラス

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

コメント

ただいまコメントを受けつけておりません。

2016/06/05

pythonからExcelを操作するクラス

MayaからExcelを操作しないといけない場面がありそうだったので、
pythonとclassの勉強の為にExcel操作クラスを作ってみました。
## -*- coding: utf-8 -*-
import site
import os
import re
site.addsitedir(os.path.dirname(os.path.abspath(__file__)) + r'\win32maya2015')
import win32com.client as com

# ------------------------------------------------
# 数字をアルファベットに変換 1  -> A  27 -> AA
def num2char(num):
    quotient, remainder = divmod(num, 26)
    chars = ''
    if quotient > 0:
        chars = chr(quotient + 64)
    if remainder > 0:
        chars = chars + chr(remainder + 64)
    return chars

# ------------------------------------------------
# アルファベットを数字に変換  A  -> 1  AA -> 27
def char2num(chars):
    num = 0
    for c in chars:
        num = num * 26 + (ord(c) - 64)
    return num


'''--------------------------------------------------------------------------------'''
class App(object):

    XLMAXIMIZED = -4137

    def __init__(self, visible=False):
        self.__excel_app = com.Dispatch("Excel.Application")
        #警告メッセージを表示しない。
        self.__excel_app.DisplayAlerts = False
        self.__excel_app.Visible = visible
        if(visible==True):
            self.__excel_app.WindowState = self.XLMAXIMIZED
            self.__excel_app.Visible = True

    # ------------------------------------------------
    # Excelブックを新規作成
    def add(self):
        book = self.__excel_app.Workbooks.Add()
        return Workbook(book)

    # ------------------------------------------------
    # エクセルファイルを開く
    def open(self, path, sheet=None, readonly=False):
        book = self.__excel_app.Workbooks.Open(path, 2, readonly)
        return Workbook(book)

    # ------------------------------------------------
    # エクセルプロセスを終了
    # (閉めにこれをやらないとプロセスに__excel_appがいっぱい残るので注意)
    def quit(self):
        self.__excel_app.Application.Quit()


'''--------------------------------------------------------------------------------'''
class Workbook(object):

    def __init__(self, book):
        self.__book = book

    # ------------------------------------------------
    # nameをアクセサ付きのプロパティとして登録
    sheets = property(doc='sheets property')

    @sheets.setter
    def sheets(self):
        pass

    @sheets.getter
    def sheets(self):
        return self._get_sheet_list()

    @sheets.deleter
    def sheets(self):
        pass

    # ------------------------------------------------
    # ブックを閉じる
    def close(self):
        self.__book.Close()
        self.__book = None

    # ------------------------------------------------
    # 上書き保存
    def save(self):
        self.__book.Save()

    # ------------------------------------------------
    # 名前をつけて保存
    def save_as(self, path):
        self.__book.SaveAs(path)

    # ------------------------------------------------
    # ブック内のシート一覧を取得する
    def _get_sheet_list(self):
        list = []
        for value in self.__book.Worksheets:
            list.append(Sheet(value))
        return list

    # ------------------------------------------------
    #  名前でシートを検索
    def find_sheet(self, name):
        if isinstance(name, str):
            name = name.decode('ShiftJIS')
        for value in self.sheets:
            if value.name == name:
                return value
        return None

    # ------------------------------------------------
    # シート削除(シート名、もしくは番号で指定)
    def delete_sheet(self, target):
        if type(target) is str:
            if not self.find_sheet(target):
                print u"[error]Designation of the sheet does not exist"
                return None
        if type(target) is int:
            if target < 1 or target > len(self.sheets):
                print u"[error]Designation of the sheet does not exist"
                return None
        self.__book.Worksheets(target).Delete()

    # ------------------------------------------------
    # シート作成
    def add_sheet(self, name):
        ws = self.__book.Worksheets.Add()
        ws.Name = name
        return Sheet(ws)


'''--------------------------------------------------------------------------------'''
class Sheet(object):

    XLDOWN = -4121
    XLUP = -4162
    XLTOLEFT = -4159
    XLTORIGHT = -4161

    def __init__(self, sheet):
        self.__sheet = sheet
        self._name = sheet.Name
        self._index = sheet.Index

    # ------------------------------------------------
    # nameをアクセサ付きのプロパティとして登録
    name = property(doc='name property')

    @name.setter
    def name(self, value):
        self._name = value
        self.__sheet.Name = value

    @name.getter
    def name(self):
        return self._name

    @name.deleter
    def name(self):
        pass

    # ------------------------------------------------
    # indexをアクセサ付きのプロパティとして登録
    index = property(doc='index property')

    @index.setter
    def index(self):
        pass

    @index.getter
    def index(self):
        return self._index

    @index.deleter
    def index(self):
        pass

    # ------------------------------------------------
    # セルを取得
    def get_cell(self, x, y):
        return Cell(self.__sheet, x, y)

    # ------------------------------------------------
    # 指定列(x)の最終行を取得する
    def max_row(self, x):
        if type(x) is str:
            x = char2num(x)
        return self.__sheet.Cells(65536, x).End(self.XLUP).Row

    # ------------------------------------------------
    # 指定行(y)の最終列を取得する
    def max_colum(self, y):
        return self.__sheet.Cells(y, 256).End(self.XLTOLEFT).Column

    # ------------------------------------------------
    # 行から指定の値のセルを取得する(横方向に調べる)
    def find_cell_from_rows(self, y, search_value, start_x=1):
        cells = []
        if isinstance(start_x, str):
            start_x = char2num(start_x)
        max_x = self.max_colum(y)
        for x in range(start_x, max_x):
            c = self.__comparison_detail(x, y, search_value)
            if c is not None:
                cells.append(c)
        return cells

    # ------------------------------------------------
    # 列から指定の値のセルを取得する(縦方向に調べる)
    def find_cell_from_columns(self, x, search_value, start_y=1):
        cells = []
        max_y = self.max_row(x)
        for y in range(start_y, max_y):
            c = self.__comparison_detail(x, y, search_value)
            if c is not None:
                cells.append(c)
        return cells

    # ------------------------------------------------
    # セルの内容比較
    def __comparison_detail(self, x, y, search_value):
        #cell_value = self.__sheet.Cells(y, x).Value
        cell = Cell(self.__sheet, x, y)
        cell_value = cell.text
        if isinstance(search_value, str):
            search_value = search_value.decode('ShiftJIS')

        if cell_value is None:
            return None

        f = re.search(search_value, cell_value)
        if f is None:
            return None

        return cell


'''--------------------------------------------------------------------------------'''
class Cell(object):

    def __init__(self, sheet, x, y):
        self.__sheet = sheet
        self.__cell = sheet.Cells(y, x)
        self.__range = sheet.Range(num2char(self.x) + str(self.y))

    # ------------------------------------------------
    # xをアクセサ付きのプロパティとして登録
    x = property(doc='x property')

    @x.setter
    def x(self):
        pass

    @x.getter
    def x(self):
        return self.__cell.Column

    @x.deleter
    def x(self):
        pass

    # ------------------------------------------------
    # yをアクセサ付きのプロパティとして登録
    y = property(doc='y property')

    @y.setter
    def y(self):
        pass

    @y.getter
    def y(self):
        return self.__cell.Row

    @y.deleter
    def y(self):
        pass

    # ------------------------------------------------
    # valueをアクセサ付きのプロパティとして登録
    value = property(doc='value property')

    @value.setter
    def value(self, value):
        self.__cell.Value = value

    @value.getter
    def value(self):
        return self.__cell.Value

    @value.deleter
    def value(self):
        pass

    # ------------------------------------------------
    # textをアクセサ付きのプロパティとして登録
    text = property(doc='text property')

    @text.setter
    def text(self):
        pass

    #表示そのままの文字列を取得するため、RangeオブジェクトにあるTextプロパティを戻す
    @text.getter
    def text(self):
        return self.__range.Text

    @text.deleter
    def text(self):
        pass

    # ------------------------------------------------
    # heightをアクセサ付きのプロパティとして登録
    height = property(doc='height property')

    @height.setter
    def height(self, value):
        self.__cell.RowHeight = value

    @height.getter
    def height(self):
        return self.__cell.RowHeight

    @height.deleter
    def height(self):
        pass

    # ------------------------------------------------
    # widthをアクセサ付きのプロパティとして登録
    width = property(doc='width property')

    @width.setter
    def width(self, value):
        self.__cell.ColumnWidth  = value

    @width.getter
    def width(self):
        return self.__cell.ColumnWidth

    @width.deleter
    def width(self):
        pass

    # ------------------------------------------------
    # colorをアクセサ付きのプロパティとして登録
    bg_color = property(doc='bg_color property')

    @bg_color.setter
    def bg_color(self, value):
        #とりあえずcolorインデックスでの指定のみ対応
        self.__cell.Interior.ColorIndex = value

    @bg_color.getter
    def bg_color(self):
        pass

    @bg_color.deleter
    def bg_color(self):
        pass

使い方としては以下のような感じですね。


## -*- coding: utf-8 -*-
#まずはインポート
import Excel

#------------------------------------------------------------------------------
#◆Appクラス

#これでエクセルを立ち上げる
excel = Excel.App()

#既存のエクセルファイルを開く→Workbookクラスが戻ってくる
book = excel.open(r"エクセルファイルのパス")

#新しいブックを開く場合はこう→Workbookクラスが戻ってくる
book = excel.add()

#すべてが終わったらエクセルを終了しましょう
excel.quit()

#------------------------------------------------------------------------------
#◆Workbookクラス

#これでシートのリストが取得できる
book.sheets

#ワークブック自身を閉じる
book.close()

#上書き保存
book.save()

#名前を付けて保存
book.save_as(r"セーブするパス")

#シートを名前で検索→存在していた場合シートクラスのインスタンスが戻される
sheet = book.find_sheet("Sheet1")

#シートを削除
book.delete_sheet(名前もしくは番号を指定)

#シートを作成→シートクラスのインスタンスが戻される
sheet = book.add_sheet()

#------------------------------------------------------------------------------
#◆Sheetクラス

#シート名
sheet.name

#シート名変更
sheet.name = "ほげほげ"

#シートのインデックス(番号)
sheet.index

#セルの取得→Cellクラスが戻る
#x : 列(横方向) A〜Zもしくは数字を指定
#y : 行(縦方向) 数字を指定
sheet.get_cell(x ,y)

#指定列(x)の最終行を取得する
sheet.max_row(x)

#指定行(y)の最終列を取得する
sheet.max_colum(y)

#行から指定の値のセルを取得する(横方向に調べる)→Cellクラスが戻る
#y : 縦方向 数字を指定
#search_value : 対象の値(調べるのは表示されている文字列。内部の式などは見ない)
#start_x : 何列目からサーチを開始するか。でふぉは1から
#現時点ではsearch_valueが含まれていれば検出対象となる。完全一致などのしては出来ない
sheet.find_cell_from_rows(y, search_value, start_x)

#列から指定の値のセルを取得する(縦方向に調べる)
#上記とほとんど一緒
sheet.find_cell_from_columns(x, search_value, start_y)

#------------------------------------------------------------------------------
#◆Cellクラス
#メソッドはなし。

#セルの列番号(横方向)
#読み取り専用
cell.x

#セルの行番号(縦方向)
#読み取り専用
cell.y

#セルの内容
#セルの内容によって戻ってくる型が違う。場合によってはちょっと扱いづらいかも?
#1と表示されているセルでも1.0として戻ってきたりする
cell.value

#セルのテキスト
#表示されている値を文字列として取得
#1と表示されているセルは1が戻ってくる。
#読み取り専用
cell.text

#セルの縦幅
cell.height

#セルの横幅
cell.width

#セルの背景色
#設定する場合は番号を指定。そのうち色直指定できるように拡張したい。
cell.bg_color


pywin32経由でのExcel操作のコードです。
冒頭の
pywin32をインポートする部分は書き換えてくださいね。

コードの中身は基本的にはこんなかんじ↓(流石わかりやすい!)

http://flame-blaze.net/archives/4887


ただし、今回の自分の目的の様にMayaから標準のpywin32モジュールを利用しようとしてもできません。。。
オー トデスク帝国によると「Maya2013以降のPythonは、通常配布されているPythonとは違うバージョンのVisual Studioでコンパルされているため外部モジュールが利用できません。 」とのことで、Mayaが使っている同じバージョンのVisual Studioで再コンパイルする必要があるそうです(メンドクサイ…)

https://knowledge.autodesk.com/ja/support/maya/troubleshooting/caas/sfdcarticles/sfdcarticles/kA230000000tsRX.html

少し前まで、ネットでMaya各バージョンのpywin32モジュールを配ってくれていた方がいたのですが、今はなにやらページが消えております…。
メンドクサイですが、それぞれご用意を…。 (;´A`)

pythonでのExcel操作モジュールは「python-excel」
openpyxl」があるのですが、
python-excelは書き込みと読込モジュールに分かれていて、かつ2007以降の*.xlsxは非対応ですし
もう一つのopenpyxlは逆に
*.xlsxにしか対応してない…(´・ω・`)
新旧のフォーマットが混在した状態の環境だとどちらも使えなさそう…ってことに。

書いたはいいけど、自分でもまだ使ってないので機能不足状態ですが
もし何かのお役にたてれば幸いですー
ヾ(*´∀`*)ノ゛

拍手[8回]

PR

コメント

プロフィール

HN:
モチオ
性別:
非公開
自己紹介:
テクニカルアーティスト(アニメーション出身)サポート対象Softimage/MotionBuilder/Mayaなど 言語 javascript / PHP / Python / VBAなど Webアプリも少しだけ作れる

P R