Kivyで「西暦・和暦・年齢・干支」変換電卓作ってみた。

# はじめに
Pythonのkivyを使って西暦・和暦・年齢・干支の変換をする電卓を作ってみた。西暦、和暦または、年齢を指定して値を入力すると変換した値を出力する。活躍する場はそれほどないけど、あると少し便利な電卓。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1299802/34286e4f-b7db-9260-a6c3-0d2d000a08f2.png)


# 目次
+ [kivyで使ったウィジェット](#kivyで使ったウィジェット)
+ [西暦⇔和暦変換](#西暦和暦変換)
+ [西暦⇔年齢変換](#西暦年齢変換)
+ [西暦→干支変換](#西暦干支変換)
+ [作成したコード](#作成したコード)

# kivyで使ったウィジェット
今回kivyで使ったウィジェットは ```Button```、``Spinner``、``TextInput``。Spinnerは、和暦の明治、大正、昭和、平成、令和を選択する際に使った。kivyについては([記事：PythonのGUIライブラリKivyを使う](https://qiita.com/tapitapi/items/a4a4e3a7164afd922115))を参照。

[戻る](#目次)

# 変換式
今回作った、Pythonコードの補足説明をする。

## 西暦⇔和暦変換
+ **西暦→和暦**
和暦それぞれの元年の西暦の明治(1868年)、大正(1912年)、昭和(1926年)、平成(1989年)、令和(2019年)を辞書型の変換テーブルにしてあげれば、西暦から和暦の変換ができる。変換時の注意点として、和暦は1オリジンなので+1することを忘れずに。
```python
	def selreki_to_wareki(self, seireki):
		# 明治、大正、昭和、平成、令和それぞれの元年の西暦
		wareki_dic ={'明治(年)':1868,'大正(年)':1912,'昭和(年)':1926,'平成(年)':1989,'令和(年)':2019}
		
		if wareki_dic['令和(年)'] <= int(seireki):
			self.root.ids.spn_wareki.text = '令和(年)'
			return str(int(seireki) - wareki_dic['令和(年)']+1)
			
		elif wareki_dic['平成(年)'] <= int(seireki):
			self.root.ids.spn_wareki.text = '平成(年)'
			return str(int(seireki) - wareki_dic['平成(年)']+1)
			
		elif wareki_dic['昭和(年)'] <= int(seireki):
			self.root.ids.spn_wareki.text = '昭和(年)'
			return str(int(seireki) - wareki_dic['昭和(年)']+1)
			
		elif wareki_dic['大正(年)'] <= int(seireki):
			self.root.ids.spn_wareki.text = '大正(年)'
			return str(int(seireki) - wareki_dic['大正(年)']+1)
			
		else:
			self.root.ids.spn_wareki.text = '明治(年)'
			return str(int(seireki) - wareki_dic['明治(年)']+1)
```
+ **和暦→西暦**
同じく、変換テーブルを使えば変換できる。和暦は1オリジンなので-1することを忘れずに。
```python
	def wareki_to_seireki(self, wareki):
		# 明治、大正、昭和、平成、令和それぞれの元年の西暦
		wareki_dic ={'明治(年)':1868,'大正(年)':1912,'昭和(年)':1926,'平成(年)':1989,'令和(年)':2019,'和暦(年)':2019}
		
		return str(int(wareki) + wareki_dic[self.root.ids.spn_wareki.text]-1)
```
[戻る](#目次)

## 西暦⇔年齢変換
+ **西暦→年齢**
現在の西暦から指定した西暦を引けばよい。
```python
	def seireki_to_toshi(self, seireki):
		# 現在の西暦は datetime.now().year で出力
		return str(datetime.now().year - int(seireki))
```
[戻る](#目次)

+ **年齢→西暦**
現在の西暦から、年齢を引けばよい。
```python
	def toshi_to_seireki(self, toshi):
		# 現在の西暦は datetime.now().year で出力
		return str(datetime.now().year - int(toshi))
```
[戻る](#目次)

## 西暦→干支変換
干支は12なので西暦を12の剰余演算すれば干支を計算できる。2023年は'卯(うさぎ)年'で、2023の剰余演算は、2023%12=7。'卯(うさぎ)'=7を基準に12干を辞書型の変換テーブルにしてあげれば、西暦から干支の変換ができる。
```python
	def serireki_to_eto(self, seireki):
		# 12の剰余演算 2023%12=7  2023年は卯で7
		eto_dic ={7:'卯(う)',8:'辰(たつ)',9:'巳(み)',10:'午(うま)',11:'未(ひつじ)',0:'申(さる)',1:'酉(とり)',2:'戌(いぬ)',3:'亥(い)',4:'子(ね)',5:'丑(うし)',6:'寅(とら)'}
		return eto_dic[int(seireki)%12]
```
[戻る](#目次)

# 作成したコード
西暦、和暦または、年齢を指定して値を入力。決定ボタンで変換する。干支は12年周期で一意に変換できないので、干支を指定した場合は西暦を入れる形にしている。kivyで作ったので、Androidアプリ化も可能( [記事：PythonでAndroidアプリを作る。](https://qiita.com/tapitapi/items/e91d67d59ed10caa59f6))。

```python:seiwa_calc.py
from kivy.app import App
from kivy.lang import Builder
import japanize_kivy # https://github.com/momijiame/japanize-kivy 日本語化
from datetime import datetime

#kivy GUI
Layout = Builder.load_string('''


#計算ボタン
CalBox:
	GridLayout:
		cols:2
		size_hint_y:0.2
		SetBtn:
			id:btn_seireki
			text:'西暦(年)'
			background_color:(0,0.5,0,1)
		CalTxt:
			id:txt_seireki
			text:''
	GridLayout:
		cols:2
		size_hint_y:0.2
		SetSpn:
			id:spn_wareki
			text:'和暦(年)'
		CalTxt:
			id:txt_wareki
			text:''
	GridLayout:
		cols:2
		size_hint_y:0.2
		SetBtn:
			id:btn_toshi
			text:'年齢(歳)'
		CalTxt:
			id:txt_toshi
			text:''
	GridLayout:
		cols:2
		size_hint_y:0.2
		SetBtn:
			id:btn_eto
			text:'干支'
		CalTxt:
			id:txt_eto
			text:''
	GridLayout:
		size_hint_y:0.6
		cols:3
		#------------------------
		NumBtn:
			id:btn_7
			text:'7'
		NumBtn:
			id:btn_8
			text:'8'
		NumBtn:
			id:btn_9
			text:'9'
		#------------------------
		NumBtn:
			id:btn_4
			text:'4'
		NumBtn:
			id:btn_5
			text:'5'
		NumBtn:
			id:btn_6
			text:'6'
		#------------------------
		NumBtn:
			id:btn_1
			text:'1'
		NumBtn:
			id:btn_2
			text:'2'
		NumBtn:
			id:btn_3
			text:'3'
		#------------------------
		SymBtn:
			id:btn_clear
			text:'クリア'
		NumBtn:
			id:btn_0
			text:'0'
		SymBtn:
			id:btn_ent
			text:'決定'


#部品=================================
<CalBox@BoxLayout>:
	#背景色
	canvas.before:
		Color:
			rgb:(0,0,0,0)	#背景色なし
		Rectangle:
			size: self.size
			pos: self.pos
	orientation:'vertical'
	
<CalTxt@TextInput>:
	size_hint_x:0.7
	font_size:30
<SetBtn@Button>:
	size_hint_x:0.3
	font_size:30
	background_color:(0,1,0,1)
	on_press:app.btn_on('Settype', self.text)
<SetSpn@Spinner>:
	size_hint_x:0.3
	font_size:30
	background_color:(0,1,0,1)
	values:'明治(年)','大正(年)','昭和(年)','平成(年)','令和(年)'
	on_press:app.btn_on('Settype', self.text)
<NumBtn@Button>:
	font_size:30
	background_color:(0,1,0,1)
	on_press:app.btn_on('Number', self.text)
<SymBtn@Button>:
	font_size:30
	background_color:(0,1,0,1)
	on_press:app.btn_on('Symbol', self.text)
''')

    
#--------------------
# 計算アプリ
#--------------------
class Calc_app(App):
	
	#Global
	settype='西暦(年)'
	
	#----------------------
	# Kivy初期化
	#----------------------
	def build(self):
		self.title = 'Calc_app'
		return Layout
	
	#----------------------
	# Kivy終了
	#----------------------
	def on_stop(self):
		pass
		
	#----------------------
	# btn_on()
	#----------------------
	def btn_on(self,btn_type, btn_id):
		# 西暦/和暦/年齢/(干支:無効)
		if btn_type == 'Settype':
			self.calc_clear()
			
			if btn_id == '西暦(年)':
				self.settype = '西暦(年)'
				self.root.ids.btn_seireki.background_color=(0,0.5,0,1)
			elif btn_id == '和暦(年)' or btn_id in self.root.ids.spn_wareki.values:
				self.settype = '和暦(年)'
				self.root.ids.spn_wareki.background_color=(0,0.5,0,1)
			elif btn_id == '年齢(歳)':
				self.settype = '年齢(歳)'
				self.root.ids.btn_toshi.background_color=(0,0.5,0,1)
			elif btn_id == '干支':
				self.settype = '西暦(年)'
				self.root.ids.btn_seireki.background_color=(0,0.5,0,1)

		# 0～9
		elif btn_type == 'Number':
			if self.settype == '西暦(年)':
				self.root.ids.txt_seireki.text += btn_id
			elif self.settype == '和暦(年)':
				self.root.ids.txt_wareki.text += btn_id
			elif self.settype == '年齢(歳)':
				self.root.ids.txt_toshi.text += btn_id
			else:
				pass
		# 決定/クリア
		if btn_type == 'Symbol':
			if btn_id == '決定':
				if self.settype == '西暦(年)'and self.root.ids.txt_seireki.text.isdecimal():
					self.root.ids.txt_wareki.text  = self.selreki_to_wareki(self.root.ids.txt_seireki.text)
					self.root.ids.txt_toshi.text   = self.seireki_to_toshi(self.root.ids.txt_seireki.text)
					self.root.ids.txt_eto.text     = self.serireki_to_eto(self.root.ids.txt_seireki.text)
					
				elif self.settype == '和暦(年)'and self.root.ids.txt_wareki.text.isdecimal():
					self.root.ids.txt_seireki.text = self.wareki_to_seireki(self.root.ids.txt_wareki.text)
					self.root.ids.txt_wareki.text  = self.selreki_to_wareki(self.root.ids.txt_seireki.text)
					self.root.ids.txt_toshi.text   = self.seireki_to_toshi(self.root.ids.txt_seireki.text)
					self.root.ids.txt_eto.text     = self.serireki_to_eto(self.root.ids.txt_seireki.text)
					
				elif self.settype == '年齢(歳)'and self.root.ids.txt_toshi.text.isdecimal():
					self.root.ids.txt_seireki.text = self.toshi_to_seireki(self.root.ids.txt_toshi.text)
					self.root.ids.txt_wareki.text  = self.selreki_to_wareki(self.root.ids.txt_seireki.text)
					self.root.ids.txt_eto.text     = self.serireki_to_eto(self.root.ids.txt_seireki.text)
			else:
				self.calc_clear()


	#----------------------
	# 西暦→干支計算
	#----------------------
	def serireki_to_eto(self, seireki):
		# 12の剰余演算 2023%12=7  2023年は卯で7
		eto_dic ={7:'卯(う)',8:'辰(たつ)',9:'巳(み)',10:'午(うま)',11:'未(ひつじ)',0:'申(さる)',1:'酉(とり)',2:'戌(いぬ)',3:'亥(い)',4:'子(ね)',5:'丑(うし)',6:'寅(とら)'}
		return eto_dic[int(seireki)%12]

	#----------------------
	# 西暦→年齢計算
	#----------------------
	def seireki_to_toshi(self, seireki):
		# 現在の西暦は datetime.now().year で出力
		return str(datetime.now().year - int(seireki))

	#----------------------
	# 西暦→和暦計算
	#----------------------
	def selreki_to_wareki(self, seireki):
		# 明治、大正、昭和、平成、令和それぞれの元年の西暦
		wareki_dic ={'明治(年)':1868,'大正(年)':1912,'昭和(年)':1926,'平成(年)':1989,'令和(年)':2019}
		
		if wareki_dic['令和(年)'] <= int(seireki):
			self.root.ids.spn_wareki.text = '令和(年)'
			return str(int(seireki) - wareki_dic['令和(年)']+1)
			
		elif wareki_dic['平成(年)'] <= int(seireki):
			self.root.ids.spn_wareki.text = '平成(年)'
			return str(int(seireki) - wareki_dic['平成(年)']+1)
			
		elif wareki_dic['昭和(年)'] <= int(seireki):
			self.root.ids.spn_wareki.text = '昭和(年)'
			return str(int(seireki) - wareki_dic['昭和(年)']+1)
			
		elif wareki_dic['大正(年)'] <= int(seireki):
			self.root.ids.spn_wareki.text = '大正(年)'
			return str(int(seireki) - wareki_dic['大正(年)']+1)
			
		else:
			self.root.ids.spn_wareki.text = '明治(年)'
			return str(int(seireki) - wareki_dic['明治(年)']+1)

	#----------------------
	# 年齢→西暦計算
	#----------------------
	def toshi_to_seireki(self, toshi):
		# 現在の西暦は datetime.now().year で出力
		return str(datetime.now().year - int(toshi))

	#----------------------
	# 和暦→西暦計算
	#----------------------
	def wareki_to_seireki(self, wareki):
		# 明治、大正、昭和、平成、令和それぞれの元年の西暦
		wareki_dic ={'明治(年)':1868,'大正(年)':1912,'昭和(年)':1926,'平成(年)':1989,'令和(年)':2019,'和暦(年)':2019}
		
		return str(int(wareki) + wareki_dic[self.root.ids.spn_wareki.text]-1)
		
	#----------------------
	# 値クリア
	#----------------------
	def calc_clear(self):
		self.root.ids.txt_seireki.text = ''
		self.root.ids.txt_wareki.text  = ''
		self.root.ids.txt_toshi.text   = ''
		self.root.ids.txt_eto.text     = ''
		self.root.ids.spn_wareki.text  = '和暦(年)'
		
		self.root.ids.btn_seireki.background_color=(0,1,0,1)
		self.root.ids.spn_wareki.background_color=(0,1,0,1)
		self.root.ids.btn_toshi.background_color=(0,1,0,1)
				
	
if __name__ == '__main__':
	Calc_app().run()
	
```
[戻る](#目次)

以上