お世話になっているオープンソースソフトウェアの備忘録的ブログ

いつもお世話になっているオープンソースソフトウエアの備忘録です。ご利用に際しましては「このブログについて」をご覧くださいませ。

作成する ―「Inkscape」と「GIMP」で万年カレンダーを作る(その7)


新年は新しいカレンダーで迎えよう。と思っていましたが、あちこち修正したり書き直したりしていたら、はや2月じゃん・・・と言うわけで、一気に春っぽいカレンダーです。まだちょっと早い・・・
例によって画像はぱくたそ様よりお借りしております。ありがとうございます。

なお、ぱくたそ様の画像を使ってカレンダーを作る場合は、ぱくたそ様の利用規約をご確認の上ご利用ください。

www.pakutaso.com

こちらの記事も参照ください。 wata-ire-hanten-17.hatenablog.com

さて、いよいよ最終回です。以下の手順で「アナタだけのカレンダーができる!」 はず

1.国民の祝日CSVを準備する

内閣府のホームページ(国民の祝日について - 内閣府)から国民の祝日CSVをダウンロードします。
なお、こちらのファイルはS_JISになっていますので、ダウンロードしたものをそのまま使用してください。行儀の悪いエディタで開いたりすると勝手にUTFに変換されるのでご注意ください。

2.準備したファイルをフォルダにコピーする

設定ファイル(main01.py)に記載したフォルダを準備します。上の例ではUSBメモリーにcalender01と言うフォルダを作っています。
今までご紹介してきた画像ファイル、スクリプトのファイル2つ(date_prc01.py、main01.py)、国民の祝日csvファイルをまとめて準備したフォルダに入れます。
ちなみにスクリプトを実行すると「date_prc01.pyc」と言うファイルが増えますが、これはPythonが作るものですのでご安心を。

※※ ご注意(再掲) ※※
フォルダ名およびサブフォルダ名について、最初の一文字が「a」とか「t」とか「n」とかだとエラーになります。(制御文字の関係でしょう。たぶん)
おそらくコードの最初に呪文を書き込めば回避できると思います。知恵者の登場を待ちましょう!
待てない方は、諦めて「c(calenderとか)」を使いましょう・・・

3.スクリプトを実行する

「main01.py」をエディタで開き、設定値を修正します。修正が終わったら右クリックで「すべて選択」、再度右クリックで「コピー」します。(Notepad++の例です。他のエディタでは異なる場合があります)

次にGIMPを立ち上げ、「フィルター>Python-Fu>コンソール」を実行し、コンソール上で右クリックで「貼り付け」を実行します。その際、何か画像を表示していても構いません。
実行には多少時間がかかります。「応答なし」の表示が出ることも有りますが、しばらくお待ちください。(1分以上かかったらハングアップかも・・・)

4.実行結果を確認する

背景を透過pngで作った場合、表示が見にくいことがあると思います。この場合は「新しいレイヤーの追加」で「塗りつぶし色」=白のレイヤーを追加し、一番下(背景の下)に置くと見やすくなるかと思います。

このレイヤーを薄い色で塗りつぶすのも有りかと思います。インクジェットで印刷すると紙がフニャフニャになりそうだけど。


最後にPythonコンソールを一番上までスクロールして、エラーの赤文字が出ていないか確認します。


これで完成です。お疲れ様でした。

オマケ 複数月のカレンダーを作る場合

複数月カレンダーは、ほぼ腕力で作っています。
まず大まかな位置を決めます。その後、灰色の矩形のみをエクスポートし、これをベースとしてカレンダーを作ります。ここまで言うと何となく先が読めたと思います・・・


設定値の「年月」を4回変更して4か月分のカレンダーを作り、別々にpngファイルで出力します。

これらを「ファイル>レイヤーとして開く」で読み込み、人力で並べます。上の例ではバックに白背景(rect1.png)を置き、数字が目立つようにしています。

・・・将来的には複数月も自動でできるようにしたいと思っています・・・

最後になりましたが、今回使わせていただきましたモデルの皆様のご紹介です。いつもありがとうございます。

www.pakutaso.com www.pakutaso.com

設定値を調整する ―「Inkscape」と「GIMP」で万年カレンダーを作る(その6)


自分でレイアウトを決めることができますので、正方形のカレンダーだって可能です。秋っぽい写真に合わせて10月分のカレンダーです。
例によって画像はぱくたそ様よりお借りしております。ありがとうございます。

www.pakutaso.com

なお、ぱくたそ様の画像を使ってカレンダーを作る場合は、ぱくたそ様の利用規約をご確認の上ご利用ください。こちらの記事も参照ください。

wata-ire-hanten-17.hatenablog.com

さて、日にちなど各項目の配置等は「main01.py」の先頭部分、各設定値に書かれています。
正方形の台紙にする場合や、縦向きを横向きにする場合など、カレンダーのレイアウトを変更した場合は日にちや曜日などの配置が変わりますので、これらの設定値を変更する必要が有ります。

設定する際、原則は画面左下の座標の数字をそのまま入力します。がレイヤーのサイズは透明領域が含まれるため、多少調整が必要になるかと思います。

以下、実際の項目についてです。数字は全てピクセル単位です。
なお、エディタの画面のスクショを付けていますが、左端の行数はお使いの環境と必ずしも一致しているとは限りません。目安程度とお考えください。


年月:カレンダーにする年(西暦)と月を入力します。
年月の表示位置:年月の画像レイヤーの左上の座標を指定します。
年月の高さ:年月の画像レイヤーの高さを指定します。内部の処理としては、レイヤーの高さがこの値になるように一文字ずつ画像を拡大縮小しています。


曜日の表示位置x-y、高さ:年月の場合と同じです。


数字の配置範囲:日にちを表示する範囲を指定します。この範囲に入るように数字が拡大縮小されます。数字が一桁の場合は太くなりすぎないように前後にスペースを付加しています。
この座標は枠の一番左上のもの(今年の1月なら先月の31日の枠)で指定してください。


文字の配置位置:「元旦」とか「花子誕生日」とかの文字の配置位置です。
文字につきましては「中央ぞろえ(センタリング)」していますので、x軸の数値は枠の上で指定してください。y軸の数値は、フォントサイズ+α分だけ小さくします(上に上がります)。フォントサイズは後で設定します。



ワンポイントの配置範囲:日にちと同じ要領です。なおワンポイントのアイコンは画像ファイルが無ければ無視されますので、不要な場合はファイルを置かなければOKです。ただし、その際も配置範囲は削除しないでください。このままの数字を残しておけば良いかと思います。

枠のサイズ:この数値のみ、座標の数字ではなく大きさで指定します。背景をインポートした際に拡大縮小してなければ、Inkscapeで設定した数字と同じでOKです。


数字の属性(前後月の透過率):当月以外の日にち(今年の1月なら12月31日とか2月1日とか)の濃さを0から100で設定します。ゼロにすると透明に、100にすると当月と同じ濃さになります。


週開始曜日(月曜日=0、日曜日=6):一週間の開始を何曜日にするか設定します。0にすると月曜スタート、6にすると日曜スタートになります。1にすると火曜スタートになります(誰得ですが・・・)


文字の属性(フォント名、フォントサイズ):「元旦」とか「花子誕生日」とかの文字の属性です。フォント名をフルネームで記載すると、そのフォントが使われます。(上の例でコメントアウトしているのはIPAフォントを使った場合です)
フォントサイズはピクセル単位です。


年月の文字:「2024年1月」などの「年」と「月」の文字画像ファイルを指定します。使わない場合は(ご面倒ですが)透明画像を置いてください。


ファイルの置き場所:すべてのファイルをこの場所にまとめて置きます。完成したGIMPファイルもここに置きますので、実行時に書き込みのできるところを指定してください。
上の例は、USBメモリーにcalender01と言うフォルダを作って、その中に全部のファイルを入れて使った場合です。(システムにDドライブが無い場合)
※※ ご注意 ※※
フォルダ名およびサブフォルダ名について、最初の一文字が「a」とか「t」とか「n」とかだとエラーになります。(制御文字の関係でしょう。たぶん)
おそらくコードの最初に呪文を書き込めば回避できると思います。知恵者の登場を待ちましょう!
待てない方は、諦めて「c(calenderとか)」を使いましょう・・・

記念日:月日、文言、ワンポイントのpng画像:誕生日や記念日を設定します。
月、日、内容、ワンポイントのアイコン名(拡張子付き)の順で入力します。
「元旦にワンポイントを付けたい」と言う場合は、文言を空欄にして画像だけ記載すればOKです。
記念日を追加する場合は、先頭に半角スペースを4つ置いてコンマで区切ってください。面倒なら一行まるごとコピぺして書き換えれば早いと思います。
不要な場合は削除しても良いかと思いますが、最初の行だけは残しておいてくださいませ。
なお項目数が4の倍数でない場合(コンマが足りないとかの場合)はエラーとして全部無視されます。ご注意ください。

背景になる画像ファイル:元になる背景の画像ファイルです。jpegでもOKですが、後にGIMPで加工したい場合はpngの方が便利かと思います。


出力するxcfファイル名:年月の数字によってファイル名が変わります。2024年3月の場合は「Calender202403.xcf」となります。同じフォルダで12か月分作っても上書きの心配は無用と言う安心設計です。


お疲れ様でした。以上で設定は終わりです。
この下の部分はスクリプトの本体になりますので、改造リファクタリング大歓迎です。コメント欄で教えていただけますと大喜びします。

次回は、いよいよGIMPでカレンダーを作ります。

Python-Fuでコードを書く(date_prc01.py) ―「Inkscape」と「GIMP」で万年カレンダーを作る(その5)

コードその2です。名前は「date_prc01.py」固定です。UTF8で保存してください。
最近はウインドウズのメモ帳もUTF8を使っている時代ですので大丈夫だとは思いますが、一応ご確認ください。

では、大変ですが以下コピーしてお使いください。

# coding: utf-8
# Ver01-24011

import os, datetime

# カレンダーのスタート日を返す関数
def make_stat_date01(CONF01):
    NENGETU01 = CONF01["NENGETU01"]
    SYUU_START01 = CONF01["SYUU_START01"]
    d01 = datetime.date(NENGETU01[0],NENGETU01[1],1)
    #前の月のSYUU_START01曜日からスタート
    for i in range(7):
        if d01.weekday() == SYUU_START01 :#曜日の先頭
            break
        d01 = d01 - datetime.timedelta(days = 1)
    return(d01)

# 記念日をリストにする関数
def make_kinenbi01(CONF01):
    NENGETU01 = CONF01["NENGETU01"]
    KINENBI01 = list(CONF01["KINENBI01"])
    
    day_kinenbi01 = []
    kosu01 = len(KINENBI01)
    kosu014 = kosu01/4
    
    if (kosu01 - int(kosu014)*4) <> 0: # 一行4件
        print("KINENBI01の件数エラー")
        return
    else:
        for i in range(kosu014):
            day_kinenbi01.append(KINENBI01[i*4:(i+1)*4])
    return(day_kinenbi01)

# 祝日をリストにする関数
def make_syukujitu01(CONF01):
    DIR01 = CONF01["DIR01"]
    NENGETU01 = CONF01["NENGETU01"]
    day_syuku01 = []
    
    syuku_file01 = os.path.join(DIR01,"syukujitsu.csv")
    if os.path.isfile(syuku_file01) == True:
        def yield01(f0):
            for line01 in f0:
                line01 = unicode(line01, 'shift-jis').encode('utf-8')
                yield line01
        
        with open(syuku_file01) as f0:
            for line01 in yield01(f0):
                line_kou01 = line01.split(",")
                if line_kou01[0].count("/") < 1: # タイトル行(一行目)を無視する
                    pass 
                else:
                    day01 = line_kou01[0].split("/")
                    tmp_day01 = datetime.date(int(day01[0]), int(day01[1]), int(day01[2]))
                    
                    if tmp_day01 < datetime.date(NENGETU01[0],1,1): # 古いものは拾わない
                        pass
                    else:
                        linekou01 = (tmp_day01, line_kou01)
                        day_syuku01.append(linekou01)
    else:
        day_syuku01.append((datetime.date(1954,1,1), ["1954/1/1",""]))
    
    return(day_syuku01)

# カレンダーの6行7列の枠線を作る。
def make_waku01(CONF01):
    
    DIR01 = CONF01["DIR01"]
    syukujitu01 = list(make_syukujitu01(CONF01))
    kinenbi01 = list(make_kinenbi01(CONF01))
    
    syuu_col01 = 0 #週の何日目か?
    syuu_lin01 = 0 #月の何週目か?
    tougetu01 = True #当月か当月以外か?
    
    waku01 = []
    
    stat_date01 = make_stat_date01(CONF01)
    
    for i in range(6*7):
        suu_up_x01 = CONF01["SUUJI_POS01"][0] + CONF01["WAKU_SIZ01"][0] * syuu_col01 # 左上のX
        suu_up_y01 = CONF01["SUUJI_POS01"][1] + CONF01["WAKU_SIZ01"][1] * syuu_lin01 # 左上のY
        suu_dw_x01 = CONF01["SUUJI_POS01"][2] + CONF01["WAKU_SIZ01"][0] * syuu_col01 # 右下のX
        suu_dw_y01 = CONF01["SUUJI_POS01"][3] + CONF01["WAKU_SIZ01"][1] * syuu_lin01 # 右下のY
        
        moji_x01 = CONF01["MOJI_POS01"][0] + CONF01["WAKU_SIZ01"][0] * syuu_col01 # 左のX
        moji_y01 = CONF01["MOJI_POS01"][1] + CONF01["WAKU_SIZ01"][1] * syuu_lin01 # 左のY
        
        point_up_x01 = CONF01["POINT_POS01"][0] + CONF01["WAKU_SIZ01"][0] * syuu_col01 # 左上のX
        point_up_y01 = CONF01["POINT_POS01"][1] + CONF01["WAKU_SIZ01"][1] * syuu_lin01 # 左上のY
        point_dw_x01 = CONF01["POINT_POS01"][2] + CONF01["WAKU_SIZ01"][0] * syuu_col01 # 右下のX
        point_dw_y01 = CONF01["POINT_POS01"][3] + CONF01["WAKU_SIZ01"][1] * syuu_lin01 # 右下のY
        
        if syuu_col01 > 5: # 改行する場合
            syuu_col01 = 0
            syuu_lin01 += 1
        else:
            syuu_col01 += 1
        
        tmp01 = dict(
                i = i, 
                date01 = stat_date01 + datetime.timedelta(days = i),
                suu_up_x01 = suu_up_x01,
                suu_up_y01 = suu_up_y01,
                suu_dw_x01 = suu_dw_x01,
                suu_dw_y01 = suu_dw_y01,
                moji_x01 = moji_x01,
                moji_y01 = moji_y01,
                point_up_x01 = point_up_x01,
                point_up_y01 = point_up_y01,
                point_dw_x01 = point_dw_x01,
                point_dw_y01 = point_dw_y01,
            )
        
# 文字関係のデータ作成
        
        kinenbi_bun01, kinenbi_png01, syukujitu_bun01= "", "", ""
        day_red01, day_blue01 = False, False
        
        calender_day01 = tmp01["date01"].day
        calender_month01 = tmp01["date01"].month
        calender_youbi01 = tmp01["date01"].weekday()
        
        if calender_youbi01 == 5: # 土曜日
            day_blue01 = True
        if calender_youbi01 == 6: # 日曜日
            day_red01 = True
        
        if calender_month01 <> CONF01["NENGETU01"][1]: # 当月以外は薄くする
            opacity01 = CONF01["SUUJI_ZOKU01"][0]
            tougetu01 = False
        else:
            opacity01 = 100
            tougetu01 = True
            
        for k in range(len(kinenbi01)):
            if (calender_month01 == kinenbi01[k][0]) and (calender_day01 == kinenbi01[k][1]) and (tougetu01):
                
                kinenbi_bun01 = kinenbi01[k][2]
                kinenbi_png01 = kinenbi01[k][3]
                
                # 記念日のワンポイントのファイルが無かったら無視する
                file_chk01 = os.path.join(DIR01,kinenbi_png01)
                if os.path.isfile(file_chk01) == False:
                    kinenbi_png01 = ""
        
        for k in range(len(syukujitu01)):
            if (calender_month01 == syukujitu01[k][0].month) and (calender_day01 == syukujitu01[k][0].day):
                day_red01 = True
                if (tougetu01):
                    syukujitu_bun01 = syukujitu01[k][1][1]
                
        tmp02 = dict(
                calender_day01 = calender_day01,
                calender_month01 = calender_month01,
                calender_youbi01 = calender_youbi01,
                kinenbi_bun01 = kinenbi_bun01,
                kinenbi_png01 = kinenbi_png01,
                syukujitu_bun01 = syukujitu_bun01,
                opacity01 = opacity01,
                day_red01 = day_red01,
                day_blue01 = day_blue01,
            )
        
        tmp03 = tmp01.copy()
        tmp03.update(**tmp02)
        
        waku01.append(tmp03)
        
    return(waku01)

Python-Fuでコードを書く(main01.py) ―「Inkscape」と「GIMP」で万年カレンダーを作る(その4)

以下、コードです。名前は「main01.py」としていますが、こちらは「Python-Fu」のコンソールにコピペする方ですので、名前は何でも良いです。
では、大変ですが以下コピーしてお使いください。(何かうまいファイルの配布方法が有れば良いのですが・・・)

# Ver01-24011

import os, sys, datetime
# ver01 = sys.version
# print (ver01) #> 2.7.18 (default, Jul  1 2023, 20:56:56)  [GCC 13.1.0 64 bit (AMD64)]

# 年月
NENGETU01 = (2024,1)
# 年月の表示位置x-y
NENGETU_POS01 = (50,50)
# 年月の高さ
NENGETU_HEIGHT01 = 50

# 曜日の表示位置x-y、高さ
YOUB_POS01 = (80,580,30)

# 数字の配置範囲
SUUJI_POS01 = (63,640,100,670)
# 文字の配置位置
MOJI_POS01 = (48,682)
# ワンポイントの配置範囲
POINT_POS01 = (110,650,140,680)

# 枠のサイズ
WAKU_SIZ01 = (100,76.7)

# 数字の属性(前後月の透過率):前後月を非表示にするときはゼロに
SUUJI_ZOKU01 = (50,)
# 週開始曜日(月曜日=0、日曜日=6)
SYUU_START01 = 6
# 文字の属性(フォント名、フォントサイズ)
MOJI_ZOKU01 = ("Sans",10)
# MOJI_ZOKU01 = ("IPAexGothic",10)

# 年月の文字
MOJI_NEN01, MOJI_TUKI01 = "nen01.png", "tuki01.png"

# ファイルの置き場所
DIR01 = "D:\calender01"

# 記念日:月日、文言、ワンポイントのpng画像(だいたい正方形のpng)
KINENBI01 = (
    100,5,"この行は消さないでね!","",
    1,1,"","gantan.png",
    1,12,"結婚記念日","tan_b01.png",
    1,15,"花子誕生日","hana01.png",
    1,16,"かすみ誕生日","tan.png",
    2,25,"太郎誕生日","",
    )

# 背景になる画像ファイル
F_BACK01 = os.path.join(DIR01,"calender01.png")
# 出力するxcfファイル名:例:Calender202401.xcfとか
F_OUT01 = os.path.join(DIR01,"Calender%d%02d.xcf" % (NENGETU01[0], NENGETU01[1]))

################# 設定ここまで ####################

# 設定を辞書にまとめる
CONF01 = dict(NENGETU01 = NENGETU01,NENGETU_POS01 = NENGETU_POS01,
            NENGETU_HEIGHT01 = NENGETU_HEIGHT01, YOUB_POS01 = YOUB_POS01,
            SYUU_START01 = SYUU_START01, SUUJI_POS01 = SUUJI_POS01,
            SUUJI_ZOKU01 = SUUJI_ZOKU01, MOJI_POS01 = MOJI_POS01, WAKU_SIZ01 = WAKU_SIZ01,
            POINT_POS01 = POINT_POS01, MOJI_ZOKU01 = MOJI_ZOKU01,
            MOJI_NEN01 = MOJI_NEN01, MOJI_TUKI01 = MOJI_TUKI01,
            DIR01 = DIR01, KINENBI01 = KINENBI01)

sys.path.append(DIR01)
import date_prc01 # 日付関係の処理(別ファイル)

# ファイル関係の作成など。
img_back01 = pdb.gimp_file_load(F_BACK01, "")
tmp01 = pdb.gimp_image_get_active_drawable (img_back01)
pdb.gimp_xcf_save(0,img_back01, tmp01, F_OUT01, F_OUT01)
XCF_OUT01 = pdb.gimp_xcf_load(0,F_OUT01,F_OUT01) # 出力するxcfを読み込み

img_num01 = [] # 数字の画像を格納する配列
img_wed01 = [] # 曜日の画像を格納する配列

# 数字の画像(d01.pngとか)を配列に格納する関数
def kakunou01():
    for i in range (10):
        in_f_nam01 = "d%02d.png" % i
        img_tmp01 = os.path.join(DIR01,in_f_nam01)
        img_num01.append(pdb.gimp_file_load(img_tmp01, ""))
    for i in range (7):
        in_f_nam01 = "wd%02d.png" % i
        img_tmp01 = os.path.join(DIR01,in_f_nam01)
        img_wed01.append(pdb.gimp_file_load(img_tmp01, ""))

# 日付の数字のレイヤーを準備する関数
def lay_from_num01(i_num01, i_num02, month01, day01):

    i_width_num01 = pdb.gimp_image_width(i_num01)
    i_height_num01 = pdb.gimp_image_height(i_num01)
    
    i_width_num02 = pdb.gimp_image_width(i_num02)
    i_height_num02 = pdb.gimp_image_height(i_num02)
    
    if day01 >9:# 2つのを画像を1つにくっつけ名前を付ける
        
        lay01_act01 = pdb.gimp_layer_new_from_visible(i_num01,XCF_OUT01, "D%02d%02d" % (month01, day01))
        pdb.gimp_image_insert_layer(XCF_OUT01,lay01_act01,None,-1)
        
        lay01_act02 = pdb.gimp_layer_new_from_visible(i_num02,XCF_OUT01, "tmp01")
        pdb.gimp_image_insert_layer(XCF_OUT01,lay01_act02,None,-1)
        
        takasa01 = (i_height_num01 - i_height_num02)/2 # 高さ調整
        
        pdb.gimp_layer_set_offsets(lay01_act02, i_width_num01, takasa01)
        ret_layer01 = pdb.gimp_image_merge_down(XCF_OUT01, lay01_act02, 0)
        
    else:# 1つ目の画像はゼロなので無視。2桁分の空のレイヤーを作ってくっつけ名前を付ける
        
        lay01_act01 = pdb.gimp_layer_new(XCF_OUT01, i_width_num02*2, 
                      i_height_num02, 1, "D%02d%02d" % (month01, day01), 100, 0)
        #layer = pdb.gimp_layer_new(image, width, height, type, name, opacity, mode)
        
        pdb.gimp_image_insert_layer(XCF_OUT01,lay01_act01,None,-1)
        lay01_act02 = pdb.gimp_layer_new_from_visible(i_num02,XCF_OUT01, "tmp01")
        pdb.gimp_image_insert_layer(XCF_OUT01,lay01_act02,None,-1)
        takasa01 = 0 # 高さ調整(ダミー)
        
        pdb.gimp_layer_set_offsets(lay01_act02, i_width_num02/2, takasa01)
        ret_layer01 = pdb.gimp_image_merge_down(XCF_OUT01, lay01_act02, 0)
        
    return ret_layer01

# 文字の配置範囲(pos01)に押し込み日付を表示する関数
def lay_put_num01(CONF01):
    waku01 = list(date_prc01.make_waku01(CONF01))
    #枠に数字と文字を入れていく
    for l01 in waku01: # 1行づつ読んでl01に入れる(=一枠ずつ入れていく)
        
        month01 = l01["calender_month01"]
        day01 = l01["calender_day01"]
        opacity01 = l01["opacity01"]
        
        # 日にちの関係
        num_2keta = int(day01/10)
        num_1keta = day01 - num_2keta * 10
        
        layer_tmp01 = lay_from_num01(img_num01[num_2keta], img_num01[num_1keta] ,month01,day01)
        
        pdb.gimp_item_transform_scale(layer_tmp01, l01["suu_up_x01"], l01["suu_up_y01"],l01["suu_dw_x01"],l01["suu_dw_y01"])
        pdb.gimp_layer_set_opacity(layer_tmp01, opacity01)
        
        if l01["day_red01"] == True: #日曜日と祝日
            pdb.gimp_drawable_color_balance(layer_tmp01, 0, FALSE, 100, 0, 0) # 赤は1番目
        elif l01["day_blue01"] == True: #土曜日
            pdb.gimp_drawable_color_balance(layer_tmp01, 0, FALSE, 0, 0, 100) # 青は3番目
            
        # 文字の関係
        txt_tmp01 = l01["syukujitu_bun01"] + l01["kinenbi_bun01"]
        if len(txt_tmp01) > 0:
            text_layer01 = pdb.gimp_text_fontname(XCF_OUT01, None, l01["moji_x01"],l01["moji_y01"],
            txt_tmp01, 1, TRUE, MOJI_ZOKU01[1], 0, MOJI_ZOKU01[0])
            pdb.gimp_text_layer_resize(text_layer01, WAKU_SIZ01[0], MOJI_ZOKU01[1]*2)
            pdb.gimp_text_layer_set_justification(text_layer01, 2)
            
            if l01["kinenbi_png01"] <> "":
                img_tmp02 = os.path.join(DIR01,l01["kinenbi_png01"])
                layer_tmp02 = pdb.gimp_file_load_layer(XCF_OUT01, img_tmp02)
                pdb.gimp_image_insert_layer(XCF_OUT01,layer_tmp02,None,-1)
                pdb.gimp_item_transform_scale(layer_tmp02, l01["point_up_x01"], l01["point_up_y01"],l01["point_dw_x01"],l01["point_dw_y01"])

# 曜日の表示
def print_week01(CONF01):
    
    kd01 = CONF01["SYUU_START01"] # 週の初めの曜日
    
    for i in range(7):
        i_wek01 = img_wed01[kd01]
        i_width_wek01 = pdb.gimp_image_width(i_wek01)
        i_height_wek01 = pdb.gimp_image_height(i_wek01)
        
        lay01_act01 = pdb.gimp_layer_new_from_visible(i_wek01,XCF_OUT01, "wd%02d" % kd01)
        pdb.gimp_image_insert_layer(XCF_OUT01,lay01_act01,None,-1)
        
        rate01 = float(float(CONF01["YOUB_POS01"][2])/i_height_wek01)
        scale_x01, scale_y01 = rate01, rate01
        source_x01, source_y01 = i_width_wek01/2, i_height_wek01/2
        
        dest_x01 = CONF01["YOUB_POS01"][0] + CONF01["WAKU_SIZ01"][0]*i + i_width_wek01*rate01/2
        dest_y01 = CONF01["YOUB_POS01"][1] + i_height_wek01*rate01/2
        
        lay_wek_f010 = pdb.gimp_item_transform_2d(lay01_act01, source_x01, source_y01, scale_x01, scale_y01, 0, dest_x01, dest_y01)
        
        if kd01 >= 6:
            kd01 = 0
        else:
            kd01 += 1


# 年月の表示
def print_nen_getu01(CONF01):
    
    nen_tmp01 = CONF01["NENGETU01"][0]
    nen_keta4 = nen_tmp01//1000
    nen_keta3 = (nen_tmp01 - nen_keta4*1000)//100
    nen_keta2 = (nen_tmp01 - nen_keta4*1000 - nen_keta3*100)//10
    nen_keta1 = nen_tmp01 - nen_keta4*1000 - nen_keta3*100 - nen_keta2*10
    
    lay_nen01 = lay_from_num01(img_num01[nen_keta4], img_num01[nen_keta3], 99, 11)
    lay_nen02 = lay_from_num01(img_num01[nen_keta2], img_num01[nen_keta1], 99, 22)
    
    tuki_tmp01 = CONF01["NENGETU01"][1]
    tuki_keta2 = int(tuki_tmp01/10)
    tuki_keta1 = int(tuki_tmp01 - tuki_keta2*10)
    
    lay_tuki01 = lay_from_num01(img_num01[tuki_keta2], img_num01[tuki_keta1], 99, tuki_tmp01)
    
    tmp01 = os.path.join(CONF01["DIR01"], CONF01["MOJI_NEN01"]) # 年
    moji_nen01 = pdb.gimp_file_load_layer(XCF_OUT01, tmp01)
    pdb.gimp_image_insert_layer(XCF_OUT01,moji_nen01,None,-1)
    
    tmp01 = os.path.join(CONF01["DIR01"], CONF01["MOJI_TUKI01"]) # 月
    moji_tuki01 = pdb.gimp_file_load_layer(XCF_OUT01, tmp01)
    pdb.gimp_image_insert_layer(XCF_OUT01,moji_tuki01,None,-1)
    
    lay_nen_f01 = layer_comb01(lay_nen01, lay_nen02, 1)
    lay_nen_f01 = layer_comb01(lay_nen_f01, moji_nen01, 2)
    lay_nen_f01 = layer_comb01(lay_nen_f01, lay_tuki01, 1)
    lay_nen_f01 = layer_comb01(lay_nen_f01, moji_tuki01, 2)
    
    pdb.gimp_item_set_name(lay_nen_f01, "nengetu01")
    
    l_width_lay01 = pdb.gimp_drawable_width(lay_nen_f01)
    l_height_lay01 = pdb.gimp_drawable_height(lay_nen_f01)
    
    rate01 = float(float(CONF01["NENGETU_HEIGHT01"])/l_height_lay01)
    
    source_x01, source_y01 = l_width_lay01/2, l_height_lay01/2
    scale_x01, scale_y01 = rate01, rate01
    
    dest_x01 = CONF01["NENGETU_POS01"][0] + l_width_lay01*rate01/2
    dest_y01 = CONF01["NENGETU_POS01"][1] + l_height_lay01*rate01/2
    
    lay_nen_f010 = pdb.gimp_item_transform_2d(lay_nen_f01, source_x01, source_y01, scale_x01, scale_y01, 0, dest_x01, dest_y01)

# 二つのレイヤーを一つにまとめ、一番目のレイヤに吸収する。フラグが1の場合は単純にくっつける。
# フラグが1でないの場合は、2番目のレイヤーは縦横比率を保持したまま縮小拡大してくっつける。高さのサイズは一番目のレイヤーに合わせる。
def layer_comb01(arg_lay01, arg_lay02, flg01):
    l_width_lay01 = pdb.gimp_drawable_width(arg_lay01)
    l_height_lay01 = pdb.gimp_drawable_height(arg_lay01)
    l_width_lay02 = pdb.gimp_drawable_width(arg_lay02)
    l_height_lay02 = pdb.gimp_drawable_height(arg_lay02)
    takasa01 = (l_height_lay01 - l_height_lay02)/2
    
    if flg01 <> 1:
        #2番目のレイヤーを縦横比率を保存したまま拡大縮小する。
        ret_width_lay02 = l_width_lay02*l_height_lay01/l_height_lay02
        pdb.gimp_layer_scale(arg_lay02, ret_width_lay02, l_height_lay01, False)
        takasa01 = 0
    
    pdb.gimp_image_raise_item_to_top(XCF_OUT01, arg_lay01)
    pdb.gimp_image_raise_item_to_top(XCF_OUT01, arg_lay02)
    pdb.gimp_layer_set_offsets(arg_lay02, l_width_lay01, takasa01)
    
    ret_layer01 = pdb.gimp_image_merge_down(XCF_OUT01, arg_lay02, 0)
    
    return(ret_layer01)


def main01():
    kakunou01()
    lay_put_num01(CONF01)
    print_week01(CONF01)
    print_nen_getu01(CONF01)

main01()

disp01 = pdb.gimp_display_new(XCF_OUT01)
pass

Python-Fuでコードを書く(準備編) ―「Inkscape」と「GIMP」で万年カレンダーを作る(その3)

『「エンジニアなんだからFAXも直せるでしょ」と言われる』「ちくしょう転職だ!」と言う転職サイトの広告があちこちで出てましたが、これも「ぱくたそ」様の画像だったんですね。 FAXについては私も出先で似たような経験は有りますが、できればこんな美人のお姉さまに問い詰められたかった・・・
www.pakutaso.com

さて、実はPythonをまともに使うのは今回が初めてだったりしますので、Pythonの達人の皆様におかれましては「なんじゃこりゃあ!!」かと思いますが、かの有名な大賢人の言葉にもありますように「綺麗なコードは良いコードだ。走るコードはもっと良いコードだ。」と言う事で・・・(すみません今私が思い付いた言葉です。本当に誰か言っていたらゴメンナサイ)

なお、最初に色々書いていますが、中身はいいから取り敢えず実行してみたい!と言う方は、3章以降をご覧くださいませ。

1.関数や変数になぜ「01」が付いているの?

言い訳です。
Pythonに限らずプログラミング関係の貴重な情報をネットに上げてくださる先達の皆様には、いつも大変お世話になっております。
ただ、上げてくださるコードについて、初心者(=自分)は「どれが組み込み関数(orクラス、クラスメソッド、プロパティ、などなど)で、どれが独自の関数(orクラス、クラスメソッド、プロパティ、などなど)か?」が分からない。と言う問題が生じます。(自分だけ・・・じゃないよね?よね?)

そんなわけで今回は、自作の関数や変数には全て「01」が付いています。(for文のカウンターとか、明らかなものは付いていないのも有ります)
「リーダブルコード」では「01とか付けると、読む人が02を探してしまうので止めろ」と言われていますが、そんなわけで01が付いているからと言って02が有るわけではありませんのでご安心ください。(たまーに02も有りますが)

2.メインの中で、なぜグローバル定数・変数をわざわざ引数にしているの?

はい。これも言い訳です。
メインは設定値のみにして、残りは全部外部ファイルにするつもりでした。
が、GIMPの独自プロシージャ?(pdb.gimp_file_load(img_tmp01, "")とか)を外部ファイルにすると「NameError: global name 'pdb' is not defined」と怒られてしまいました。パスは通っているし、おかしいなあ・・・と色々調べてみたのですが、私のアタマでは分からず諦めて放置したのでした・・・

で、最後にブログの見直しをしている時に、参考にさせていただいたサイトを見直している中で、こちらのサイト(GIMPで画像を一括でレベル補正(Python-Fu) #Python - Qiita)からリンクされているこちらのサイト(本の電子化を補助するためにGimpとpython-fuを使ってみた。 - Oillerの日記)で発見してしまいました。先頭で「from gimpfu import *」を追加すればよいという事を・・・

で、別ファイルに直そうか・・・とも思ったのですが、次期バージョンの万年カレンダーで実装しよう。と言う野望を胸に、みっともないですがそのままにしてあります。(わざわざもう一度修正するのがメンドイ。と言うのは図星です)

3.サンプルのコードって、どうやって実行すればいいの?

本来ならregister関数を使う所ですが、設定項目が多い+繰り返し使う+記念日リストを作ったりしているため「コンソール張り付け」と言う原始的な方法で実行します。(register関数を使うのがメンドイと言う(以下略))

例えば下のコードを実行したい場合です。

def hello01():
    print("Hello World!")

hello01()
pass

ブラウザ上でコードの最初から最後までドラッグして選択し、選択できたら右クリックでコピーします。(試しに上のコードでやってみてください)

GIMPで「フィルター>Python-Fu>コンソール」を実行します。

Pythonコンソール」画面上で右クリックし、「貼り付け」を実行します。

無事「Hello World!」が表示されました。
(どれが何なの?と言う話ですが、下から2行目の緑色のところです。はい)

なお、今回はブラウザ上でコピーしたものをそのままコンソール上で実行しましたが、実際は設定値を変更したうえで実行することになると思います。
その場合はブラウザ上でコピーしたものを一度エディタ上に張り付け、値を変更してから再度全部コピー、コンソールに張り付けるという手順になります。

では、次回以降コードがダラダラ出てきますので、「右クリックでコピーからの張り付け」を頑張ってください!

付録.エディタは何を使えばいい?

エディタは何でも良いのですが(何ならウインドウズのメモ帳でも)、自分は「Notepad++ 」を使っています。もちろんオープンソースです。

本家:Notepad++

英語に抵抗のある方はこちら(窓の杜
「Notepad++」オープンソースで開発されているプログラマー向けのテキストエディター - 窓の杜

なお、Pythonで「Notepad++ 」を使う場合、いくつか設定をしておいた方が使いやすくなります。

Pythonではインデントにタブとスペースを混ぜるとエラーになりますので、分かりやすいように「表示>制御文字の表示」で「半角スペースとタブを表示」にチェックを入れます。

また「設定>環境設定」で「言語メニュー」を選択し、右側の「タブ設定」でPythonを選択し、「スペースで置換」にチェックして、「タブ幅=4」にします。一応このインデントが公式の推奨みたいです。
参考:はじめに — pep8-ja 1.0 ドキュメント

謝辞.参考にさせていただいたサイト

最後になりましたが、今回の記事を書くのに参考にさせていただいたサイトの一部です。ありがとうございました。

スクリプト言語Python-Fu」関連

GIMPについては有名どころですので参考になるサイトは多いと思います。以下にいくつか上げさせていただきます。


GIMP (GNU 画像編集プログラム)
本家のドキュメントの日本語版。翻訳者の皆様に感謝です。
基本的な項目が網羅されています。この中の「Python-Fu」関連の記事が参考になると思います。


Windows版GIMP-2.10でPython-Fuを試す。 | 迷惑堂本舗
スクリプトの書き方を参考にさせていただきました。


GIMPで画像を一括でレベル補正(Python-Fu) #Python - Qiita
こちらのサイトの「関数の探し方」は必読です。
有用なサイトを紹介いただき助かりました。


GIMP / Python 自作プラグイン (ファイルをレイヤーとして読込) 解説 - 省型旧形国電の残影を求めて
Python-Fu」を使ってレイヤーを操作する方法を参考にさせていただきました。

Python関連

旬の言語だけあって検索するとサイトがゾロゾロ出てきます。が、最新のバージョン(Python3系)について書かれているサイトが多く、「Python-Fu」では使えない関数とかが出てくることが有りますのでご注意ください。


Python Documentation contents — Python 2.7.18 ドキュメント
本家のドキュメントです。「Python-Fu」のPythonのバージョンは「2.7.18」ですのでご注意ください。


とほほのPython入門 - とほほのWWW入門
有名な「とほほ」氏の入門サイトです。多少あっさり目の記述ですが(JavaScriptなんかと比べると・・・こらこら)分かりやすくまとまっています。ちょっと分からなくなった時に開くのに最適です。


note.nkmk.me控えめに言って神サイトです。むしろネ申サイトと言ってよろしいかと思います。
こちらの記事に何回助けていただいたことか・・・

その他、多くのサイトを参考にさせていただきました。ありがとうございました。

Inkscapeで部品を作る ー「Inkscape」と「GIMP」で万年カレンダーを作る(その2)

まずInkscape万年カレンダーの部品を作っていきます。
具体的には、全体のベースや数字、曜日、ワンポイントのアイコン、等々です。
なお、画像は例によって「ぱくたそ」様の写真をお借りしております。いつもお世話になります。
www.pakutaso.com

1.ベースとなる画面を作る

Inkscapeで画像や数字の入る部分を大まかに配置します。
数字(日にち)の入る部分の矩形は、後の配置の目安にしますので、サイズをスクショ等で記録しておきます。単位はピクセルにします。(四角の一番右のプルダウンメニューで変更可能)
この矩形はサイズを図るためのものですので、終わったら非表示にしておきます。(再試行するかもしれないので削除しない方がよろしいかと)

2.日付の枠を作る

上のサイズを元に、7曜日×6週間の枠線を作ります。
数字(日にち)の入る部分の矩形は「幅700px、高さ460px」でしたので、枠線の幅は700/7=100、高さは460/6=76.7とします。位置は上の矩形に合わせます。ついでにメモ欄的な横線を3本追加しグループ化しています。もちろん横線は任意ですが、矩形だけでもグループ化しておいた方が後々作業しやすいかと思います。

なお、今回はInkscapeの設定は、用紙サイズA4、DPIはデフォルトの96で行っています。使用するプリンタによるかと思いますが、印刷してみてジャギーが気になる様だったら、用紙サイズを大きくして(A3とか)、印刷する際に縮小した方が綺麗になると思います。用紙サイズを変更した場合は、上の数字は当然変わります。

グループ化した矩形(「レイヤーとオブジェクト」のg3)を選択し、6行7列のタイルクローンを作ります。タイルクローンの作り方は以下の記事をご参照ください。
wata-ire-hanten-17.hatenablog.com


「レイヤーとオブジェクト」で見ると、use1からuse42まで、42個のクローンができています。「日付の枠は不要だ!」など、いろいろ変更したい場合は、オリジナル(「レイヤーとオブジェクト」のg3)を変更すれば自動的に42個とも変更されます。便利ですね!
なお、前回はオリジナルは離れたところに置きましたが、今回は再試行を考えて元の場所に置いてありますので、この部分(左上の枠)だけクローンとオリジナルが二重になって色が濃くなっています。このため、画像ファイルを作る際にはクローンを不可視にします。(useの一番小さい番号になると思います)

3.その他の必要な部品を作る

数字、曜日、ワンポイントのアイコンなどを作ります。これらの部品は配置される際に自動で拡大縮小されますので、そんなに大きさを気にする必要は有りませんが、あまりバラバラだと太さ等が不ぞろいになってしまうので、ある程度はそろえておいた方が良いかと思います。

数字、曜日の書き方につきましては、こちらの記事を参照ください。
wata-ire-hanten-17.hatenablog.com

ワンポイントのアイコンは、こちらの記事を参照ください。 wata-ire-hanten-17.hatenablog.com

4.部品を画像ファイルとして出力する

「ファイルーエクスポート」で各部品をpngの画像ファイルとして出力します。
背景は「ページ」を選択すると用紙サイズで出力できます。
数字、漢字、アイコン等は「選択範囲」で選択し出力します。

部品の名前は、数字は「d+数字.png」(d00.pngとか)、曜日は月曜日をゼロ、日曜日を6として「wd+曜日.png」(wd00.pngとか)とします。この二つは固定です。
その他は任意ですが、上の画面(=次章のサンプル)と同じ名前にしていただくとコードを変更する必要がありませんので便利です。

参考までに活字を切り出す場合について。
活字ごとに選択範囲にすると配置後の空間が多少不自然になってしまいます。なので、活字で文字や数字を書いた後に、矩形で切り出した方が良いかと思います。(矩形で切り出す方法は、こちらの記事の5章をご参照ください。「Inkscapeのクリップグループの凄さに感動したのでご紹介 - お世話になっているオープンソースソフトウェアの備忘録的ブログ」)
こうする事により数字の周りに任意のスペースを付けることができます。この辺はお好みで。
サンプルはフォント「Linux Libertine」を使った例です。

5.画像ファイルサンプル

画像ファイルとファイル名(画像の下)です。
自慢ではありませんが「ぶちゃいく(不細工)」ですので、参考程度にご覧ください。
なお右クリックの「名前を付けて保存」で保存していただくと、次回以降の作例の部品としても使えます。(一括ダウンロードできるようにしておけば良いのですが・・・良い方法が無くて・・・)

calender01
d00.png
d01.png
d02.png
d03.png
d04.png

d05.png
d06.png
d07.png
d08.png
d09.png
wd00.png
wd01.png
wd02.png
wd03.png
wd04.png
wd05.png
wd06.png
nen01.png
tuki01.png
gantan.png
tan.png
tan_b01.png
hana01,png

次回はPython-Fuを使っていきます。

「Inkscape」と「GIMP」で万年カレンダーを作る(その1)

昨年の話ですが、愛読しているサイトでこのような記事を見つけました。
Excelで「万年カレンダー」を自作! 日付処理の極意を駆使したエクセル使いの技を伝授 - 残業を減らす!Officeテクニック - 窓の杜

そこで「Inkscape」と「GIMP」で同じようなことができないかやってみました。なぜLibreOffice Calcでないのか?謎です。

 ※ ご注意 ※
今回もカレンダーのサンプル画像には「ぱくたそ」様の写真をお借りしておりますが、「ぱくたそ」様の写真は著作権フリーではありません。そのため、当ホームページのカレンダーの画像につきましても、個人で楽しむ範囲(個人的に、または家庭内その他これに準ずる限られた範囲内)でのご利用でお願いいたします。詳しくは「ぱくたそ」様の利用規約をご確認ください。 ぱくたそ - 会員登録不要、無料の写真素材・AI画像素材

今回の万年カレンダーの特長

1.全体のレイアウトが自由


Inkscapeで背景を作りますので、好きな画像を張り付けることができます。上の例は以前作成した画像を使用したものです。
wata-ire-hanten-17.hatenablog.com 縦向きでも横向きでも作成可能です。

2.カレンダーの日にちレイアウトの変更が自動で自由


設定値をPython-Fuで読み込んでカレンダーの日にちの位置、サイズを設定していますので、設定値を変更する事でレイアウトを簡単に変更できます。

3.国民の祝日を自動取り込み、また個人的な記念日の設定が自由

内閣府のホームページ(国民の祝日について - 内閣府)から国民の祝日CSVをダウンロードしてくれば、国民の祝日の設定は自動でできます。また個人的な記念日はいくつでも設定でき、ワンポイントのアイコンも設定できます。

4.日にち、曜日などが画像なので、文字形が自由


全て手書きの数字、漢字でカレンダーを作ることも可能です。(ただし、作者の美的センスがモロに出ます・・・)
既存のフォントを切り抜いて胡麻化すこともできますが、既存のフォントを使用する場合は使用許諾にご注意。(ちなみに今回は、キレイな字のサンプルは「Linux Libertine」を使用しています)

5.日にち、曜日などが別々のレイヤーなので、ピンポイントで変更が自由


最終的に完成するGIMPでは、各々の全ての日付、文言、曜日、年月が別々のレイヤーになりますので、日にち単位で自由にレイヤー操作(移動、拡大、縮小、フィルターの適用、などなど)ができます。上の例のように、「こどもの日の文字レイヤー」を選択して、フォントサイズを変更し色を変える。みたいなことを簡単にできます。

6.オープンソースなので改変が自由

Python-Fuを使っていますので、自由に改変していただけます。


以上、今回はこんなところで。
次回から具体的な使用方法のご紹介です。

最後になりましたが、今回のサンプルで使わせていただいたモデルの皆様のご紹介です。ありがとうございました。

www.pakutaso.com

www.pakutaso.com

www.pakutaso.com