2016/01/12(火) [n年前の日記]
#1 [dxruby][dxrubyws] DXRubyWSのチュートリアル文書に従って試してみる。その2
_DXRubyWS
の、
_doc/tutorial02.txt
を写経して動作確認してみる。
◎ 最も簡単なコード。 :
_tuto21.rb
標準GUIを使う時は、
WS::WSWindow.new() でウインドウを作って、WS.desktop.add_control() で画面に登録しているのかな。たぶん。
require_relative './lib/dxrubyws' require_relative './lib/standardgui' WS.desktop.add_control(WS::WSWindow.new(100,100,300,200,"TutorialWindow")) Window.loop do WS.update endウインドウが表示された。マウスでドラッグ移動、サイズ変更ができるし、閉じるボタンを押せばウインドウを閉じることもできる。
標準GUIを使う時は、
require_relative './lib/standardgui'を、最初のほうに書いておくものらしい。
WS::WSWindow.new() でウインドウを作って、WS.desktop.add_control() で画面に登録しているのかな。たぶん。
◎ ボタンを配置して、ボタンを押したら反応させる。 :
まずは、ボタンを配置。
_tuto22.rb
tutorial02.txt には…。
ボタンに反応させてみる。
_tuto23.rb
ボタンがクリックされたら ―― :click シグナルが来たら、処理がされるように設定されてる。
_tuto22.rb
require_relative './lib/dxrubyws' require_relative './lib/standardgui' # ウインドウを生成 window = WS::WSWindow.new(100,100,300,200,"TutorialWindow") # 画面に登録 WS.desktop.add_control(window) # ボタンを生成 button = WS::WSButton.new(10,10,100,20,"TutorialButton") # ウインドウ(のクライアント領域)に登録 window.client.add_control(button) Window.loop do WS.update endボタンを表示することができた。
tutorial02.txt には…。
- WSWindowオブジェクトはコンテナの集合体。タイトルバーとクライアント領域に分かれてる。
- ユーザがコントロールを配置するのはクライアント領域のほう。
- クライアント領域は、WSWindow#clientでアクセスすることができる。
ボタンに反応させてみる。
_tuto23.rb
require_relative './lib/dxrubyws' require_relative './lib/standardgui' window = WS::WSWindow.new(100,100,300,200,"TutorialWindow") WS.desktop.add_control(window) button = WS::WSButton.new(10,10,100,20,"TutorialButton") window.client.add_control(button) # ボタンをクリックしたときに呼ばれる処理を登録 button.add_handler(:click) do # ボタンの表示位置を、右下に10ドット変更する button.x += 10 button.y += 10 end Window.loop do WS.update endボタンを押すと、ボタンの表示位置が変わっていく。たしかに、ボタンが反応してる。
ボタンがクリックされたら ―― :click シグナルが来たら、処理がされるように設定されてる。
◎ TutorialWindowクラスを作ってみる。 :
_tuto24.rb
これで、 _tutorial02.txt の内容を一通り試せた。
require_relative './lib/dxrubyws' require_relative './lib/standardgui' module WS class TutorialWindow < WSWindow def initialize(*args) super button = WS::WSButton.new(10,10,100,20,"TutorialButton") client.add_control(button, :btn) button.add_handler(:click, self.method(:on_click)) end def on_click(obj, tx, ty) client.btn.x += 10 client.btn.y += 10 end end end window = WS::TutorialWindow.new(100,100,300,200,"TutorialWindow") WS.desktop.add_control(window) Window.loop do WS.update endtuto23.rb と同じことを、クラスを作ることで実現できた。
これで、 _tutorial02.txt の内容を一通り試せた。
[ ツッコむ ]
#2 [dxruby][dxrubyws] DXRubyWSで画像ボタンを表示してみる
DXRubyWS上で、ツールバーだか、ツールボックスだか、そんな感じの表示をしてみたい。そのためには、最低限、画像ボタンが表示できないといけない。画像ボタンを表示する方法はあるのかな。調べてみる。
◎ 画像ボタンを1つだけ表示。 :
_sample/minsample.rb
を眺めると、WS::WSImageButton なるものがあるらしい。おそらくコレが、名前からして、画像ボタンなのだろう…。
_lib/StandardGUI/button.rb の中に、WSImageButton の定義がある。ソレによると…。
サンプルソースを書いてみることにしよう。以下の画像を使って、画像ボタンを表示してみる。この画像は、CC0 / Public Domain ってことで。
_imagebutton_test.rb
マウスカーソルをボタンの上に持ってくると、説明のためのバルーン表示(?)までしてくれるのだな…。素晴らしい。
けど、マウスカーソルでバルーン表示が隠れちゃって、文字が読めないから、イラッとするのだな…。微妙に残念。バルーンの表示位置については、改善が必要な予感。
_lib/StandardGUI/button.rb の中に、WSImageButton の定義がある。ソレによると…。
WS::WSImageButton.new(tx, ty, image, width, height, caption) x : 表示位置 x y : 表示位置 y image : 画像オブジェクト(DXRuby の Imageオブジェクト) width : 横幅。nil なら自動調整 height : 縦幅。nil なら自動調整 caption : キャプション文字列こんな感じなのだろう。たぶん。おそらく。
サンプルソースを書いてみることにしよう。以下の画像を使って、画像ボタンを表示してみる。この画像は、CC0 / Public Domain ってことで。
_imagebutton_test.rb
# ImageButton(画像ボタン)を表示してみる require_relative 'lib/dxrubyws' require_relative 'lib/standardgui' module WS # 画像ボタンを表示してみるウインドウのクラス class ImageButtonWindow < WSWindow # コンストラクタ。初期化処理 def initialize(*args) super # 画像を用意する。アイコンが、8x3個、並んでる imgs = Image.loadTiles("toolbar.png", 8, 3) # 画像ボタンを生成 # 引数として、(x, y, Imageオブジェクト, ボタン横幅, ボタン縦幅, caption) を渡す # 横幅、縦幅の指定が nil なら、画像サイズ+αで調整してくれる x, y = 10, 10 w, h = nil, nil imgbtn = WS::WSImageButton.new(x, y, imgs[1], w, h, "Open") # 画像ボタンをクライアント領域に追加 client.add_control(imgbtn, :btn) # ボタンが押された時の処理を設定 imgbtn.add_handler(:click, self.method(:on_click)) end # ボタンを押した時の処理 def on_click(obj, tx, ty) # ファイル選択ダイアログを開いてみる filter = [ ["PNGファイル(*.png)", "*.png"], ["すべてのファイル(*.*)", "*.*"] ] filepath = Window.openFilename(filter, "ファイルを選択してください") unless filepath # キャンセルされた puts "Cancel." else # ファイルが選ばれた puts filepath end end end end # ウインドウを生成 window = WS::ImageButtonWindow.new(8, 8, 320, 96, "ImageButtonWindow") # 画面に追加 WS.desktop.add_control(window) Window.loop do WS.update end画像ボタンが表示できた。
マウスカーソルをボタンの上に持ってくると、説明のためのバルーン表示(?)までしてくれるのだな…。素晴らしい。
けど、マウスカーソルでバルーン表示が隠れちゃって、文字が読めないから、イラッとするのだな…。微妙に残念。バルーンの表示位置については、改善が必要な予感。
◎ 画像ボタンをたくさん表示。 :
画像ボタンが表示できたので、ツールバーだか、ツールボックスだか、そんな感じのウインドウも表示してみたい。
_imagebutton_test2.rb
ウインドウのサイズを、値を直接指定して決めてるあたりが、なんだかダサイ…。
_imagebutton_test2.rb
# ImageButton(画像ボタン)を表示して、ツールボックスっぽく並べてみる require_relative 'lib/dxrubyws' require_relative 'lib/standardgui' module WS # ツールボックスっぽく表示してみるウインドウクラス class ToolBoxWindow < WSWindow # コンストラクタ。初期化処理 def initialize(*args) super # アイコン画像を読み込む。アイコンが、8x3個、並んでる imgs = Image.loadTiles("toolbar.png", 8, 3) # アイコン種類を定義 icon_list = [ # 画像番号, caption, 押された時の処理名 [0, "New", :create_new_data], [1, "Open", :load_data], [2, "Save", nil], [3, "Export", nil], [11, "Zoom +", nil], [12, "Zoom -", nil], [13, "Brush Size +", nil], [14, "Brush Size -", nil], [17, "Swap Fg/Bg Color", nil], [16, "Grid on/off", nil], [15, "Undo", nil], [4, "Pen", nil], [5, "Erase", nil], [6, "Line", nil], [7, "Rectangle", nil], [8, "Rectangle Fill", nil], [9, "Fill", nil], [10, "Text", nil], ] # アイコンの数だけループする x = 0 y = 0 i = 0 icon_list.each do |d| idx, caption, job_sym = d # マウスカーソルでバルーン表示が隠れてしまうので、少し修正 caption = " " + caption + " " # 画像ボタンを生成 # 引数として、(x, y, Imageオブジェクト, ボタン横幅, ボタン縦幅, caption) を渡す # 横幅、縦幅の指定が nil なら、画像サイズ+αで調整してくれる imgbtn = WS::WSImageButton.new(x, y, imgs[idx], nil, nil, caption) # 画像ボタンをクライアント領域に追加 client.add_control(imgbtn, :btn) if job_sym != nil # ボタンが押された時の処理を設定 imgbtn.add_handler(:click, self.method(job_sym)) end i += 1 # 次のボタンの表示位置を決める if i % 11 == 0 x = 0 y += imgbtn.height else x += imgbtn.width end end end # Newボタンを押した時の処理 def create_new_data(obj, tx, ty) puts "create new data" end # Openボタンを押した時の処理 def load_data(obj, tx, ty) # ファイル選択ダイアログを開いてみる filter = [ ["PNGファイル(*.png)", "*.png"], ["すべてのファイル(*.*)", "*.*"] ] filepath = Window.openFilename(filter, "ファイルを選択してください") unless filepath # キャンセルされた puts "Cancel." else # ファイルが選択された puts filepath end end end end # ツールボックスウインドウを生成 window = WS::ToolBoxWindow.new(8, 8, 320, 96, "ToolBoxWindow") # 画面に追加 WS.desktop.add_control(window) Window.loop do WS.update endそれらしい表示ができた。
ウインドウのサイズを、値を直接指定して決めてるあたりが、なんだかダサイ…。
- ボタンをずらずら並べていったら、自動でウインドウサイズも変わってほしいし、
- 逆に、ウインドウサイズを変更したら、ボタンが横に並ぶ個数も自動で変わったりしてほしい。
[ ツッコむ ]
#3 [dxruby][dxrubyws] DXRubyWSでチェックボックスを表示してみる
DXRubyWS で、チェックボックスを表示してみたい。
_sample/minsample.rb を眺めると、WS::WSCheckBox なるものがある。名前からして、コレがチェックボックスだな…。
さらに、 _lib/StandardGUI/checkbox.rb を眺めると…。
_checkbox_test.rb
チェックを入れたり外したりすると、on_change メソッドが呼ばれて、キャプションとチェック状態が標準出力に出力される。
_sample/minsample.rb を眺めると、WS::WSCheckBox なるものがある。名前からして、コレがチェックボックスだな…。
さらに、 _lib/StandardGUI/checkbox.rb を眺めると…。
WS::WSCheckBox.new(tx, ty, width, caption) tx : 表示位置 x ty : 表示位置 y width: 横幅 caption : キャプション文字列他にも…。
- checked てのが、チェックされてるかされてないかを保持している変数らしい。
- 状態が変化すると(チェックが入れられたり外されたりすると)、:change というシグナルが来るっぽい。
_checkbox_test.rb
# DXRubyWSでチェックボックスを表示してみるテスト require_relative 'lib/dxrubyws' require_relative 'lib/standardgui' module WS # チェックボックスを表示してみるウインドウクラス class CheckBoxWindow < WSWindow # コンストラクタ。初期化処理 def initialize(*args) super @chkbox = [] chkbox_list = [ "Character", "Fg Color", "Bg Color", ] x, y = 8, 8 w, h = 0, 0 chkbox_list.each do |caption| # チェックボックスを生成 # (x, y, width, caption) を渡す cb = WS::WSCheckBox.new(x, y, 120, caption) # あらかじめチェックを入れておく cb.checked = true # クライアント領域に追加 client.add_control(cb, :chkbox) # 状態が変更された時の処理を登録 cb.add_handler(:change, self.method(:on_change)) # 後で参照するかもしれないから一応記憶しておく @chkbox.push(cb) # 次の表示位置を求める y += cb.height + 4 end end # 状態が変更された時の処理 def on_change(obj, checked) puts "#{obj.caption}, #{checked}" end end end # ウインドウを生成して画面に登録 window = WS::CheckBoxWindow.new(8, 8, 120, 120, "CheckBoxWindow") WS.desktop.add_control(window) Window.loop do WS.update endそれらしく表示された。
チェックを入れたり外したりすると、on_change メソッドが呼ばれて、キャプションとチェック状態が標準出力に出力される。
[ ツッコむ ]
#4 [vine][linux] Apache2にDOS攻撃対策のモジュールを入れてみたり
最近、たまに、自宅サーバにDOS攻撃っぽいアクセスがあって、自宅サーバが無反応になってしまう時があるのです。
DOS攻撃と言っても、おそらくはSPAM業者が、投稿可能なページだの、リンク元が残るページだのを見つけて、宣伝URLを書き込もうと大量アクセスして失敗してるとかそんな感じだろうと勝手に想像してるのですが。何にせよ、HTTPサーバも含めて、自宅サーバが沈黙しちゃうのは困るわけで。
何か対策は無いのかとググってみたら、Apache2 に mod_evasive なるモジュールを入れると対策らしきことができるそうで。
_Apache,Linux - ApacheのDOS攻撃対策 - Qiita
_Apache DoS攻撃にそなえる | Developers.IO
てなわけで、自宅サーバ、Vine Linux 6.x にもインストールしてみたり。
wget でソースを入手して、tar で解凍して、解凍したフォルダに入って。
ここから、apxs という、apache にモジュールを追加するためのツールを使って、コンパイルして .so を作ってモジュール追加をするのだけど。
*1
ここで、ちょっとハマってしまった。今現在の Vine Linux上で apxs を使う時は、/usr/sbin/apxs ではなく、/usr/bin/apxs を使わないとダメなようで。前者は apache 1.x 用で、後者が apache 2.x 用なのではないかと。たぶん。分かんないけど。単に apxs -i -a -c mod_evasive20.c と打ってみたら、エラーがバンバン出て困り果ててしまった。/usr/bin/apxs と打たないとダメ。
/var/log に mod_evasive というフォルダを作って、そこにログを入れることにする。
/etc/apache2/conf/httpd.conf に設定を追加。
設定ファイルに問題が無いかチェック。
apache2 を再起動。
DOS攻撃と言っても、おそらくはSPAM業者が、投稿可能なページだの、リンク元が残るページだのを見つけて、宣伝URLを書き込もうと大量アクセスして失敗してるとかそんな感じだろうと勝手に想像してるのですが。何にせよ、HTTPサーバも含めて、自宅サーバが沈黙しちゃうのは困るわけで。
何か対策は無いのかとググってみたら、Apache2 に mod_evasive なるモジュールを入れると対策らしきことができるそうで。
_Apache,Linux - ApacheのDOS攻撃対策 - Qiita
_Apache DoS攻撃にそなえる | Developers.IO
てなわけで、自宅サーバ、Vine Linux 6.x にもインストールしてみたり。
wget でソースを入手して、tar で解凍して、解凍したフォルダに入って。
wget http://www.zdziarski.com/blog/wp-content/uploads/2010/02/mod_evasive_1.10.1.tar.gz tar zxvf mod_evasive_1.10.1.tar.gz cd mod_evasive
ここから、apxs という、apache にモジュールを追加するためのツールを使って、コンパイルして .so を作ってモジュール追加をするのだけど。
*1
ここで、ちょっとハマってしまった。今現在の Vine Linux上で apxs を使う時は、/usr/sbin/apxs ではなく、/usr/bin/apxs を使わないとダメなようで。前者は apache 1.x 用で、後者が apache 2.x 用なのではないかと。たぶん。分かんないけど。単に apxs -i -a -c mod_evasive20.c と打ってみたら、エラーがバンバン出て困り果ててしまった。/usr/bin/apxs と打たないとダメ。
# /usr/bin/apxs -i -a -c mod_evasive20.cなんか色々メッセージが表示されてるけど、追加されたっぽい。
/var/log に mod_evasive というフォルダを作って、そこにログを入れることにする。
mkdir /var/log/mod_evasivechown か chmod も必要なのだろうか。
/etc/apache2/conf/httpd.conf に設定を追加。
# この行は自動で追加されているはず LoadModule evasive20_module /usr/lib/apache2/modules/mod_evasive20.so # 同一ページに2秒間で3回アクセスされたら、 # あるいは、同一サイトに1秒間で30回アクセスされたら、 # 180秒間ブロックする例 <IfModule mod_evasive20.c> DOSHashTableSize 3097 DOSPageCount 3 DOSSiteCount 30 DOSPageInterval 2 DOSSiteInterval 1 DOSBlockingPeriod 180 DOSWhitelist 127.0.0.1 DOSWhitelist 192.168.1.* DOSLogDir "/var/log/mod_evasive" DOSEmailNotify "-s 'DoS Alert' hoge@fuga.com" </IfModule>
設定ファイルに問題が無いかチェック。
# apache2 -t Syntax OK
apache2 を再起動。
# service apache2 restart
◎ テストスクリプトが動かない。 :
mod_evasive には、test.pl というテスト用スクリプトがついてきているのだけど、コレがどうもうまく動かない。
Windowsが動いてる別PCに持ってきて、アクセス先のURL? URI? を修正して、AcivePerl を使って動かしてみたものの、いきなり最初から403しか返ってこない。変だな…。ホワイトリストに入れてるから、200が返ってくるはずだけど…。
自宅サーバ上で、ホワイトリスト行を無効にして apache2 を再起動してから test.pl を走らせてみたものの、これも最初から403。200なんて一度も出てこない。何故。
かといって、ブラウザでアクセスできないかというとそんなことはなく。フツーにページが見れてるわけで。
test.pl の中身は、こんな感じで。
サーバ側のログを眺めてみたけど、別のログに記録が残っていて、首を捻ったり。test.pl は、"GET /?0 HTTP/1.0" とか "GET /?1 HTTP/1.0" という形でなんかくれよーくれくれくれー、とアクセスしてるわけだけど。その手のアクセスは、Nimdaウイルス等からアクセスされた時に残るログのほうに記録されてる。なんでそっちに行っちゃうんだ…?
_2004/04/19 にやってた設定が効いてるみたい。ホストが不明なアクセスは隔離するようにしていることで、こういうことになっている、のかも。よく分かってないけど。
Windowsが動いてる別PCに持ってきて、アクセス先のURL? URI? を修正して、AcivePerl を使って動かしてみたものの、いきなり最初から403しか返ってこない。変だな…。ホワイトリストに入れてるから、200が返ってくるはずだけど…。
自宅サーバ上で、ホワイトリスト行を無効にして apache2 を再起動してから test.pl を走らせてみたものの、これも最初から403。200なんて一度も出てこない。何故。
かといって、ブラウザでアクセスできないかというとそんなことはなく。フツーにページが見れてるわけで。
test.pl の中身は、こんな感じで。
# test.pl: small script to test mod_dosevasive's effectiveness use IO::Socket; use strict; for(0..100) { my $response; my $SOCKET = new IO::Socket::INET( Proto => "tcp", PeerAddr => "127.0.0.1:80"); if (! defined $SOCKET) { die $!; } print $SOCKET "GET /?$_ HTTP/1.0\n\n"; $response = <$SOCKET>; print $response; close($SOCKET); }もちろん、PeerAddr => "127.0.0.1:80" は書き換えて試してるけど。何がおかしいんだろう…。
サーバ側のログを眺めてみたけど、別のログに記録が残っていて、首を捻ったり。test.pl は、"GET /?0 HTTP/1.0" とか "GET /?1 HTTP/1.0" という形でなんかくれよーくれくれくれー、とアクセスしてるわけだけど。その手のアクセスは、Nimdaウイルス等からアクセスされた時に残るログのほうに記録されてる。なんでそっちに行っちゃうんだ…?
_2004/04/19 にやってた設定が効いてるみたい。ホストが不明なアクセスは隔離するようにしていることで、こういうことになっている、のかも。よく分かってないけど。
*1: apxs を使う際、Linuxディストリによっては、httpd-devel だか apache2-devel だかのパッケージが必要になる時もあるらしいので、apt-get だか yum だかでインストールしておく必要があるのかもしれないがよくわかってない。
[ ツッコむ ]
以上、1 日分です。