テキスト描画自体は、cairo::Contextにshow_textというのがあるので簡単だ。
context.move_to(100f64, 200f64);
context.set_source_rgb(0.0, 0.0, 0.0);
context.set_font_size(200.0);
context.show_text("Help");
context.stroke();
再描画の時のために描画領域を求める方法だが、cairo::Contextにtext_extentsがあって、これで得られる。ここには、width, height, x bearing, y bearing, x advance, y advanceが含まれている。このうちx advanceとy advanceが良く分からないが、残りの4つのパラメータで描画領域は計算できるようだ。
始点をx, yとすると、描画域は以下で得られるようだ。
左上: (x + x bearing, y + y bearing) 右上: (x + x bearing + width, y + y bearing) 右下: (x + x bearing + width, y + y bearing + height) 左下: (x + x bearing, y + y bearing + height)
サンプルは、GitHubに置いておいた。
Rustで、任意の処理をはさみこんで、前後に定型処理を入れるようなことをしたい。例えば処理の時間を測って一定時間を超えていたらタイムアウト処理を呼ぶようなのを考えてみる。
struct Foo {
i: i32
}
impl Foo {
fn around_mut<'a, T, F: FnMut(&mut Foo) -> T + 'a>(&mut self, mut f: F) -> T {
let before = self.get_time();
let ret = f(self);
let after = self.get_time();
if after - before > 1000 {
self.timeout()
}
ret
}
fn around<'a, T, F: Fn(&Foo) -> T + 'a>(&self, f: F) ->T {
let ret = f(self);
ret
}
fn timeout(&self) {
}
fn get_time(&self) -> i32 {
222
}
fn inner_mut(&mut self) -> i32 {
111
}
fn outer_mut(&mut self) -> i32 {
self.around_mut(move |this| {
this.inner_mut()
})
}
fn inner(&self) -> i32 {
111
}
fn outer(&self) -> i32 {
self.around(move |this| {
this.inner()
})
}
}
fn main() {
let mut foo_mut = Foo { i: 123 };
let ret_mut = foo_mut.outer_mut();
println!("Ret: {}", ret_mut);
let foo = Foo { i: 222 };
let ret = foo.outer();
println!("Ret: {}", ret);
}
結構難しい... 慣れるものなんだろうか。
Gladeの編集画面に、ボーダとか色の指定とかが無いので、どうするのだろうかと思ったら、どうやらCSSを使うようだ。
CSSは外部ファイルから読むことも、テキストとして与えることも可能だが、一番簡単なのはソースに埋め込んでしまう方法だろう。src/resources/style.cssにファイルを配置して、コードからは以下で読み込める。include_str!()は、コンパイル時に指定ファイルを読んでソースの中に埋め込むマクロだ。
let css_str = include_str!("resources/style.css");
let css_provider = CssProvider::new();
css_provider.load_from_data(css_str).unwrap();
StyleContext::add_provider_for_screen(
&Screen::get_default().unwrap(), &css_provider, 1
);
CSSのセレクタだが、id指定の場合、Glade上のIDではなく「ウィジェット名」が使われるので注意が必要だ。
この場合、CSSでは以下のように指定できる。
#titleLabel {
border: inset 2px;
background-color: red;
}
GTKのウィジェットクラス名を使って全てのラベルに一律に適用することもできる。
GtkLabel {
padding: 3px;
}
Glade上のスタイルクラスを使うこともできる。
この場合、CSSでは以下のように指定できる。
.name {
border: solid 1px;
}
なお、上のリファレンスではラベルに背景色を付ける例が載っているが、試した限りラベルは透明コンポーネントのようで背景色の設定は効かなかった。
サンプルは、GitHubに置いておいた。
第6曲は、これまでとは違って自由な構成になっている。最初は短調で始まり、
このあたりからクレシェンド。うまく行かない現状への怒りを思わせる。
ここから長調となり、過去の幸せな記憶を思い起こすかのような、幻想的で暖かな感じの曲調になる。
ここからは、なんとなく諦めのような、不思議な音型の繰り返しとなって、そのまま曲を閉じる。
う〜む、何か嫌なことでもあったのか。
楽譜の引用はエキエル版から
今日も来た。コメントformのURLを見ているわけではないようだ。
今度は、フォーム内のフィールドのname属性を変更してみた。あと承認待ちのコメントに名前を出さないようにしてみた。
GTK(Cairo)での、線、矩形描画が思ったようにいかずに、少し悩んだのでメモ。
例えば以下のパラメータで矩形を描画した場合:
- 始点: 2, 2
- 終点: 8, 6
- 線の太さ: 2
- 線の色: 青
- フィルの色: 赤
以下のように描画されるようだ。
線の太さは直径なので、2ピクセル未満とすると、サブピクセルになってしまうので、きっちりドットバイドットで表示したければ、最低でも2を指定する必要がある。また、GTKに再描画を依頼する場合、領域の指定は(2, 2) - (8, 6)にすると線の外周部分が漏れてしまうので、線の太さを考慮して、(1, 1) - (9, 7)を指定する必要がある。完全に1ピクセルの線を引きたかったら、線の太さを0にした矩形を描画した方が良さそう。
作品15の2番目も、3部構成になっているが、今度はどちらも長調になっている。第3、4曲での極端なやり方に反省して、幻想的な雰囲気を壊さないように配慮したのかもしれない。
しかし、中間部の右手の多声進行は複雑だ。
エキエル版での謎の箇所として、まず出だし
そして中間部が終わった後の出だし。この2箇所でスラーのかかり方が微妙に違う。
これが意図したものなのか、単なる間違いなのか良く分からない。ちなみに全音だと、どの箇所も以下になっている。
みんな違うし ^^;
今回の演奏では、エキエル版の最初の出だしのスラーのかかり方を正としている。
楽譜引用は、エキエル版と全音版。
コメント・スパムが来ているが、コメントは承認が必要なので全部削除。ただ、削除も面倒なので何か対策ができるか試し中。
とりあえずIPのblacklistをnginxに入れてみたが効果無し。これまでコメントスパムって、WordPressとかの有名どころのアプリケーションを使っているところに、機械的に投げ込んでいるのだと思っていたのだけど、うちのようにフルスクラッチのところにも来るというのは、どういう仕組みなのだろう。formのURLがpost comment的なところを見つけて送っているんだろうか。まさか人力でもあるまいし。
というわけでコメント投稿のURLを変更してみた。Play!だとroutesを変えるだけで良いのでラクチンだ。
作品15も、9に引き続き3つで構成されている。作品15の最初の曲となるこの曲は、第3曲と同じで前後を穏かな長調、中間部を激しい短調のパッセージという形式になっている。ただ第3曲よりも洗練されて、全体に簡潔にまとめらている。
中間部、第2小節はペダルを踏む指示があるけど、スタカーティシモ。どのくらいペダルを踏み込むのかが演奏者に任されている感じ。
楽譜引用はエキエル版
Rustで、Multimap(1つのキーに複数の値を登録できるMap)を実装してみる。後でキーをソート順の取り出したいので、BtreeMapを使って、値にVecを入れる。
use std::collections::BTreeMap;
struct Bag {
map: BTreeMap<i32, Vec<i32>>
}
impl Bag {
fn add(&mut self, key: i32, value: i32) {
match self.map.get_mut(&key) {
None => {
self.map.insert(key, vec!(value));
},
Some(vec) => vec.push(value)
}
}
}
でも、これはコンパイルが通らない。
error[E0499]: cannot borrow `self.map` as mutable more than once at a time
--> /Users/shanai/.emacs.d/rust-playground/at-2017-01-22-230525/snippet.rs:32:17
|
30 | match self.map.get_mut(&key) {
| -------- first mutable borrow occurs here
31 | None => {
32 | self.map.insert(key, vec!(value));
| ^^^^^^^^ second mutable borrow occurs here
...
35 | }
| - first borrow ends here
mutable borrowが2回起きているという警告。まずここでself.mapに対するmutable参照を取得している。
match self.map.get_mut(&key) {
で、このmatchが終了する前に、ここでもう一度self.mapに対するmutable参照を取得している(insertは、mutableなselfが必要)。
None => {
self.map.insert(key, vec!(value));
},
つまりmutableな参照が、2つ存在してしまう瞬間がある。これはRustでは禁止されている。以下がエラーになるのと同じ。
let mut i = 0;
let pi = &mut i;
let pi2 = &mut i;
スコープを分けて、mutable参照が2つ同時に存在する瞬間を無くしてしまえば良い。
impl Bag {
fn add(&mut self, key: i32, value: i32) {
{
match self.map.get_mut(&key) {
None => {},
Some(vec) => {
vec.push(value);
return
}
}
}
self.map.insert(key, vec!(value));
}
}
でも、BtreeMapにはentry()という素敵なメソッドがあるので、これを使うのが簡単。
impl Bag {
fn add(&mut self, key: i32, value: i32) {
self.map.entry(key).or_insert(vec!()).push(value)
}
}
entryはキーを引数に取り、Entryというenumを返す。指定されたキーが無ければVacant、あればOccupiedが返る。or_insert()を呼び出すと、Vacantの場合は、引数に指定された値をmapに格納した上で、その値へのmutable参照を返す。Ocupiedの場合は、値へのmutable参照を返す。なので上のように書けば目的が達成できる。
fn main() {
let mut bag = Bag { map: BTreeMap::new() };
bag.add(1, 10);
bag.add(1, 20);
bag.add(2, 100);
println!("map = {:?}", bag.map);
}
map = {1: [10, 20], 2: [100]}