日本語プログラミング言語「なでしこ」 Advent Calendar 2022
閲覧ありがとうございます。この記事はなでしこアドカレ2022の4日目の記事となります。
雪乃 雫さん(@snowdrops89)の3日目の記事はコチラ
クジラ飛行机さん(@kujirahand)の5日目の記事はコチラ
「Hello, なでしこアドカレとQiita!!」と表示する
なでしこアドカレは初参戦です。そしてそもそも、Qiitaを書くのも初めてです。
というわけで、Hello, なでしこアドカレとQiita!! ユメミノウツツと申します。なでしこ3貯蔵庫とかでは賢者ユメミノウツツという名前で出没することもあります。ただの表記揺れで同一人物です・・・。(今まで作ったものはコチラ → https://n3s.nadesi.com/index.php?action=list&user_id=63)
なでしこは今年の年始に始めた若輩者ですが、この度制作物発表もかねてアドカレに参加させていただきました。技術面で至らぬ点も多々あるとは思いますが、大目に見てくださると幸いです。 m(_ _)m
CSV to LuaMacros変換機
なでしこに関する詳しい説明は私よりも有識者の方々がやってくれると思いますので(投げ)、早速作ったアプリの紹介をさせていただきます。なお、記事の性質上なでしこ以外の話が大部分を占めてしまいます。どうか寛大なお心でお許しください・・・。
名前は「CSV to LuaMacros変換機」。テンプレートに沿ってCSVに書き込めば、それを元にLuaMacrosで使えるソースコードを書いてくれるアプリとなっています。(ソースコードを書くといっても、ただ指定の位置に文字列を挿入しているにすぎませんが・・・)
今回はなでしこ3で作ったので、なでしこ3貯蔵庫にて公開しています。
・・・と一通り概要を説明しましたが、これを読んだ99.99%の人が「LuaMacrosってなんじゃらほい?」と思われたと思います。ですから、先にそもそもLuaMacrosとは? という説明をさせていただきます。
LuaMacrosとは
LuaMacrosとは、Petr Medekさんが作られたWindows向けソフトウェアの名前です。Git Hubにて公開されています。
このソフトウェアは、同じ方が作られたHID Macrosというソフトウェアの後継として制作されました。
LuaMacrosを端的に説明すれば、「キーボードにマクロを割り当てられるソフト」です。例えば、キーボードのこのキーを押したらメモ帳が開くようにしたい、このキーを押したらnakopad.exeが開くようにしたい、このキーを押したら「Hello」と入力してほしい、などなど、キー1つ1つにマクロを組んで設定することができます。今使っているキーボードをプログラマブルキーボードにできるソフトウェア、といえばわかりやすいでしょうか。
しかし、そうやって聞くと、「Zキーにマクロを割り当てたら、二度とザ行が打ち込めなくなるってこと?」と思うかもしれません。ご安心ください。キーにマクロを設定できるソフトは他にもありますが、LuaMacrosの凄いところはキーボードを2個用意すればそれを防げるという点にあるのです。
なでしこv1でもホットキー登録などを使えば似たようなアプリを作ることは可能です。しかし、例えばキーボードAとキーボードBをPCに接続して、キーボードAでは普通に入力ができて、キーボードBにはマクロを設定したいというようなことを考えたとき、これをなでしこで実装するのは難しいです(多分)。どうして難しいかというとUSB HIDとかOSとかキーボードデバイスドライバーとか仮想キーコードとか・・・そういう話になるのでしょうが、私が知識がないため適当なことをいうのはやめておきます。
とにかく多くのアプリケーションは「どのキーボードのどのキーが押されたか」ではなく、「どのキーが押されたか」を受け取って実装されているのではないかと思います。それに対してLuaMacrosは、USBデバイスそれぞれに固有に割り振られているデバイスIDというものを使って、特定のキーボードだけを対象にマクロを組めるソフトウェアなのです。ですから、キーボードBのZキーだけにマクロを割り当てるといった動作が可能になります。
LuaMacrosに関する詳細はコチラの記事が詳しいので、詳しく知りたければリンク先を読んでいただければと思います。(Qiitaの記事で1点気になるのが、else ifで書いているところです。Luaの場合はelseifなのでコードが間違っていると思われます。)
また、公式Wikiと公式フォーラムがあります。英語ですがそもそもLuaMacrosの日本語の情報はほとんどないので、貴重な情報ソースとしてリンクしておきます。(フォーラムはHIDMacrosと銘打ってありますが、現在はLuaMacrosのみサポートされています。)
LuaMacrosのコーディング
LuaMacrosは色々な使い方ができますし、何より作者さんはこれをフライトシミュレーター用に開発されました。ですが、ここではあるキーを押したときに任意の文字列を送信する、という使い方に焦点を絞りたいと思います。(その理由は私が実験レポートをLaTeXで書く際の補助ツールにしようと思ってこのアプリを作ったからです。)なお、任意の文字列というのはCtrl+Zなどのショートカットキーも含めるものとします。
LuaMacrosは名前の通り、Luaでコーディングを行います。とはいいますが、Luaの知識はほとんどなくても大丈夫です。私はLuaが一切書けませんが見様見真似で使えるようになりました。そして何より、キーを押したときに好きな文字列を送信したいというだけならCSVに内容を書き込むだけでコードを作れます、というのがこのアプリの売りになります。ですからぜひご活用ください!
さて、自作アプリの紹介の前に、少しだけLuaMacrosのメソッドについて説明します。
LuaMacrosでは前述の通り、デバイス固有のIDであるデバイスIDを用いてキーボードを識別しています。そのデバイスIDを出力してくれるメソッドが「lmc_print_devices()」です。
lmc_print_devices()
[RESULT]
\\?\HID#VID_046D&PID_C313&MI_00#9&378940FB&0&0000#{884B96C3-56EF-11D1-BC8C-00A0C91405DD} [89397213]
LuaMacrosを起動してテキストエリアに上記メソッドだけを入力して実行(▶)を押すと、上記リザルトのような文字列が出力されます。このとき、デバイスIDは「378940FB」の部分になります。マクロを割り当てたいキーボードのデバイスIDを探してメモしておいてください。
また、文字列送信の要となるメソッドが「lmc_send_keys()」 です。
lmc_send_keys('Hello world')
このメソッドを実行すると、「Hello world」が文字送信されます。ただし、送信に対応しているのはキーボード上で直接入力できるキーに限り、ひらがなとかは不可能です。また、"^Z"とすればCtrl+Zが送信されるなど、装飾キーに変化する特殊文字などがあります。
他のメソッドや特殊文字について、詳しくはLuaMacros公式Wikiをご覧ください。
ちなみに、シングルクォーテーションでもダブルでも特に変わりはないそうです。(参考:https://densan-labs.net/tech/lua/chapter2.html)
CSV to LuaMacros変換機
さて、ここからやっと自作アプリの紹介をしていきたいと思います。
CSV to LuaMacros変換機は、テンプレートに沿ってCSVに書き込めば、それをLuaMacrosのソースコードに変換してくれるアプリです。まずは動作画面を見ていただきたいと思います。
動作説明
こちらは貯蔵庫でCSV to LuaMacros変換機を実行し、CSVを入れて変換ボタンを押した後の状態になります。
使い方の説明です。
最初に、CSVファイルを読み込みます。①ファイルを選択 をクリックしてファイルを選ぶか、(①)のテキストエリアにCSVファイルをドラッグ&ドロップするとCSVが読み込まれ、(①)に中身が表示されます。ただし、CSVの文字コードがUTF-8ならBOMありでもBOMなしでもどちらでも問題ないのですが、Shift_JIS(ANSI)の場合は①の下にチェックを入れてください。(チェックを入れると自動で再読み込みされます。)
次に、先ほどlmc_print_devices()でメモしていただいたデバイスIDを②の中に書き込んでください。これを初期設定のUNKNOWN_IDのままにしても変換は可能ですが、デバイスIDがないとどのみち動かせないので後でご自分で設定していただくことになります。
ここまで準備が済みましたら③LuaMacrosソースに変換 ボタンをクリックします。すると、下の④のテキストエリアに完成したソースコードが表示されます。
最後に⑤ですが、本来このソースをluaファイルとしてダウンロードするボタンを作ろうと思ったのですが、うまくいかなかったのでここは現在工事中になっております。④のテキストを全選択・コピーしてお使いください。
(ダウンロードするのが難しそうだったから⑤を押すとクリップボードにコピーされるように設定したのですが、作品の編集画面ではちゃんと機能するのに、作品ページで実行するとなぜだかクリップボードにコピーされませんでした・・・原因がわかる方がいらっしゃったら教えていただきたいです・・・。)
ソースコード
では、次に生成されるソースコードの一例を見てみましょう。
--CSV to LuaMacros Converter
--Created by ymm_utt
--lmc_device_set_name...デバイスIDに名前を付ける
--デバイスIDを入力しなかった場合"UNKNOWN_ID"となるので正しく書き換えてください。
lmc_device_set_name("CSVLMConverter","378940FB")
--各キーのフラグ
--一度押すとtrueになり、もう一度押すとfalseになる
--本当は押しっぱなしでtrueにしたかったがうまくいかなかったのでこの仕様になった
flag_shift = false
flag_ctrl = false
flag_alt = false
--各キーの押された状況を数字で返す
function SCAcheck()
-- |-----+-----+-----+-----|
-- | Num |Shift|Ctrl | Alt |
-- |-----+-----+-----+-----|
-- |0 |x |x |x |
-- |1 |o |x |x |
-- |2 |x |o |x |
-- |3 |o |o |x |
-- |4 |x |x |o |
-- |5 |o |x |o |
-- |6 |x |o |o |
-- |7 |o |o |o |
-- |-----+-----+-----+-----|
if (flag_shift == true) then
if (flag_ctrl == true) then
if (flag_alt == true) then
return 7
else
return 3
end
elseif (flag_alt == true) then
return 5
else
return 1
end
elseif (flag_ctrl == true) then
if (flag_alt == true) then
return 6
else
return 2
end
elseif (flag_alt == true) then
return 4
else
return 0
end
end
--フラグの状況をフィールドに表示する
function SCAprint()
clear()
print("Shift : " .. tostring(flag_shift))
print("Ctrl : " .. tostring(flag_ctrl) )
print("Alt : " .. tostring(flag_alt) )
end
--フラグを全て押されていない状況にする
function restore()
flag_shift = false
flag_ctrl = false
flag_alt = false
SCAprint()
return
end
--起動時にフラグ状況(全部false)を表示
SCAprint()
--キーが押された時の動作を設定。第1引数はキーボード名、第2引数は実行関数
--第2引数関数の第1引数は押されたボタン。第2引数は押した・押しっぱなしのときは1、離したら0。
lmc_set_handler("CSVLMConverter",function(button,direction)
--離したときのみ動作
if (direction == 1) then return end
--Shift(16)を押したとき、フラグがfalseならtrueにし、trueならfalseにする
if(button == 16) then
if(flag_shift == false) then
flag_shift = true
else
flag_shift = false
end
SCAprint()
return
end
--Ctrl(17)を押したとき、フラグがfalseならtrueにし、trueならfalseにする
if(button == 17) then
if(flag_ctrl == false) then
flag_ctrl = true
else
flag_ctrl = false
end
SCAprint()
return
end
--Alt(18)を押したとき、フラグがfalseならtrueにし、trueならfalseにする
if(button == 18) then
if(flag_alt == false) then
flag_alt = true
else
flag_alt = false
end
SCAprint()
return
end
--Key map
local scanflag = SCAcheck()
if (button == 90) then --Z
if (scanflag == 0) then
lmc_send_keys("XYZ")
elseif (scanflag == 1) then
lmc_send_keys("+a")
elseif (scanflag == 2) then
lmc_send_keys("^z")
elseif (scanflag == 3) then
lmc_send_keys("{^}z")
elseif (scanflag == 4) then
lmc_send_keys("{ENTER}")
elseif (scanflag == 5) then
lmc_send_keys("a{{}ENTER{}}b{ENTER}c")
elseif (scanflag == 6) then
lmc_send_keys("a\nb")
elseif (scanflag == 7) then
lmc_send_keys("a\\nb")
end
restore()
return
else --未定義のキーはそのまま送信
lmc_send_input(button, 0, 0)
lmc_send_input(button, 0, 2)
end
end
)
事細かくコメントを入れてあるので読めば大体の意味がわかるようにはしていますが、ちょっと特殊な動作を入れてあるのでそこを解説しておきます。
今回私がレポートを書くための補助ツールとして使うために、同じキーに複数のマクロを割り当てたい場合を想定して、Shift・Ctrl・Altキーの状況に合わせて計8通りまで割り振れるようにプログラムを組んでいます。この状況というのは、「一度Shift・Ctrl・Altを押したらそれぞれのフラグがtrueになり、もう一度押したらfalseになる」といった具合です。また、SCAcheckメソッドでフラグの状態を0~7の数字として返すようにしています。
「--Key map」より前のソースはデバイスIDを除いて全く変わりませんので、このアプリを使う上で重要な部分はデバイスID(②で指定します)と、キーマップの中の情報だけということになります。そして、後者をCSVで指定することになります。
CSVの書き方
それでは、肝心のCSVの書き方について見ていきましょう。
CSVでは、「どのキーに」「どの状態(0~7)のとき」「どのように文字列送信させたいか」を列挙して記述します。
テンプレートを作成したのでそれに従って確認してみましょう。Googleドライブに置いてありますのでご自由にダウンロード・改変してご利用ください。(BOM付きUTF-8なのでExcelでもそのまま開けると思います。)
上のtempate.csvの中身は次のようになっています。
Keycode | Pressed key name (Option) | Send key (0: Neutral) | 1: Shift | 2: Ctrl | 3: Shift+Ctrl | 4: Alt | 5: Shift+Alt | 6: Ctrl+Alt | 7: Shift+Ctrl+Alt |
---|---|---|---|---|---|---|---|---|---|
90 | Z | XYZ | +a | ^z | ^z | {ENTER} | a{ENTER}b改行c | a¥nb | a\nb |
項目を1つ1つ説明していきます。
まず「Keycode」です。ここにはマクロを割り当てる側であるキーの仮想キーコードを10進数で指定します。
仮想キーコードはググればいろんなサイトが表を作ってくれています。適当に参照してください。ここで紹介する1つ目のリンクは表、2つ目のリンクは押したキーのキーコードを返してくれるサイトです。
次に「Pressed key name (Option)」です。ここはキーコードだけではどのキーなのかわかりにくいので、それが何のキーなのかを書いておく枠です。ここに書かれた言葉は「if (button == (Keycode)) then --(Pressed key name (Option))」とコメントアウトに入りますので、正直あってもなくてもよいですし、何を書いても構いません。各自わかりやすいようにご活用ください。
「Send key (0: Neutral)」には、装飾キーが何も押されていない最もオーソドックスな状態で送信する文字列を入力します。また、この後の1~7も同様に、1~7の状態のときに送信する文字列を入力してください。
送信する文字列について注意点、そしてこのアプリの目玉ポイントの紹介です。
先ほども書いた通り、LuaMacorsには特殊文字があります。これは2種類に大別できて、Luaの特殊文字(例えば「\n」が改行を意味する、など)とLuaMacrosの特殊文字(例えば「^」がCtrl、「+」がShiftを意味する、など)があります。このうちLuaの特殊文字は「\」を重ねることでエスケープでき、LuaMacrosの特殊文字は「{}」で囲むことでエスケープできます。
CSV to LuaMacros変換機では、設計思想として基本これらの文字はエスケープをする前提となっています。すなわち、表の3「^z」や5「{ENTER}」、7「\n」は自動的にエスケープされ、そのまま文字送信されます。すなわち、CSVに書かれたとおりに文字が入力されます。
エスケープするのが前提なのは、無駄にLuaMacrosの特殊文字に引っかかって意図しない文字列が送信されるのを防ぐためです。今回私はLaTeXのコマンドを手早く入力するためにこのアプリを作ったので、\や{}なんかはしょっちゅう出てきます。これらをCSV上ではなんも考えずにそのまま打ち込めばプログラムの方ではちゃんとエスケープしてくれる、それがこのアプリの売りです。(LuaMacrosを使ったことがない人が使うことを想定しても、特殊文字のせいで意図しない文字列が送信されるのは好ましくありませんしね。)
では実際に特殊文字を特殊文字として使いたい場合はどうすればよいのでしょうか。その場合は、各種記号を全角で記入してください。表の1「+a」、2「^z 」、4「{ENTER}」、6「a¥nb」はよく見ていただくと全て全角記号になっています。このように全角で書いていただければ、それらをプログラム内で半角に置換処理しますので、特殊文字を特殊文字としてご利用いただけます。(詳しくはなでしこのプログラム内部にあるCSVLM変換関数を読んでください。)
また、いちいち全角記号を挟むのは面倒くさいということで、使用頻度が高い特殊命令は日本語で入力すると命令に置換するようにプログラムされています。すなわち、表の5「a{ENTER}b改行c」は、プログラムを通すと「a{{}ENTER{}}b{ENTER}c」に変換されます。({ENTER}はエスケープされ、改行は{ENTER}に置換されています。)
日本語コマンドと対応表を載せておきます。(詳しくはなでしこのプログラム内部にある日本語コマンド処理関数を読んでください。)
日本語コマンド | 特殊命令 | 意味 |
---|---|---|
← | {LEFT} | 左矢印キー |
↑ | {UP} | 上矢印キー |
→ | {RIGHT} | 右矢印キー |
↓ | {DOWN} | 下矢印キー |
改行 | {ENTER} | エンターキー |
タブ | {TAB} | タブキー |
以上をまとめると、template.csvと実際にZキーを押したときに送信される文字列は次のようになります。
Keycode | Pressed key name (Option) | Send key (0: Neutral) | 1: Shift | 2: Ctrl | 3: Shift+Ctrl | 4: Alt | 5: Shift+Alt | 6: Ctrl+Alt | 7: Shift+Ctrl+Alt |
---|---|---|---|---|---|---|---|---|---|
90 | Z | XYZ | +a | ^z | ^z | {ENTER} | a{ENTER}b改行c | a¥nb | a\nb |
---- | ---- | XYZ | A (Shift+a) |
(Ctrl+Z) | ^z | (改行) | a{ENTER}b c |
a b |
a\nb |
ちなみに、先頭の行・KeycodeかSend key (0: Neutral)が空欄の行はエラー対策としてなかったものにされるので、CSVの1行目は消さないでください。また、1~7の中で空欄のところは0の内容がそのまま入るようになっています。「そんなShift,Ctrl,Altの使い分けとかいらない・・・」って方も安心して0のところだけ埋めて変換してください。
なでしこソースと処理の話
アプリの紹介はこれで全てなのですが、このままではなでしこのアドベントカレンダーの割になでしこの占めるスペースが本当に少なくなってしまったので(ごめんなさい)、ソースと処理の話を軽く挟んでおきたいと思います。なお、ソースコードは貯蔵庫から見られますのでこちらには掲載しません。
まずDOMとかその辺のGUIのお話です。
今回のGUIの実装で悩んだのが、パソコンからCSVファイルを読み込む方法とファイルをダウンロードする方法でした。なでしこ3のリファレンスを読んでもその手の方法が見つからないなぁ・・・と思っていた矢先、雪乃雫さんがご自身のプラグインを改造して、CSVファイルを読み込めるようにしてくださいました。(そのプラグインの記事はコチラ。)ですのでそちらをありがたく使わせていただき、前者の話については事なきを得ました。雪乃雫さん、ありがとうございました!
そして後者についてですが、結局JavaScriptを書くしか方法がなさそうかな・・・ということで、とりあえず保留に。URL.createObjectURL(Blob)を使って一時的にURLを作成し、そこにアクセスさせることでダウンロードする・・・といった具合なのでしょうか。JavaScript、わかんないよー!💦
放置されたダウンロードボタンはコピーするボタンに仕様変更したものの、それもなぜだか作品ページでは動かなくて・・・今回の未解決問題になっております。作品編集ページではちゃんとクリップボードにコピーできるのに、作品ページではうまくいかないのはなんででしょう。Chromeの権限周りとかなんですかねぇ・・・?
次に肝心の変換処理のお話です。
まず変換ボタンを押すと、CSVを処理するところから始まります。
CSVLM変換で、半角の特殊文字をエスケープ、全角の特殊文字を半角に置換しています。これは反復と置換を用いて普通に処理するだけです。また、日本語コマンド処理でズボラさん(私)のための置換もやっておきます。
テキスト状態のCSVをこうやって処理したのち、なでしこのCSV取得で二次元配列にします。あと、1行目の削除とエラーを吐きそうな行の削除、1~7で空欄がある場合は0の文字列で埋めてあげたりしておきます。
あとはソースコードを作るだけです。今回LuaMacrosのソースコードを作るわけですが、前述した通り人によって違う部分はデバイスIDとキーマップしかありません。ですから、それ以外の部分は共通な文になるわけですが、問題はいかんせんそれが長いこと。ただソースコードに埋め込むのは汚いし芸がありません。
そこで貯蔵庫のアップローダー機能を使い、事前に共通部分をtxtファイルとしてアップロード、それを読み込む形で実装してみました。
S=「https://n3s.nadesi.com/image.php?f=212.txt」にAJAX送信
この部分は自分的にはうまくいったなと思っているのですが、txtファイルの中身を読み取る方法って、AJAX送信でよかったのでしょうか? JavaScriptを学んでいないのでAJAXがなにか全然わかっていないのですが、ともかく動いたのでヨシ!👈という気分で使っております(汗)。
あとはデバイスIDを②から拾ってセットしてあげるのと、配列をひたすら反復してキーマップを生成するだけです。LuaMacrosを使っていて、ひたすら似たソースコードを作成する作業とエスケープし忘れるのをなんとかしたい! と思って作ったアプリでしたが、想定通りCSVに情報をまとめておくことでluaソースの作成・メンテナンスが簡単になって、私としてはにんまり笑顔であります。(ついでに、LuaMacros利用者がもっと増えるといいなぁ。便利なアプリですよ。)
おわりに
以上、制作物紹介も兼ねたなでしこアドベントカレンダー記事でした。想定以上になでしこ要素が薄くなってしまって本当にすみませんでした。そして投稿が遅くなってしまいこちらもすみませんでした・・・。
LuaMacros、普段使ってるキーボードとは別にテンキー1個でも買ってくれば、定型文入力やショートカットキーが手軽にできるようになるので、とても便利なアプリだと思います。日本語の送信に対応していないのが難点ですが、まあ全角モードの状態で「osewaninatteorimasu.」とか送信すると、日本語の送信にもギリギリ使用できるかと思います。あとイラストレーターとかゲーマーの左手デバイスみたいな使い方にもいかがでしょうか。
そしてLuaMacrosを使う機会があれば、ぜひこのアプリを使っていただけると幸いです。きっとあなたのお役に立ちますよ。
また、LuaMacrosの方からこのページを開いてくださった方へ。このアプリは「なでしこ3」という日本語プログラミング言語を用いて制作されています。プログラミングって難しそう・・・という方もそんな臆さないで大丈夫です。日本語でプログラミングができますので、初心者の方でも日本人ならすんなりプログラミングの世界へ足を踏み入れることができましょう。プログラミングには覚えがある方にも。普段CとかPythonとかっていうのは英語でコーディングするものですけれども、一度日本語でプログラミング、してみませんか? 日本語でプログラミングする不思議な感覚を体験できるるとともに母国語でプログラミングする気分というのを味わえると思います。
そんなわけで、日本語ができる方ならどんな方でも、Let's なでしこ!
さて、記事が長くなってしまいましたが、最後に一言述べて締めさせていただきたいと思います。
なでしこ・ひまわり、20周年おめでとうございます! 日本語でプログラミングするという逆転の発想、非常に気に入っております。プログラミングについてはまだまだ初心者の私ですが、これからもなでしこを愛用していきたいと思いますので、今後もどうぞよろしくお願いいたします。
以上、ユメミノウツツがお送りしました! またね~!