年号変換プログラムをGUIバージョンにした。Tcl/Tkを使用。

OCamlでは、labltkライブラリを使うことで、Tcl/Tkキットを使える。

ただ、Windows環境つまり Cygwin環境ではうまくいかなかった。OPAMでlabltkライブラリのインストールまでは成功したのだが、OCamlトップレベルで、labltk.cmaをロードできなかったのである。原因は不明。

とりあえず、Ubuntuのデスクトップで使えるようになった。

(*
 * 年号変換プログラム ver 0.4 -- labltk バージョン
 *
 * ChangeLog 
 *   0.1: 明治以降の年号と西暦を相互変換できるようにした。
 *   0.2: 文禄以降の年号にも対応できるようにした。
 *   0.3: GUIバージョンを作成
 *   0.4: 和暦の選択をリストから選べるようにした。(2019.05.01)
 *
 * Copyright 2019 Seiichi Nukayama
 *)

open Tk

let ver = "0.4"

(* ware = true --> グレゴリオ暦から和暦を求める処理
 * gre  = true --> 和暦からグレゴリオ暦を求める処理 *)
let ware = ref false
and gre = ref false

(* 和暦の換算リスト *)
let wareki_list =
    [
      ("文禄", 1591); ("慶長", 1594); ("元和", 1614);
      ("寛永", 1623); ("正保", 1643); ("慶安", 1647);
      ("承応", 1651); ("明暦", 1654); ("万治", 1657);
      ("寛文", 1660); ("延宝", 1672); ("天和", 1680);
      ("貞享", 1683); ("元禄", 1687); ("宝永", 1703);
      ("正徳", 1710); ("享保", 1715); ("元文", 1735);
      ("寛保", 1740); ("延享", 1743); ("寛延", 1747);
      ("宝歴", 1750); ("明和", 1763); ("安永", 1771);
      ("天明", 1780); ("寛政", 1788); ("享和", 1800);
      ("文化", 1803); ("文政", 1817); ("天保", 1829);
      ("弘化", 1843); ("嘉永", 1847); ("安政", 1853);
      ("万延", 1859); ("文久", 1860); ("元治", 1863);
      ("慶応", 1864); ("明治", 1866); ("大正", 1911);
      ("昭和", 1925); ("平成", 1988); ("令和", 2018);
      ("未来", 2100)]

(*
 * 和暦からグレゴリオ暦を求める
 * @param: nengo -- (string) たとえば "元禄14" などの文字列
 *         l     -- (list)   wareki_list を想定している
 * @return: suuji + num -- (int) gregorian year
 *)
let rec gregorio nengo l = 
    let nengo_len = String.length nengo in
    let moji = String.sub nengo 0 6 in
    let suuji = String.sub nengo 6 (nengo_len - 6) in
    let num = int_of_string suuji in
    match l with
    [] -> 0
    | (gengo, suuji) :: rest ->
            if moji = gengo then suuji + num
            else gregorio nengo rest
            
(*
 * グレゴリオ暦から和暦を求める
 * @param:  year -- (int) ex.1985
 *          (list) -- wareki_list 
 * @return:      -- ex.("昭和", 31)
 *)
let this_gengo = ref "" and this_year = ref 0

let rec wareki year = function
    [] -> ("", 0)
    | (gengo, nen) :: rest ->
        if nen < year 
        then
            (this_gengo := gengo;
            this_year := nen;
            wareki year rest)
        else
            (!this_gengo, year - !this_year)

let kotae = ref ""  (* 答えの文字 *)

let wareki_select = ref "";;  (* 和暦選択で選択した和暦 *)

let top = openTk ()  (* 一番基礎のウィンドウ -- Tcl/Tk initialize *)

let tv_kotae = Textvariable.create ()  (* 答えをいれる場所 *)

let print_kotae tv =
  Textvariable.set tv !kotae

let tv_wareki = Textvariable.create ()  (* 和暦を入れる場所 *)

let print_wareki tv =                (* tv に和暦をセットする *)
    Textvariable.set tv !wareki_select

(* GUIから和暦を読み取って、西暦変換関数を呼び出し、tvにセットする *)
let chg2seireki nengo entry_year =
  let year = Entry.get entry_year in
  let nengo_year = nengo ^ year in
  let seireki_year = gregorio nengo_year wareki_list in
  kotae := Printf.sprintf "西暦 %d年" seireki_year;
  print_kotae tv_kotae;;
  
(* GUIから西暦を読み取って、和暦変換関数を呼び出し、tvにセットする *)
let chg2nengo entry =
  let seireki_year = int_of_string (Entry.get entry) in
  let (nengo, year) = wareki seireki_year wareki_list in
  kotae := Printf.sprintf "%s %d 年" nengo year;
  print_kotae tv_kotae


(* -------------------- 和暦選択メニュー作成 ------------ *)

(*
 * メニューに年号リストを加える
 * @param -- menu         menuオブジェクト
 *           wareki_list  和暦リスト
 *)
let rec add_menu menu = function
    [] -> []
    | (nengo, year) :: rest ->
            Menu.add_command
            ~label:nengo
            ~command:(fun () -> wareki_select := nengo; print_wareki tv_wareki) menu;
            add_menu menu rest;;

(* -------------------- 1段目 ------------------------- *)
  
let label_title =
  let s = Printf.sprintf "年号<=>西暦 変換プログラム ver: %s" ver in
  Label.create top ~text:s ~relief:`Raised

let label_notice =
  let s = "どちらかに入力して右のボタンをクリックしてください。" in
  Label.create top ~text:s

(* -------------------- 2段目 ------------------------- *)
  
let btn1_frame = Frame.create top

let label_L_btn1 = Label.create btn1_frame ~text:"西暦"
and entry_btn1 = Entry.create btn1_frame ~width:6
and label_R_btn1 = Label.create btn1_frame ~text:"年"
  
let button_btn1 = Button.create btn1_frame ~text:"年号に変換"
                                ~command:(fun () -> chg2nengo entry_btn1)

(* -------------------- 3段目 ------------------------- *)
  
let btn2_frame = Frame.create top

let mb = Menubutton.create btn2_frame ~text:"年号選択" ~relief:`Raised
and label_L_btn2 = Label.create btn2_frame ~textvariable:tv_wareki ~width:6 ~background:`White
and label_C_btn2 = Label.create btn2_frame ~text:"-"
and entry2_btn2 = Entry.create btn2_frame ~width:4
and label_R_btn2 = Label.create btn2_frame ~text:"年"

let menu = Menu.create mb;;

let button_btn2 = Button.create btn2_frame ~text:"西暦に変換"
                 ~command:(fun () -> chg2seireki !wareki_select entry2_btn2)
  

(* -------------------- 4段目 ------------------------- *)
  
let btn3_frame = Frame.create top
  
let label_kotae = Label.create btn3_frame ~textvariable:tv_kotae ~width:30

let button_end = Button.create btn3_frame ~text:"終了" ~command:(fun () -> closeTk(); exit 0)

(* ----------------------- コマンド --------------------------- *)
let () =
    add_menu menu wareki_list;

    Menubutton.configure ~menu:menu mb;

    (* -------------------------- pack -------------------------------------- *)
    pack [coe label_L_btn1; coe entry_btn1; coe label_R_btn1; coe button_btn1] ~side:`Left;

    pack [coe mb; coe label_L_btn2; coe label_C_btn2; coe entry2_btn2; coe label_R_btn2; coe button_btn2] ~side:`Left;

    pack [coe label_kotae; coe button_end] ~side:`Left;

    pack [coe label_title; coe label_notice; coe btn1_frame; coe btn2_frame; coe btn3_frame] ~side:`Top;

    mainLoop()