ActionScript: マウスをイベントリスナーに登録
先ずは下のFlashの円にマウスポインタを合わせたり、クリック・ダブルクリックなどするとそれに伴ったアクションがあります。
2019-01-14追記:Flashのサポート終了に伴い、本ページからFlashへのリンクを削除しました。
このFlashの仕様をコードに書きおこす際、1つのクラスに全ての機能を書き込む方法もありますが(この場合はその方がコード量が少なくなる)、折角なのでOOに基づいたコードを書いてみました。が、OOのパターンを適応途中に飽きたので少し崩しています(ぇー)。
ソースコード
上のFlashは以下の三つのクラスで描かれています。
- Main.as
- Ball.as
- EventMonitor.as
Main.as
MainクラスではマウスイベントをキャッチするBallクラス(後述)とその結果を観測し画面左上に出力するEventMonitorクラス(後述)のインスタンスを生成し画面への出力登録を行っています。ball変数をEventMonitorのコンストラクタに渡しているのはEventMonitorからマウス座標等を取得する為のballインスタンスのメソッドを呼び出すからです。
今回はイベントを発生させるオブジェクトがball一つですので後述のコードにしましたが、複数あるときはパターンに合わせた方が良いと考えます。
package info.yukun
{
import flash.display.Sprite; // タイムラインを使わない場合、MovieClip より軽い
public class Main extends Sprite
{
private var ball:Ball; // Ballクラス型の変数
private var monitor:EventMonitor; // イベントのモニタリング用のクラス
// コンストラクタ: ここからプログラムコードを読んでいく
public function Main():void
{
init();
}
private function init():void
{
ball = new Ball(); // Ballインスタンスを作成
ball.x = stage.stageWidth / 2; // オブジェクトの表示位置(中央)
ball.y = stage.stageHeight / 2;
ball.regEvent(); // マウスイベントリスナーの登録
addChild(ball); // 最上位のSpriteにballオブジェクトを表示
monitor = new EventMonitor(ball);
monitor.x = 0; // 左上に設置
monitor.y = 0;
addChild(monitor);
}
}
}
Ball.as
次に、冒頭のFlashに描かれている青い円を描画するBallオブジェクトを読んでみましょう。先ず、コンストラクタのBallとinit()メソッドで青い円を描画しています。
次にregEvent()メソッドでイベントリスナーの登録手続きを書いています。大抵この規模のコードですとMain.asでball.addEventListener(ホニャララ)とリスナー登録しているコードを散見しますが、オブジェクトの振る舞いはオブジェクト毎に持たせた方がOOぽいし、何だか再利用できそうな希望の片鱗を与えてくれるのでBallクラスにregEvent()メソッドを付けてみました。これはMainクラスから呼び出されることで、ballのマウスのイベントリスナーの一括登録が行われます。
後の二つのメソッド、mousePosition()とgetState()はEventMonitorクラスから呼び出されるものです。このメソッドを通して、画面左上にマウス座標等を表示します。ここでも機能の分離を行っています。すなわち、情報を投げる側(Ball)と観測し表示する側(EventMonitor)です。
package info.yukun
{
import flash.display.Sprite;
import flash.events.MouseEvent; // マウスイベント用
public class Ball extends Sprite
{
private var radius:Number; // 円の半径
private var color:uint; // 色
private var event_state:String; // イベントの状態
public function Ball(radius:Number = 50, color:uint = 0xA3D5FF)
{
this.radius = radius;
this.color = color;
this.event_state = "";
init();
}
public function init():void
{
graphics.beginFill(color);
graphics.drawCircle(0, 0, radius); // 円の描画
graphics.endFill();
}
// イベントリスナーの登録
public function regEvent():void
{
// このオブジェクトにクリックが発生したら登録したメソッド(onMouseEvent)を実行、と読む
addEventListener(MouseEvent.CLICK, onMouseEvent);
doubleClickEnabled = true; // ダブルクリックの検出を可能にする
addEventListener(MouseEvent.DOUBLE_CLICK, onMouseEvent);
addEventListener(MouseEvent.MOUSE_DOWN, onMouseEvent);
addEventListener(MouseEvent.MOUSE_MOVE, onMouseEvent);
addEventListener(MouseEvent.MOUSE_OUT, onMouseEvent);
addEventListener(MouseEvent.MOUSE_OVER, onMouseEvent);
addEventListener(MouseEvent.MOUSE_UP, onMouseEvent);
addEventListener(MouseEvent.MOUSE_WHEEL, onMouseEvent);
}
public function onMouseEvent(event:MouseEvent):void
{
event_state = event.type;
trace(event_state);
switch(event_state) {
case MouseEvent.ROLL_OVER:
this.color = 0x0006FA;
init();
break;
case MouseEvent.ROLL_OUT:
this.color = 0xA3D5FF;
init();
break;
}
}
public function mousePosition():String
{
// この場合、軸の原点は円の中心(オブジェクトのSpriteを基準)
return "("+ Math.floor(mouseX) + ", " + Math.floor(mouseY) + ")";
}
public function getState():String
{
return event_state;
}
}
}
EventMonitor.as
最後になりましたが、マウスイベントの内容やマウス座標の表示を行うEventMonitorクラスです。 ここでのイベントリスナーの登録は、
addEventListener(Event.ENTER_FRAME, onEnterFrame);
ですね。 フレームが更新される毎に、第二引数に登録したonEnterFrame()メソッドを実行します。Event.ENTER_FRAMEはアニメーションさせる時に良く使うプロパティですが、ここではマウス座標やマウスクリックイベント情報を取得し出力する為に使われています。
package info.yukun
{
import flash.display.Sprite;
import flash.events.Event;
import flash.text.*;
public class EventMonitor extends Sprite
{
private var ball:Ball; // 観測オブジェクト
private var monitor_label:TextField; // 表示ラベル
private var TITLE:String;
public function EventMonitor(ball:Ball)
{
this.ball = ball;
init();
}
public function init():void
{
TITLE = "イベントモニター";
monitor_label = new TextField();
monitor_label.text = TITLE;
monitor_label.autoSize = TextFieldAutoSize.LEFT;
monitor_label.selectable = false;
addEventListener(Event.ENTER_FRAME, onEnterFrame);
addChild(monitor_label);
}
private function onEnterFrame(event:Event):void
{
var text:String = TITLE + "\n";
text += "マウス座標(原点はボールの中心): " + ball.mousePosition() + "\n";
text += "発生イベント: " + ball.getState() + "\n";
monitor_label.text = text;
}
}
}
AS3になりJavaやC#に似て格段に扱いやすくなったように感じます。OOはGUIにマッチする設計手法ですしね。 話し変わりますが、以前windows.hを用いたGUIのコードではイベントループで悪戦苦闘してたっけ。今なら多少は上手く書けるかな。どうだろ。
Python: 指定したパスのディレクトリ中のファイル一覧を出力
あるディレクトリから特定のファイルを検索したい場合、探索対象ディレクトリ内のファイルを全て取得する必要があります。今回は、引数にディレクトリを指すパスを指定することによって、そのディレクトリの内容を取得する関数を2つ示します。
ソースコード
# coding: Shift_JIS
import os # osモジュールのインポート
# os.listdir('パス')
# 指定したパス内の全てのファイルとディレクトリを要素とするリストを返す
files = os.listdir('C:\Python25\')
for file in files:
print file
実行結果の一例
DLLs
Doc
include
Lib
libs
LICENSE.txt
lxml-wininst.log
NEWS.txt
PIL-wininst.log
pysqlite-wininst.log
pysqlite2-doc
python.exe
pythonw.exe
README.txt
Removelxml.exe
RemovePIL.exe
Removepysqlite.exe
Scripts
tcl
Tools
w9xpopen.exe
ワイルドカードでリスティング対象を指定
globモジュールのglob()関数の引数内にワイルドカード「*」を含めることが出来ます。これによって、より手軽に目的のファイルを探索することが出来ます。
ソースコード
# coding: Shift_JIS
import glob
# パス内の全ての"指定パス+ファイル名"と"指定パス+ディレクトリ名"を要素とするリストを返す
files = glob.glob('C:\Python25\*.*') # ワイルドカードが使用可能
for file in files:
print file
os.listdir()と異なり、glob.glob()は取得したファイル文字列には先頭に探査ディレクトリ(引数)のパスが付いています。
実行結果の一例
C:Python25LICENSE.txt C:Python25lxml-wininst.log C:Python25NEWS.txt C:Python25PIL-wininst.log C:Python25pysqlite-wininst.log C:Python25python.exe C:Python25pythonw.exe C:Python25README.txt C:Python25Removelxml.exe C:Python25RemovePIL.exe C:Python25Removepysqlite.exe C:Python25w9xpopen.exe
リファレンス
サイトURLの変更のお知らせ
この度、サイトURLを
から、 http://www.yukun.info/ に変更しました。 それに合わせて、RSSも以下のように変更しました。 http://feedproxy.google.com/yukun-blog まぁ、サブドメインをwwwとしました。URL短くなるし良いかなと思って(trumpcodeって一見で何だか分かんないね)。 一応、これまでのインデックスが消えるまでは、旧URLはリダイレクトでこちらに投げています。
Python: 正規表現の基本 - 最長、最短マッチング
直前の文字、メタ文字を繰り返しマッチングさせる量指定記号である「*」「+」「?」などは、テキスト中にその繰り返しパターンがマッチする箇所が複数ある場合は、通常最後にマッチした箇所をオブジェクトに記録します。このような最長マッチングに対して、マッチング箇所が複数の場合に最初にマッチした箇所を記録する最短マッチング方法があります。 最短マッチングを行う量指定記号は最長マッチングの記号の末尾に「?」を付けるだけです。すなわち、最長の量指定が「*」「+」「?」であるのに対して、**最短マッチングは「*?」「+?」「??」**となります。それでは、以下のコードで確認してみましょう。
ソースコード
# coding: Shift_JIS import re # 正規表現を扱うモジュールのインポート # 正規表現のチェックプリント用の関数 def PrintRegMatch(pat, txt): # 書式: re.search(パターン, テキスト) m = re.search(pat, txt) # パターンにマッチしなかった場合はNoneを返す if m != None: print 'パターン: "%s"nテキスト: "%s"nマッチ : する' % (pat, txt) print 'マッチング開始位置:', m.start() print 'マッチング終了位置:', m.end() else: print 'パターン: "%s"nテキスト: "%s"nマッチ: しない' % (pat, txt) print return m # サンプルテキスト txt = '
Hello! World!!
Goodbye World
' # 最長: .* # 最短: .*? PrintRegMatch('
.*
', txt) # 最長: 文字列末尾の
にマッチ PrintRegMatch('
.*?
', txt) # 最短: World!!
の
にマッチ # 最長: .+ # 最短: .+? PrintRegMatch('
.+
', txt) # +?は直前の文字の1文字以上の繰り返し PrintRegMatch('
.+?
', txt) # ↑の控えめマッチング # 最長: .? # 最短: .?? PrintRegMatch('B.?C', 'BCCC') # ?は直前の文字の0 or 1回の繰り返し PrintRegMatch('B.??C', 'BCCC') # ??はその控えめ(ry
re.searchはre.matchと異なり、パターンをテキスト文字列の先頭以外にもマッチさせます。
実行結果
パターン: "
.*
" テキスト: "
Hello! World!!
Goodbye World
" マッチ : する マッチング開始位置: 0 マッチング終了位置: 41 パターン: "
.*?
" テキスト: "
Hello! World!!
Goodbye World
" マッチ : する マッチング開始位置: 0 マッチング終了位置: 21 パターン: "
.+
" テキスト: "
Hello! World!!
Goodbye World
" マッチ : する マッチング開始位置: 0 マッチング終了位置: 41 パターン: "
.+?
" テキスト: "
Hello! World!!
Goodbye World
" マッチ : する マッチング開始位置: 0 マッチング終了位置: 21 パターン: "B.?C" テキスト: "BCCC" マッチ : する マッチング開始位置: 0 マッチング終了位置: 3 パターン: "B.??C" テキスト: "BCCC" マッチ : する マッチング開始位置: 0 マッチング終了位置: 2
リファレンス
Python: set型の集合演算で2つのリスト要素を比較
2つのリストの要素を比較する際、リスト型をset型に変えると「-」「&」などの演算子1つで集合演算できます(AND、OR、NOTとか)。
ソースコード
#!/usr/bin/python
# coding: UTF-8
# リストの比較(by 集合演算)
old_list = ['A', 'B', 'C', 'D', 'E', 'F'] # 古いリスト
new_list = ['A', 'C', 'F', 'G', 'H', 'I'] # 更新された新しいリスト、とする
# 組み込み関数set()を用いて(リストを含む)シーケンス型からset型データを作成
old_set = set(old_list)
new_set = set(new_list)
print 'old_set ==', old_set
print 'new_set ==', new_set
# 差集合: old_setの要素からnew_setに含まれる要素を削除した要素集合
# すなわち、old_setをnew_setに更新した際に、削除された要素集合、とみる
print 'old_set - new_set ==', old_set - new_set
print 'len(old_set - new_set) ==', len(old_set - new_set) # 削除された個数
# old_setをnew_setに更新した際に、追加された要素集合、とみる
print 'new_set - old_set ==', new_set - old_set
print 'len(new_set - old_set) ==', len(new_set - old_set) # 追加された個数
# old_setをnew_setの共通要素集合(更新した際に変更されなかった要素、とみる)
print 'new_set & old_set ==', new_set & old_set
# old_setかnew_setに含まれる要素集合
print 'new_set ^ old_set ==', new_set ^ old_set
# 和集合: old_setとnew_setに含まれる重複の無い要素集合
print 'new_set | old_set ==', new_set | old_set
実行結果
old_set == set(['A', 'C', 'B', 'E', 'D', 'F'])
new_set == set(['A', 'C', 'G', 'F', 'I', 'H'])
old_set - new_set == set(['B', 'E', 'D'])
len(old_set - new_set) == 3
new_set - old_set == set(['I', 'H', 'G'])
len(new_set - old_set) == 3
new_set & old_set == set(['A', 'C', 'F'])
new_set ^ old_set == set(['B', 'E', 'D', 'G', 'I', 'H'])
new_set | old_set == set(['A', 'C', 'B', 'E', 'D', 'G', 'F', 'I', 'H'])
リスト(and シーケンス型)をset型に変更すると要素の順序が失われます。再度リスト型に戻したいときは、list()関数を用います。
>>> set_data = set(['a', 'b', 'c', 'd'])
>>> set_data
set(['a', 'c', 'b', 'd'])
>>> list_data = list(set_data)
>>> list_data
['a', 'c', 'b', 'd']
>>> type(list_data)
<type 'list'>
>>>
話変わりますが、Rubyだと配列のまま加減算ができます(配列オブジェクトが「+」「-」演算子などをサポートしているみたい)。
リファレンス
Python: 文字列の上位型であるシーケンス型の構文 - Sequence[X:Y:Z]
データの順序が存在するデータ型としてシーケンス型があり、文字列型の上位型となっています。このシーケンス型には文字列中の文字の抽出や操作を簡略化する分かりやすい構文がありますので、これを確認してみましょう。
String[X:] はString[X]から末尾までの文字を持つ文字列
#!/usr/bin/python
# coding: UTF-8
s = 'abcdefg'
print 's ', s
print 's[0:] ', s[0:]
print 's[1:] ', s[1:]
print 's[-1:] ', s[-1:]
print 's[-5:] ', s[-5:]
実行結果
s abcdefg
s[0:] abcdefg
s[1:] bcdefg
s[-1:] g
s[-5:] cdefg
String[:Y] は文字列の先頭からString[Y-1]までの文字を持つ文字列
s = 'abcdefg'
print 's ', s
print 's[:3] ', s[:3] # s[0] s[1] s[2]まで
print 's[:-1] ', s[:-1] # Yは-1よりs[(-1)-1]→s[-2]までの文字列
print 's[:-5] ', s[:-5]
実行結果
s abcdefg
s[:3] abc
s[:-1] abcdef
s[:-5] ab
String[::Z] は文字列の先頭から末尾まで Z 間隔で文字を抽出した文字列
s = 'abcdefg'
print 's ', s
print 's[::1] ', s[::1] # 1間隔なのでsと同じ
print 's[::2] ', s[::2]
print 's[::-1]', s[::-1] # 「-」を付けると逆順に辿っていきます
実行結果
s abcdefg
s[::1] abcdefg
s[::2] aceg
s[::-1] gfedcba
文字列の文字順序を逆順にする処理を構文で賄えるのは良いですね。汎用性があって。
上述の複合 - String[X:Y:Z]
s = 'abcdefg'
print 's ', s
print 's[1:3] ', s[1:3]
print 's[:-1:2] ', s[:-1:2]
print 's[-1::-2]', s[-1::-2]
print 's[1:4:2] ', s[1:4:2]
実行結果
s abcdefg
s[1:3] bc
s[:-1:2] ace
s[-1::-2] geca
s[1:4:2] bd
チュートリアル
リファレンス
Python: リストの上位型であるシーケンス型の構文 - Sequence[X:Y:Z]
データの順序が存在するデータ型としてシーケンス型があり、リストの上位型となっています。このシーケンス型には要素の抽出や操作を簡略化する分かりやすい構文がありますので、これを確認してみましょう。
List[X:] はList[X]から末尾までの要素を持つリスト
#!/usr/bin/python
# coding: UTF-8
a = [10, 20, 30, 40, 50]
print 'a ', a
print 'a[0:] ', a[0:]
print 'a[1:] ', a[1:]
print 'a[-1:] ', a[-1:]
print 'a[-5:] ', a[-5:]
実行結果
a [10, 20, 30, 40, 50]
a[0:] [10, 20, 30, 40, 50]
a[1:] [20, 30, 40, 50]
a[-1:] [50]
a[-5:] [10, 20, 30, 40, 50]
List[:Y] はListの先頭からList[Y-1]までの要素を持つリスト
a = [10, 20, 30, 40, 50]
print 'a ', a
print 'a[:3] ', a[:3] # a[0] a[1] a[2]まで
print 'a[:-1] ', a[:-1] # Yは-1よりa[(-1)-1]→a[-2]までのリスト
print 'a[:-5] ', a[:-5]
実行結果
a [10, 20, 30, 40, 50]
a[:3] [10, 20, 30]
a[:-1] [10, 20, 30, 40]
a[:-5] []
List[::Z] はListの先頭から末尾まで Z 間隔で要素を抽出したリスト
a = [10, 20, 30, 40, 50]
print 'a ', a
print 'a[::1] ', a[::1] # 1間隔なのでaと同じ
print 'a[::2] ', a[::2]
print 'a[::-1]', a[::-1] # 「-」を付けると逆順に辿っていきます
実行結果
a [10, 20, 30, 40, 50]
a[::1] [10, 20, 30, 40, 50]
a[::2] [10, 30, 50]
a[::-1] [50, 40, 30, 20, 10]
リストの要素順序を逆順にする処理を構文で賄えるのは良いですね。
上述の複合 - List[X:Y:Z]
a = [10, 20, 30, 40, 50]
print 'a ', a
print 'a[1:3] ', a[1:3]
print 'a[:-1:2] ', a[:-1:2]
print 'a[-1::-2]', a[-1::-2]
print 'a[1:4:2] ', a[1:4:2]
実行結果
a [10, 20, 30, 40, 50]
a[1:3] [20, 30]
a[:-1:2] [10, 30]
a[-1::-2] [50, 30, 10]
a[1:4:2] [20, 40]
チュートリアル
リファレンス
Python: 10進数整数を2進数文字列に変換する関数
2進数文字列を10進数整数に変換する関数int()はありますが、
>>> int('1011', 2)
11
その逆の、10進数整数を2進数文字列に変換する関数が(Python2.5では)見当たらなかったので、書いてみました。
ソースコード
#!/usr/bin/python
# coding: UTF-8
import math, string
# 10進数整数を2進数文字列に変換する関数
# decimal : 10進数整数
# press : 上位桁の0を切り詰めるフラグ
def toBinary(decimal, press=True):
if decimal == 0: return '0'
bin_str = ""
i = 31
while i >= 0:
bi = int((decimal & int(math.pow(2, i))) >> i)
bin_str += str(bi)
i -= 1
if press:
try:
bin_str = bin_str[bin_str.index('1'):]
except ValueError:
print 'error'
bin_str = '0'
return bin_str
bin_arr = [toBinary(i) for i in range(21)]
print '10進数t2進数'
for i in range(len(bin_arr)):
print '%2dt%5s' % (i, bin_arr[i])
Pythonでは明示的に型変換する必要がありますので、所々int()、str()を用いて型直ししています。
実行結果
10進数 2進数
0 0
1 1
2 10
3 11
4 100
5 101
6 110
7 111
8 1000
9 1001
10 1010
11 1011
12 1100
13 1101
14 1110
15 1111
16 10000
17 10001
18 10010
19 10011
20 10100
追記: 32bit以上の整数を扱う場合
参考: 10進数を2進数と16進数に変換する - gan2 の Ruby 勉強日記 ↑の記事で、データサイズにかかわらず処理できるRubyプログラムがありましたので、参考させていただきました。
def toBinary2(decimal):
if decimal == 0: return '0'
bin_str = ""
while decimal > 0:
bin_str += str(decimal % 2)
decimal >>= 1
return bin_str[::-1]
実行結果は上と同じです。
リファレンス
Python: リスト中の文字列を大文字⇔小文字に変換
文字列を比較する際に、大文字・小文字を区別したくない場合があります。その時は、比較する文字列を大/小文字列のどちらかに統一しておく、という手があります。Pythonでは大文字・小文字変換メソッドlower()、upper()はstringオブジェクトに組み込まれています。 今回は、その使い方と実際に使用する状況に近いデータ構造、ここでは変換対象文字列がリスト中の要素である場合を想定し、for文とリストコンプリヘンション(リスト内包表記)の両表記を以下に示します。
ソースコード
#!/usr/bin/python
# coding: UTF-8
# リスト中の文字列要素を大文字⇔小文字変換
str_atog = "ABCDEFG"
str_hton = "hijklmn"
# lower(), upper()メソッドの使い方
print "大文字(列) %s を小文字(列) %s に変換" % (str_atog, str_atog.lower())
print "小文字(列) %s を大文字(列) %s に変換" % (str_hton, str_hton.upper())
print
arr = ['And', 'Begin', 'Code', 'Double']
arr2 = ['end', 'flag', 'gem', 'halt']
# for文で小文字[大文字](列)を要素とするリストを生成
n_arr = []
for str in arr:
n_arr.append(str.lower())
print n_arr
n_arr2 = []
for str in arr2:
n_arr2.append(str.upper())
print n_arr2, 'n'
# リストコンプリヘンションで小文字[大文字](列)を要素とするリストを生成
print [str.lower() for str in arr]
print [str.upper() for str in arr2]
実行結果
大文字(列) ABCDEFG を小文字(列) abcdefg に変換
小文字(列) hijklmn を大文字(列) HIJKLMN に変換
['and', 'begin', 'code', 'double']
['END', 'FLAG', 'GEM', 'HALT']
['and', 'begin', 'code', 'double']
['END', 'FLAG', 'GEM', 'HALT']
List Comprehensions ならワンライナーで書けるってのは地味に良いですね。 話変わりますが、Rubyにもイテレータやブロックを用いた簡略記法がありましたね。アレはアレで、応用しやすいものです。
