単項マイナスと構文解析
単項マイナスとは 単項マイナスと括弧 括弧なし単項マイナスを許容する場合のBNF calcの場合
今回もRubyとGUIのトピックです。 Glimmerを取り上げます。
GlimmerはRubyにおけるDSL(Domain specific language, ドメイン固有言語)です。 すなわち、Rubyを用いてその中にプログラミング言語を作ったものです。
RubyはDSLを比較的簡単に作ることのできる言語です。 例えばRakeなどは一種のDSLで、taskやfileなどの独自のコマンドを持っています。 これらのコマンドはRubyのメソッドやブロックを用いていることが多いです。
GlimmerはGUIに関する言語をRubyにおいて作ったものです。 これは言語ですから、GUIライブラリとは異なります。 GUIライブラリを土台にして、その上にライブラリを動かす言語を作っています。 GUIライブラリはJava/SWT(JRuby使用)、Opal(Rails使用、ウェブブラウザ上のGUI)、libui、Tk、Gtk3(開発初期段階)などが可能です。 今回はlibui上のGlimmerを紹介します。
libuiは開発段階がmid-alpha(アルファ版の中段階)となっています。 まだ安定版は出ていませんので、このライブラリを採用するかどうかは迷うかもしれません。 もしも安定版を使いたい場合は「Glimmer dsl for SWT」を考えても良いかもしれません。 ただ、こちらはJRubyなので起動が遅い、メモリ使用量が多いなどのデメリットもあります。 長所、短所を見比べて選ぶことになると思います。
「Glimmer-dsl-libui」のGitHubページのREADME.mdが詳しいドキュメント(英語)になっています。 読むのが大変なくらい長いですが、詳しく丁寧なのでとても助かります。
ここでは「Glimmer-dsl-libui」をインストールし、使ってみた経験を書きたいと思います。 この記事はドキュメントではないので、詳細については上記のGitHubを参照してください。
Gemをインストールするだけです。 今回はGemfileを作ってBundlerでインストールしました。 Glimmerのプログラムを作る作業ディレクトリを作成し、そこにGemfileを作ります。
source "https://rubygems.org"
gem 'glimmer-dsl-libui', '~> 0.5.24'
そのディレクトリにカレントディレクトリを移動して、Bundlerでインストールします。
$ bundle install
Fetching gem metadata from https://rubygems.org/.........
Resolving dependencies...
Using bundler 2.3.23
Using matrix 0.4.2
Fetching os 1.1.4
Fetching libui 0.0.15
... ... ...
Installing libui 0.0.15
... ... ...
Fetching glimmer 2.7.3
Installing glimmer 2.7.3
Fetching glimmer-dsl-libui 0.5.24
Installing glimmer-dsl-libui 0.5.24
Bundle complete! 1 Gemfile dependency, 15 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
$
正しくインストールできていれば、次のコマンドでウィンドウが表示されます。
$ ruby -r glimmer-dsl-libui -e "require 'examples/meta_example'"
ものごとの始まりはいつも「Hello world」の表示です。 プログラムを見てみましょう。
require 'glimmer-dsl-libui'
include Glimmer
w = window('hello') {
label('Hello world')
}
w.show
Glimmerを使うには、glimmer-dsl-libui
をrequireする必要があります。
また、Glimmerモジュールをインクルードしておきます。
表示された画面は次のようになりました。
requireとincludeはhello.rbのサンプル同様に指定します。
メインウィンドウはwindowメソッドで定義します。
window(タイトル, 幅, 高さ, メニュー有無) {... ... ...}
メニューの有無はtrue/falseで指定します。 デフォルトはtrueです。 trueでもメニューを定義しなければメニューは現れません。
メニューを定義するにはmenuメソッド(メニューバーに現れる項目)、menu_itemメソッド(メニューバーをクリックしたときに現れるメニューの項目)を使います。
menu(項目名)
menu_item(項目名) {... ... ...}
menu_itemではメニューがクリックされたときの動作をブロックに書きます。 動作はリスナー(クリックを聞いているもの、クリックされると動作するもの)といいます。 リスナーはon_clickedメソッドで書きます。
window、menu、menu_itemなどのメソッドはコントロールを定義し、on_clickedはリスナーを定義しますが、両者でブロックの書き方を変えるのがGlimmerの流儀です。
これは分かりやすくするための習慣で、異なる書き方をしてもエラーにはなりません。 ウィンドウとメニューの例menu.rbのコードを以下に示します。
require 'glimmer-dsl-libui'
include Glimmer
menu('File') {
menu_item('Open') {
on_clicked do
file = open_file
@label.text = File.read(file)
end
}
quit_menu_item
}
window('Menu', 800, 600) {
@label = label('')
}.show
open_fileメソッドはファイル・オープン・ダイアログを表示し、選択されたファイルのパス名を返します。
quit_menu_itemは終了メニューです。 これだけ書いておけばメニューからクリックにより終了できます。
これに似たメニューアイテムにabout_menu_itemがあります。 このメソッドは「About」という名前のメニューアイテムを追加します。 on_clickedリスナーはブロックの中に書きます。
画面のスクリーンショットは、Openメニューからファイル「menu.rb」を読み込んだ後に、終了メニューをクリックする直前の様子です。
コントロールを縦または横に並べるコンテナとしてhorizontal_box(横に並べる)とvertical_box(縦に並べる)があります。 それぞれのメソッドのブロックの中にコントロールを記述します。
ホリゾンタル・ボックス内のコントロールに与えられる幅は等しくなります。 バーティカル・ボックス内のコントロールに与えられる高さは等しくなります。
ボックス内の幅、高さが等分にならないようにするには、コントロールのブロック内に
stretchy false
を入れておきます。 これにより次のコントロールが詰めて配置されます。
windowあるいはボックスに配置できるコントロールには、以下のようなものがあります(全てではない)。
この中のボタンとエントリーを使うと、前回のShoesで作った電卓プログラムを作れます。
ボタンは次のような構文で使います。
button(ボタンに表示する文字列) { ... ... ...}
エントリーは次のような構文で使います。
entry {... ... ...}
なお、ブロックにはパラメータをひとつ付けることができ、そのパラメータは各メソッドの返すオブジェクトと同じものです。 例えば、
entry {|e| ... ... ...}
eはentryが返すエントリーオブジェクトと同じものです。
ファイルはcalc.rb
とlib_calc.rb
の2つです。
lib_calc.rb
は前回の記事(Shoesの記事)で出てきたものと同じですので省略します。
以下にcalc.rb
のソースコードを示します。
require 'glimmer-dsl-libui'
require_relative 'lib_calc.rb'
include Glimmer
def get_answer a
if a.instance_of?(Float) && a.to_i == a
a.to_i.to_s
else
a.to_s
end
end
window('calc', 400, 80) { |w|
calc = Calc.new
vertical_box {
horizontal_box {
@e = entry
horizontal_box {
button("計算") {
on_clicked do
@answer.text = " "+get_answer(calc.run(@e.text))
end
}
button("クリア") {
on_clicked do
@e.text = ""
end
}
button("終了") {
on_clicked do
w.destroy
LibUI.quit
0
end
}
}
}
@answer = label("")
}
}.show
windowメソッドの部分を説明しましょう。
calc
に代入する。@answer
変数にそのオブジェクトを代入しておく)@e
に代入(2)ホリゾンタルボックス@e.text
から計算した結果を代入する@e.text
を空文字列にするw.destroy
)、libuiのメインループを終了させ(LibUI.quit)0を返す実行すると次のような画面になります。 「(2+3)*4」を実行したところです。
プログラムの大筋はShoesと似ている感じがします。
表をウィンドウ上に作るためのメソッドが用意されています。 二重配列で表のデータを作り、その配列を渡すことでウィンドウ上に表が現れます。
まず、text_column
メソッドでタイトル行を定義し、表本体は二重配列をcell_rows
メソッドに引数として渡します。
以下に例を示します。 この例は政府統計から取ってきたもので、通信機器利用を世帯単位で調査したものです。
require 'glimmer-dsl-libui'
include Glimmer
tbl = [
["20-29","79.0","99.0","41.8","30.9","51.5","4.6","0.8"],
["30-39","74.9","99.0","51.5","44.1","47.3","3.5","0.9"],
["40-49","78.1","97.8","53.0","37.6","43.1","3.6","1.7"],
["50-59","80.2","96.4","46.0","32.4","27.8","3.4","2.4"],
["60-69","75.3","94.3","36.3","26.4","14.3","1.4","2.6"],
["70-79","68.7","86.7","26.5","20.4","9.1","1.9","5.5"],
["80-","67.5","84.5","25.6","22.0","8.8","2.1","5.7"]
]
window('インターネット利用機器調査', 800, 600) {
margined true
vertical_box{
label("政府統計 令和3年通信利用動向調査より") {
stretchy false
}
label("※ 複数回答あり") {
stretchy false
}
horizontal_box {
table {
text_column('世帯主年齢')
text_column('PC')
text_column('携帯スマホ')
text_column('タブレット')
text_column('テレビ')
text_column('ゲーム機')
text_column('その他')
text_column('無回答')
cell_rows tbl
}
}
}
}.show
実行すると次のような画面が現れます。
ここで用いたのは基本的なメソッドのみです。 他のメソッドなどについてはGlimmer-dsl-libuiのドキュメントを参考にして下さい。
蛇足ですが、この統計を見ると携帯・スマホが普及していることがわかります。 これから、ウェブサイトのスマホ対応は必須だということが分かりますね。 政府統計は公開されているので、個人や会社がコストをかけずに統計を手に入れることができ、便利です。 マーケティングのコストダウンにも繋がると思います。
図形描画のためのキャンバスにあたるコントロールがareaコントロールです。 areaの内部にpathコントロール、さらにpathの内部に正方形(square)長方形(rectangle)円(circle)円弧(arc)直線(line)ベジェ曲線(bezier)などを描くことができます。
また、静的な図形(一度描き、そのまま居座り続ける図形)だけでなく、動的な図形(書き直しのきく図形)も可能です。 動的な図形の場合は、その図形のために用いたメモリを描画後に解放し、メモリ効率を良くします。 ここでは、動的な図形は扱いませんが、難しくはないのでドキュメントを参考にしていただけば、すぐに理解できると思います。
図形描画の例を以下に示します。
require 'glimmer-dsl-libui'
include Glimmer
include Math
window('図形描画', 800, 600) {
margined true
vertical_box {
area {
path {
arc(200, 300, 150, 90, 180, true)
fill r: 200, g: 200, b: 255, a: 1.0
}
path {
circle(200, 300, 150)
rectangle(200,100,500,400)
stroke r: 0, g: 0, b: 0
}
path {
polygon(500,300-150, 500-150*cos(PI/6),300+150*sin(PI/6), 500+150*cos(PI/6), 300+150*sin(PI/6))
fill r: 255, g: 200, b: 200, a: 1.0
}
path {
polygon(500,300-150, 500-150*cos(PI/6),300+150*sin(PI/6), 500+150*cos(PI/6), 300+150*sin(PI/6))
stroke r: 0, g: 0, b: 0
}
}
}
}.show
areaはボックスの中に入れて使います。 pathはareaの中に入れ、更にその中にrectangleなどの図形を入れ、描き方(fillまたはstroke)を指定します。
上記のプログラムを実行すると次の画面が現れます。
ここに書いていない図形のメソッドはドキュメントを参考にしてください。 また、アニメーションもできます。 例えばサンプルプログラムの「テトリス」を試してみてください。
Glimmerは画面(View)とモデル(Model: データを保持しているもの)をプレゼンター(Presennter)が管理するMVPの方法をとることができます。
これはRailsなどのMVCと同じ考え方です。
具体的にはビューとモデルのデータに双方向の結合(バインディング)または片方向の結合を設定できます。
片方向の場合はモデルからビューへの方向のみ可能です。
それぞれ<=>
または<=
という演算子を用います。
まず、モデルを用意します。
モデルはRubyのオブジェクトで、ビューに対応させたいデータはattr_accessor
で定義をしておきます。
例えば、インスタンス変数@dataをビューに対応させたいときは
class A
attr_accessor :data
end
このようにします。
それにより、クラスAのインスタンスをa
とすると、a.data
で@data
を参照でき、a.data=
で@data
に代入することができます。
双方向のデータ結合をentryのテキストと行いたいときは
entry {
text <=> [a, :data]
}
のように、右辺は配列で`[オブジェクト, インスタンス変数名のシンボル]とします。 左辺のtextはentryのプロパティ、すなわち入力された文字列です。
片方向のデータ結合は、例えば
label {
text <= [a.data]
}
のようにします。
例としてentryとlabelがオブジェクトaのインスタンス変数@data
とバインディングされたプログラムを紹介します。
require 'glimmer-dsl-libui'
include Glimmer
class A
attr_accessor :data
def initialize
@data = ""
end
end
window('バインディング', 800, 100) {
margined true
a = A.new
vertical_box {
entry {
text <=> [a, :data]
}
label {
text <= [a, :data]
}
}
}.show
同じデータがentryとlabelのテキストと結合されているので、エントリに文字列を打ち込むとラベルにもそれが反映されます。
インストールしたglimmer-dsl-libuiのgemにはexampleフォルダに沢山の例が入っています。 インストール先は次のようにして調べられます。
$ gem Environment
RubyGems Environment:
- RUBYGEMS VERSION: 3.3.7
- RUBY VERSION: 3.1.2 (2022-04-12 patchlevel 20) [x86_64-linux]
- INSTALLATION DIRECTORY: /(ユーザディレクトリ)/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0
... ... ...
... ... ...
INSTALLATION DIRECTORYと書いてあるのがgemのインストール先です。
この例を調べることにより、Glimmerの書き方が理解できます。 実行してみて面白いと思った例を以下にあげてみます。
実行してみて面白いだけでなく、プログラムを見ることによってGlimmerの書き方を習得することができます。
Glimmer-dsl-libuiはShoes4と比べると起動が速く、精神的なストレスがありません。 まだ安定版がないので、使うかどうかを迷うかもしれませんが、開発版の段階でもある程度は実用になると思います。
Glimmerのベースになっているlibuiというライブラリ(こちらも開発段階)はGtk3ベースと書いてありました。 Gtk4になって2年くらいになるので、ぜひともGtk4に対応してもらいたいです。 Gtk3とGtk4ではいろいろな違いがあり、Gtk4はかなり進歩していますから。
なお、Glimmer-dsl-libuiは2022年10月15日の「福岡 mruby Kaigi」で特別賞を受賞しています。 今後の開発に期待したいと思います。
単項マイナスとは 単項マイナスと括弧 括弧なし単項マイナスを許容する場合のBNF calcの場合
パーサ・ジェネレータとは 少し複雑な文法 四則(加減乗除)計算のBNF Racc で実装 クラス定義、BNFの記述部分 ヘッダー、インナー、フッター コンパイルと実行 演算子の優先順位と結合における左右の優先順位 まとめ
StrScanライブラリのドキュメント 字句解析とは StrScanライブラリ StrScanライブラリを使った字句解析 実例
lbtというgemを作って公開してみた lbtはどんなgemか ファイルの配置 lbt.gemspec Rakefile gemのビルド RubyGems.orgへのアップロード 補足・・rake/gempackagetaskサブライブラリについて
文字列のエンコーディングに頭を悩ませることはほとんどなくなりました。 なぜなら、どのアプリ、システムもUTF-8を使うようになったからです。 Rubyでもエンコーディングの問題が起こることはまず無いでしょう。 ですが、今回はエンコーディングの考え方を整理してみたいと思います。
Fiberを書いたときから、次はスレッドを書こうと思っていましたが、時間がかかってしまいました。 その理由は、期待したとおりのスレッドの効果がなかったためです。 今回はそのことを書きますが、これはRubyのスレッドの抱えている問題なのか、自分のやり方が悪いのかははっきりしていません。
Fiberは「ノンプリエンプティブな軽量スレッド」とRubyのマニュアルに記載されています。
今回はRubyプログラムから自動的にドキュメントを作成するRDocについて書きたいと思います。 私はこのことについて、エキスパートではありません。 この記事も、初心者の体験談だと考えてください。
Ruby/Gtkの記事を先日書いたときに、「これはかなり使える」という手応えを感じたので、WordBook(Railsで作った単語帳プログラム)のGTK 4版を作りました。 プログラムは「徒然なるままにRuby」のGitHubレポジトリに置いてあります。 レポジトリをダウンロードし、ディレクトリ_example/...
今回はGTK 3とGTK 4をRubyで使うライブラリについて書きたいと思います。
今回もRubyとGUIのトピックです。 Glimmerを取り上げます。
Rubyはグラフィックについて弱い印象があります。 しかし、グラフィックはデバイスに関することなので、言語そのものには直接の関係はないはずで、あるとすればライブラリです。 今後グラフィック関係のgemが開発されることに期待しましょう。
Rails7におけるシステムテストについて書きます。
前回作ったWordbook(リソースフル)のテストを書いてみます。 RailsのテストはminitestをRails用に拡張したものです。
今回はRailsの慣例に沿った形でWordbookを作り直します。
今回はWordBookの検索と削除についてです。
今回はRailsにおけるデータの作成と保存、そして変更について説明します。 そのベースになるモデルとデータベースの話から始め、appendとchangeの動作について詳しく説明します。
一般に、HTMLは文書の構造を表し、CSSはその体裁(見栄え)を表します。 Railsは最終的にCSSを含むHTML文書を出力するので、この2つについての理解が必須です。 この記事ではとくにCSSの人気ライブラリであるBootstrapを紹介します。 BootstrapはJavascriptも含んでいます。
Rubyの最も人気のあるアプリケーションであるRuby on Railsを取り上げようと思い、書き始めました。 予想してはいましたが、相当な分量になってしまいました。 そのため、何回かに分けて記事にすることにします。 また、対象となる読者のレベルをどうしようかと考えましたが、「徒然Ruby」が基礎的な内容から始ま...
Rubyのライブラリ管理システムのRubygemsとコマンドgemおよびbundlerについて説明します。
minitestについて連続して2回書いてきました。 「minitestはドキュメントが少ない」という人がいますが、私も同感です。 例えば、モックとスタブの説明も少ないです。 そこで、今回はmock.rbのソースコードを参考に、モックの私的ドキュメントを書いてみました。 あくまで私個人の考えであり、minites...
今回もminitestの話です。 mockとstubに焦点をあて説明します。
アプリ作成の記事でminitestを使いました。 今回はminitestについて、また一般にテストについて、私の考えを書こうと思います。
今回はメソッドの呼び出し制限ついて説明します。 呼び出し制限にはpublic、private、protectedの3つがあります。
今回は特異メソッド、特異クラス定義、名前空間、モジュール関数について説明します。
2023/10/29 追記:この記事は新しく書き直しました。 古い記事で使っていたGitHubのCalcが大幅にアップデートされたためです。 そこで、この記事に合うようなプログラムsimple_calcを新たに作りました。 このプログラムは本レポジトリの_example/simple_calcにあります。
if〜elsif〜・・・〜else〜endは皆さん良く使うでしょうか? これは場合分けで良く使われる方法です。 これと同様の制御構造にcase文があります。 Cのswitch文に似ていますが、より強力な機能を持っています。 if-else-endよりも高い能力があるといえます。
Procオブジェクトを生成するメソッドlambdaについて説明します。
今回はブロックを一般化したオブジェクトProcを説明します。
ブロック付きメソッドの作り方を説明します。
モジュールには名前空間とミックスイン(Mix-in)の2つの機能があります。 ここではミックスインについて説明します。
クラスの親子関係
Rubyの演算子とその再定義について書きます。
今回からクラスとインスタンスを定義、生成する方法を説明します
Kernelモジュールのメソッドはどこでも使うことができます。 そのメソッドの中には便利で有用なものが多いです。
ここでは私が便利だと思ったメソッドを紹介します。
実数
今回はシンボルとハッシュについて説明します。
文字列は最も使うオブジェクトのひとつです。 特にウェブ・アプリケーションでは、コンテンツだけでなくHTMLのタグやCSSを含めすべてが文字列です。 Rubyは文字列オブジェクトのメソッドが充実しており、またパターンマッチのための正規表現も充実しています。
配列は、どのプログラミング言語にもあると思います。 複数の要素を一括して扱うことができるのが配列です。 Rubyの配列はメソッドが充実しているので、プログラムを効率的、機能的に書くのに役立ちます。
今回の目標はインスタンスです。 インスタンスを説明するために、ローカル変数と文字列オブジェクトを事前に扱います。
今回はメソッド定義です。 メソッド定義はRubyの核心ですが、今回はトップレベルに限って説明します。 この限定によって、内容はかなり易しくなっています。
ブロックはRubyの特長です。 ブロックのおかげで記述が非常にすっきりと分かりやすくなります。 今回はブロックをイテレータの本体として使う方法を説明します。
ここではRubyの最も基本的なオブジェクトである整数について説明します。
「徒然なるままに」をネットで調べてみると、「することもなく、手持無沙汰なのにまかせてという意味」とありました。 まさに、自分の現状を言い当てた言葉。 しかも、ブログに書くネタもなかなか思いつかない日々。