※例によって Linux Mint 19.2 + Python2.7 です
画面下の方に見えるのは以前作ったカラーを決めるGUI群。中央に見えるのは解りづらいですが、ディレクトリ検索。上に見えるのは Blender をパク…リスペクトしたファイル作成GUI。
しかして、「CREATE」ボタンを押すと…
指定した色の15x15ドットの24ビットカラーの四角いBMPファイルができるだけ。
しかもエラー処理も書いてないから、上書き禁止フォルダに書き込もうとすると落ちる等バグは放置してます。
何かの役に立つアプリを作る気は毛頭なくて、
Python でバイナリファイルを作る処理を組む
Bitmap ファイルの扱い方を思い出す
という目標と後述する目的の足掛かりとして作成しました。
コード
## make bmp ##
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.slider import Slider
from kivy.uix.floatlayout import FloatLayout
import os
from os.path import join,isdir
from kivy.uix.filechooser import FileChooserIconView
from kivy.uix.filechooser import FileChooserListView
### Input Directory
#class DirectoryChooser(FileChooserListView):
class DirectoryChooser(FileChooserIconView):
def is_dir(self,directory,filename):
self.parent.parent.t1.text = directory
return isdir(join(directory,filename))
class BoxSelectFile(BoxLayout):
orientation='vertical'
def __init__(self,**kwargs):
super(BoxSelectFile,self).__init__(**kwargs)
self.dc =DirectoryChooser(size_hint_y=1)
self.dc.filters=[self.dc.is_dir]
self.add_widget(self.dc)
### Input FileName
class BtSave(Button):
def on_press(self):
self.parent.parent.MakeFile()
class BoxSaveFileName(BoxLayout):
orientation='horizontal'
def __init__(self,**kwargs):
super(BoxSaveFileName,self).__init__(**kwargs)
self.t1 = TextInput(text='',size_hint_x=5,multiline=False)
self.l1 = Label(text='.bmp',size_hint_x=2)
self.b1 = BtSave(text='CREATE',size_hint_x=3)
self.add_widget(self.t1)
self.add_widget(self.l1)
self.add_widget(self.b1)
### Input Color Value
class TxiColor(TextInput):
def on_text_validate(self):
value = int(self.text)
if value <= self.parent.max and value >= self.parent.min:
self.parent.SetValue(value)
self.parent.ReDraw()
class SlColor(Slider):
def on_touch_up(self,touch):
value = int(self.value)
self.parent.SetValue(value)
self.parent.ReDraw()
class BoxColor(BoxLayout):
def __init__(self,**kwargs):
super(BoxColor,self).__init__(**kwargs)
self.SetUp('',0,100)
def SetUp(self,text,min,max):
self.tName = text
self.min = min
self.max = max
self.value = min
self.clear_widgets()
self.orientation='horizontal'
self.l1 = Label(text=self.tName,size_hint_x=1)
self.s1 = SlColor(min=self.min,max=self.max,value=self.value,size_hint_x=7)
self.t1 = TxiColor(text=str(self.value),size_hint_x=2,multiline=False)
self.add_widget(self.l1)
self.add_widget(self.s1)
self.add_widget(self.t1)
def SetValue(self,value):
self.value = value
def ReDraw(self):
self.s1.value = self.value
self.t1.text = str(self.value)
### BMP File cotrol
class BMPFileControl():
def __init__(self,size_w,size_h):
# 24bit Color Only
self.header = bytearray(range(54))
self.padding = (4-(size_w*3)%4)%4
self.w = size_w
self.h = size_h
imagesize = int(size_w*3 + self.padding)*size_h
filesize = int(54 + imagesize)
# FILE HEADER
# 'BM'
self.header[0] = 'B'
self.header[1] = 'M'
# FileSize
self.header[2] = filesize & 0xff
self.header[3] = (filesize >> 8) & 0xff
self.header[4] = (filesize >> 16) & 0xff
self.header[5] = (filesize >> 24) & 0xff
# Reserved1
self.header[6] = 0
self.header[7] = 0
# Reserved2
self.header[8] = 0
self.header[9] = 0
# Offset
self.header[10] = 54
self.header[11] = 0
self.header[12] = 0
self.header[13] = 0
# INFO HEADER
# DIB header size
self.header[14] = 40
self.header[15] = 0
self.header[16] = 0
self.header[17] = 0
# image w
self.header[18] = size_w & 0xff
self.header[19] = (size_w >> 8) & 0xff
self.header[20] = (size_w >> 16) & 0xff
self.header[21] = (size_w >> 24) & 0xff
# image h
self.header[22] = size_h & 0xff
self.header[23] = (size_h >> 8) & 0xff
self.header[24] = (size_h >> 16) & 0xff
self.header[25] = (size_h >> 24) & 0xff
# planes
self.header[26] = 0
self.header[27] = 0
# Bits per Pixel
self.header[28] = 24 # 24bit Color Only
self.header[29] = 0
# Compression
self.header[30] = 0
self.header[31] = 0
self.header[32] = 0
self.header[33] = 0
# Imagesize
self.header[34] = imagesize & 0xff
self.header[35] = (imagesize >> 8) & 0xff
self.header[36] = (imagesize >> 16) & 0xff
self.header[37] = (imagesize >> 24) & 0xff
# Resolution W
self.header[38] = 0
self.header[39] = 0
self.header[40] = 0
self.header[41] = 0
# Resolution H
self.header[42] = 0
self.header[43] = 0
self.header[44] = 0
self.header[45] = 0
# Palette
self.header[46] = 0
self.header[47] = 0
self.header[48] = 0
self.header[49] = 0
# Essential Color
self.header[50] = 0
self.header[51] = 0
self.header[52] = 0
self.header[53] = 0
def MakeBMPFile(self,filepath,r,g,b):
with open(filepath,'wb') as f:
data = self.header
pixel = [chr(b),chr(g),chr(r)]
linedata = pixel*self.w
for p in range(self.padding):
linedata.append(0x00)
mapdata = linedata*self.h
data.extend(mapdata)
f.write(data)
class Display(BoxLayout):
orientation='vertical'
def __init__(self,**kwargs):
super(Display,self).__init__(**kwargs)
self.t1 = TextInput(text="",size_hint_y=1,multiline=False,readonly=True)
self.add_widget(self.t1)
self.boxSave = BoxSaveFileName(size_hint_y=1)
self.add_widget(self.boxSave)
self.boxFile = BoxSelectFile(size_hint_y=15)
self.add_widget(self.boxFile)
self.boxR = BoxColor(size_hint_y=1)
self.boxR.SetUp('R',0,255)
self.add_widget(self.boxR)
self.boxG = BoxColor(size_hint_y=1)
self.boxG.SetUp('G',0,255)
self.add_widget(self.boxG)
self.boxB = BoxColor(size_hint_y=1)
self.boxB.SetUp('B',0,255)
self.add_widget(self.boxB)
def MakeFile(self):
txtFilepath=self.t1.text+'/'+self.boxSave.t1.text+self.boxSave.l1.text
print(txtFilepath)
bmp = BMPFileControl(15,15)
bmp.MakeBMPFile(txtFilepath,self.boxR.value,self.boxG.value,self.boxB.value)
class MainApp(App):
def build(self):
layout = Display()
return layout
if __name__=='__main__':
MainApp().run()
自覚しているので最初に書いてしまうと、ツッコミ所は BMPFileControl クラスです。冗長ってレベルじゃねーぞ。
VC++で BMP 取り扱いクラスを書いていた時はヘッダー構造体を作って、コレを継承するクラスを書いた後、this ポインタにヘッダ構造体ポインタをキャストしてファイル出力に直接流していた記憶がうっすらあるのですが…
慣れていないせいなのでしょうけど python でバイナリファイルを扱おうとすると非常に面倒ですね。
時代の流れで、データサイズを明示したり、ポインタ渡しなどメモリアドレスを直接触れさせる処理を隠蔽していった流れが悪い方に出てしまったように感じるのは私が古い人間だからでしょうか?
4バイト数値をいちいちビットシフトしているのはビッグエンディアン系のCPUも考えると悪くは無いと考えているのですがどうでしょう?
10数年ぶりに Bitmap を取り扱うアプリを作って、大分いろいろ忘れてますね…パディングってなんだっけ?とか、1画素の色の並びをRGBにして失敗したりとか(正解はBGRの順。大昔のハードウェアの都合だったハズ)。
あと、いい加減 Python 3 系にしろと言われそうですが、Linux mint (というか ubuntu)と Blender と kivy の FileChooser が対応してくれない事には身動きとれません。どうにかできる地力も無いですし。
- さて、コレの何処が地獄なのよ?
昔々、20代後半~30くらいでゲーム会社でプログラマーをやっていた頃、髪の毛がザラザラ抜けた仕事が二つありまして、一つは社内ゲーム開発用エフェクトツールを作っていた時、もう一つはテクスチャファイルコンバータを作っていた時なのですよ。
31で引退して以来髪の毛が減っていない事から(注:増えてもいない)、アレは毛根に厳しい仕事だったなと今でも思うのですが、モデリングがある程度片付いてきたのでテクスチャ用の BMP→S3TC(DXTC)コンバータに再挑戦しようかなと思いまして、その足掛かりとして作りました。
…まあ、正直そんなに乗り気では無かったのですが、購入するとすごく高いんですよ、あのコンバータ。みんな貧乏が悪いんや。
- 最後に
今回 python kivy について検索していたら Google 先生の日本の検索上位にこのページが出た事があったのですよ。物を書いている以上、読まれているのは構わないのですが…
閲覧数10回以下
… python + kivy で物作ってる日本人は絶滅種なのでは?と不安になりました。
0 件のコメント:
コメントを投稿