Clipoli 0.0.0

IronPython を Windows 7 から削除の予定。
言語としては素晴らしいのだがなにせ遅い、我慢するのはもう限界だ。
さて困った、こいつは自分で利用しているからなんとかしなければ。

NotifyIcon to use from IronPython

C# で作り変えするしか無い、いや C++ でも作れるけど何故かやりたくない。
WindowsForm を使うので同じなんだが .NET で作りたいので。

せっかく作り変えるのだし exe にまとめるのだし、もう少し拘りたい。

まずはアイコンを埋め込みしたい、これは Python じゃ無理だったので。
しかし Form は使わないので this.Icon からトレイ用のアイコンを取得できない。

Icon.ExtractAssociatedIcon メソッド (System.Drawing)

なんだ、exe からアッサリ取得できるみたい。
ということは自分自身の exe 名を指定だけでイケそうだ。
アイコンの指定は csc.exe への引数で埋め込みできるもんね。

どうでもいいかもしれないけど exe 名の名前空間に収めたいな。
exe 名は、minipoli 同様なミニアプリだから clipoli にしよう。

そう、Windows 用の新規アプリで公開してしまおうと、三年ぶりか。
Cinema とかのもう自分が使っていないアプリの公開を終了するか悩む所。

そうそう、バージョン情報ダイアログも追加しなきゃ。
これは SeeMe v4 の時に作ったのを持ってきて、って SeeMe v4 は WPF だ。
流用すると WindowsForm と混在になるので名前空間がややこしくなる…

しかたがない、 Form でテキトーに作ってみるか。

about.cs

using System;
using System.Windows;
using System.Windows.Forms;
using System.Drawing;

namespace Clipoli
{
	class About : Form
	{
		public About(Icon icon)
		{
			this.Text = "About Clipoli";
			this.Icon = icon;
			this.Width = 300;
			this.Height = 200;
			var label = new Label();
			label.Location = new Point(10, 10);
			label.Text = "情報";
			this.Controls.Add(label);
			var button = new Button();
			button.Location = new Point(10, 100);
			button.Text = ("OK");
			button.Click += on_ok;
			this.Controls.Add(button);
		}
		void on_ok(object sender, EventArgs e)
		{
			Close();
		}
	}
}

WindowsForm が嫌いだからといえ我ながらテキトーすぎる。
それはそれとしてメインソース。

mainsrc.cs

using System;
using System.IO;
using System.Windows;
using System.Windows.Forms;
using System.Drawing;

namespace Clipoli
{
	class TrayIcon
	{
		private NotifyIcon _icon;
		public TrayIcon()
		{
			var menu = new ContextMenu();
			menu.MenuItems.Add("メモ帳", on_notepad);
			menu.MenuItems.Add("クリップボード", on_clipboard);
			menu.MenuItems.Add("-");
			menu.MenuItems.Add("バージョン情報", on_about);
			menu.MenuItems.Add("-");
			menu.MenuItems.Add("終了", on_exit);
			// create tray icon
			_icon = new NotifyIcon();
			_icon.ContextMenu = menu;
			_icon.Icon = Icon.ExtractAssociatedIcon("clipoli.exe");
			_icon.Text = "Description Text";
			_icon.Visible = true;
		}
		private void on_notepad(object sender, EventArgs e)
		{
			System.Diagnostics.Process.Start("notepad.exe");
		}
		private void on_clipboard(object sender, EventArgs e)
		{
			try
			{
				Clipboard.SetText("あずにゃん");
				System.Media.SystemSounds.Beep.Play();
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}
		private void on_about(object sender, EventArgs e)
		{
			var about = new About(_icon.Icon);
			about.ShowDialog();
		}
		private void on_exit(object sender, EventArgs e)
		{
			_icon.Visible = false;
			Application.Exit();
		}
	}
	class __main__
	{
		[STAThread]
		public static void Main(string[] args)
		{
			var n = new TrayIcon();
			Application.Run();
		}
	}
}

アイコンは IronPython 用に作ったのを仮で置いてビルドバッチ。
WindowsForm で .net 4.0 は馬鹿馬鹿しいので 3.5 の csc exe で。

build.bat

C:\Windows\Microsoft.NET\Framework\v3.5\csc.exe ^
/reference:System.Windows.Forms.dll ^
/reference:System.Drawing.dll ^
/target:winexe ^
/out:clipoli.exe ^
/win32icon:icon.ico ^
/optimize+ ^
*.cs
PAUSE

まとめて ZIP
clipoli000.zip

まだテスト段階だしこんな感じでいいだろう。
動かしてみる、まあ IronPython 作の時とほぼ同じだし当然動くか。

問題はトレイアイコンの右クリックしか受け付けないことなんだよな。
自分で使うだけの分にはそれでいいのだが公開するとなると…

というか、ミニノートで作るのが少々辛くなってきた。
意地をはってもしかたがないし、もう少しマシな Windows を買おうかなと。

Fake System.IO

System.IO.File.ReadLines は .NET 3.5 や mono で使えない。
だったら自分で作ってしまえ、と思ったのでやってみた。

どうでもいいけど gmcs も csc.exe と同じオプションが使えるんだね。
/out:FILENAME は -out:FILENAME みたくハイフンにするだけ。

いや、yield を使ったことが無いのでやってみようかと。
こんな強引というよりアホなメソッド追加なんてできるのかな?

using System;
using System.Collections.Generic; //IEnumerable

namespace System.IO
{
    public static partial class File
    {
        public static IEnumerable ReadLines(string path)
        {
            using(var sr = new System.IO.StreamReader(path))
            {
                string buf;
                while ((buf = sr.ReadLine()) != null)
                {
                    yield return buf;
                }
            }
        }
    }
}

File クラス (System.IO)
File クラスは partial class では無いみたいだけど。

コンフリクトか、やっぱりダメだった。
Windows でもやってみたけど同じ、こんなことをやろうと考えるほうがおかしい。
しかたがない、名前空間には追加できるはずだから違うクラス名にしてみる。

fakeio.cs

using System;
using System.Collections.Generic; //IEnumerable

namespace System.IO
{
    public static class FakeFile
    {
        public static IEnumerable ReadLines(string path)
        {
            using(var sr = new System.IO.StreamReader(path))
            {
                string buf;
                while ((buf = sr.ReadLine()) != null)
                {
                    yield return buf;
                }
            }
        }
    }
}

readlines.cs

using System;
using System.IO;

class __main__
{
    [STAThread]
    public static void Main(string[] args)
    {
        if (args.Length == 0)
        {
            Console.WriteLine("no arg...");
            return;
        }
        int n = 0;
        foreach (var line in FakeFile.ReadLines(args[0]))
        {
            n++;
            Console.WriteLine("{0}: {1}", n.ToString("000"), line);
        }
    }
}

で、cat.exe という実行ファイルを作るコマンド。

gmcs -out:cat.exe *.cs

何故か exe という拡張子を付けないとエラーになる。
Linux でも強制するんかい、まぁ解りやすいと思えばいいし。
アスタリスクでディレクトリ内をまるごとコンパイルしてくれるのはありがたい。

なるほど、yield はこういう時に使うと確かに便利だ。
C#4.0 の ReadLines って上記を自作する以上のメリットがあるのかな?

Default argument and ReadLines

何を今頃なのだが C# 4.0 を調べてみた。
なんと 4.0 では関数にデフォルト引数を指定できるようになっていた。

てか、今まで無かったのかよ…

Default argument
で可能とかウソを書いてしまった、はずかしい。
引数に数の差だけのためにオーバーロードをする必要はコレで無くなるわけだ。

それと、System.IO.File.ReadLines() というイテレーション関数も追加らしい。
StreamReader.ReadLine() じゃダメなの?と思うんだが…

他は興味がわかないので今回はパス。

ということで 3.5 と 4.0 の csc.exe で試しコードを書く。

default_param.cs

using System;

class Test
{
    public static int Func(int x=5, int y=7)
    {
        return x+y;
    }
}

class __main__
{
    [STAThread]
    public static void Main(string[] args)
    {
        int n = Test.Func(y:20);
        Console.WriteLine(n);
    }
}

readlines.cs

using System;
using System.IO;

class __main__
{
    [STAThread]
    public static void Main(string[] args)
    {
        int n = 0;
        //foreach (var line in File.ReadAllLines(args[0]))
        foreach (var line in File.ReadLines(args[0]))
        {
            n++;
            Console.WriteLine("{0}: {1}", n.ToString("000"), line);
        }
    }
}

ちなみに私がパスを通しているのは 3.5 のみなのでこうなる。
うん、やっぱりどちらも 4.0 でしかビルドできないや。

これだけではツマラナイので Ubuntu でもやってみる。

あれ?デフォルト引数のほうだけ問題なくビルドできちゃった。
どっちなんだよ Ubuntu に最初から入っている mono は。

Readlines は「そんなメソッドありません」ですね、そりゃそうだ。
ReadAlllines とコメントアウトを変更すれば普通にビルドできた。
StreamReader で while ループしたほうが圧倒的効率だけどテストです。

つまり何が言いたいかというと。
コンパイルするのがメンドイけど実用無視で遊ぶなら C# は最強。
Linux で Python は単なる実用ツールでしかないもの、私はだが。

Using wget

結局 Windows に戻ったのは正月だけだった、あぁ Linux は快適。

Linux で不便なのは Opera 11 の検索バーで日本語変換を行うと死ぬことだけだ。
フォントは pre と monospace を VL ゴシックに変更しただけで綺麗になった。

一昔前では考えられないほどマトモ、新参の WebKit 軍団にボロ負けだったしね。
しかしデフォルトショートカットキーをコロコロ変更しないでよ。

ネットをウロウロしていると自作漫画配布サイトに。
最近は Blog 機能を利用しているせいか連番で落とせないトコばかり。
だったけど連番で一枚一枚配布している所もまだ存在するんだね。

これは久々に新規スクリプトを書かないと。
何のために wget コマンドが存在するというのか。
wget だけでリンクをたどるのではなく連番ファイル以外はいらないし。

連番最初は 0 か 1 だし確実に 100 ファイル以下だと決め付けでいいだろう。
パラメータ 2 つ指定を強制するけど自分で使うんだから問題無い。
一応ゼロ詰めしているかどうかで振り分けしている。

#!/usr/bin/env python
#-*- coding:utf-8 -*-

"""
    連番ファイルの一括ダウンロード
    
    コマンド
    $ download url num
    
    url 連番最初のファイルのフルパス
    num 最後のファイル番号の整数
"""

import sys
import os

if len(sys.argv) < 3:
    print "URL と 数を指定してください"
else:
    # ファイル名分離
    pathname, filename = os.path.split(sys.argv[1])
    try:
        n = filename.rindex(".")
    except ValueError:
        print "拡張子が見つかりません"
        sys.exit()
    # 連番最初の数値(0 or 1) と拡張子
    begin = int(filename[n-1])
    ext = filename[n:]
    # ゼロ詰めかどうかで振り分け
    if filename[n-2] == "0":
        name = "wget " + pathname + "/" + filename[:n-2] + "%02d" + ext
    else:
        name = "wget " + pathname + "/" + filename[:n-1] + "%d" + ext
    # 連番数のチェック
    try:
        end = int(sys.argv[2]) + 1
    except ValueError:
        print "二番目の引数は整数でお願いします"
        sys.exit()
    # 開始
    for i in range(begin, end):
        os.system(name % i)
        #print name % i

~/bin に download という名前で置いて chmod で +x して。
覚書ページに書いているけど ~/bin を作れば Ubuntu は勝手にパスが通る。

使ってみる、よしよし動く。
端末を普通に使うようになると「コレで十分」になって退化する私であった。
てゆーか、アッサリこんなのを作って使えるから Linux のほうが楽なんだよね。

実は Python での文字列のスライス方法を忘れていたので何か作ろうと…
こんなしょーもないスクリプトあたりをたまには作らないと忘れるねん。

関係無いけどこんなのを見つけた。

[ Dolphinで右クリックメニューを使う方法 Dolphin用Script] by ひねもすLinux

Dolphin でもスクリプト拡張ができるんだ、知らなかった。
しかし面倒くさいな、Nautilus がこうならないことを祈る。

WPF4 Text Rendering

WPF 4 をコードで使う方法は前回で解ったので今回は XAML を。

System.Xaml が新たに作られたということは xmlns 名前空間が変わる?
というか System.Windows.Markup から書き換えも必要なのだろうか?

WPF から System.Xaml に移行した型

難しく説明しないでくれよ…
WPF の XAML 名前空間は以前と同じでイイということは解ったけど本当かな。

[XAML] WPF 4 を使用して、Windows 7 のジャンプ リストにカスタム タスクを追加する | 逆引きサンプル コード

上記とかでも 3.5 と同じだしイケそうだ。

後は IronPython で自分で書いた下記のようにソースコードに埋め込みしたいのだが。
IronPython で XAML

C# では Python の docstring みたく丸ごと XML とはいかない。
@”リテラル” で改行付き文字列はイケるけど ” を “” にする必要あり。

string (C#)

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;

class TextReader : Window
{
	private const string TXT = @"
		<TextBlock 
		xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
		xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
	        Hello World
	   </TextBlock>";
	public TextReader()
	{
		this.Title = "XamlReader.Parse Test";
		this.Width = 400;
		this.Height = 400;
		try
		{
			var tt = XamlReader.Parse(TXT) as TextBlock;
			this.Content = tt;
			tt.Text = "こんにちは日本";
		}
		catch (Exception e)
		{
			MessageBox.Show(e.ToString());
		}
	}
}

class __main__
{
	[STAThread]
	public static void Main(string[] args)
	{
		var app = new Application();
		app.Run(new TextReader());
	}
}

これをビルドしても上手くいったけど XAML は通常 ” が多いし…
C# では別ファイルにして XamlReader.Load() のほうがいいな。

それと XAML から object を作ったら as で型を教えてあげないとメソッドが使えない。
IronPython は動的だからいらないけどコンパイルする C# では必須だね。

WPF 4.0 Text Stack Improvements – WPF Text Blog – Site Home – MSDN Blogs

とりあえずこの Text Rendering API ってのを試してみよう。
3.5 までは Windows XP での描写が変だったのがどうなるかだ。
私的にはもう XP はガン無視でいいけど、いつの OS だよ Linux 使えよ。

でも世間はそうじゃないし、しかたがないので XP に .NET 4.0 を入れる。
Windows Update に Client Profile ってのがあるんだね。

意外とサイズが小さいな、これなら仮想 OS な XP に入れてもいいや。
入れてみたら csc.exe なんかもしっかりあるのでコンパイルできると思う。

と思ったけど何このエラーは…

build した exe を持っていくと問題なく動くんだが、よくワカンネェ。
本当に XP って糞 OS だ、俺は XP で開発しないから無視しておこう。
てか Ubuntu 上の仮想マシンなので PC を行ったり来たりでメンドイ。
スクリーンショットも全部 Ubuntu から撮影してるし、関係無いか。

というわけでコード。

test.xaml

<StackPanel 
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	TextOptions.TextFormattingMode="Display">
    <TextBlock Name="block">
        クリアータイプ ... ClearType
    </TextBlock>
    <TextBlock TextOptions.TextRenderingMode="Grayscale">
        白黒 ... Grayscale
    </TextBlock>
    <TextBlock TextOptions.TextRenderingMode="Aliased">
        ぎざぎざ ... Aliased
    </TextBlock>
</StackPanel>

src.cs

using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;

class TextRender : Window
{
	public TextRender()
	{
		this.Title = "てきすとれんだぁ!";
		this.Width = 300;
		this.Height = 200;
		try
		{
			var fs = new FileStream("test.xaml", FileMode.Open, FileAccess.Read);
			var sp = XamlReader.Load(fs) as StackPanel;
			this.Content = sp;
			(sp.FindName("block") as TextBlock).MouseUp += on_click;
		}
		catch (Exception e)
		{
			MessageBox.Show(e.ToString());
		}
	}
	private void on_click(object sender, RoutedEventArgs e)
	{
		MessageBox.Show("くりっく");
	}
}

class __main__
{
	[STAThread]
	public static void Main(string[] args)
	{
		var app = new Application();
		app.Run(new TextRender());
	}
}

まとめて ZIP
textrender.zip

描写だけじゃ寂しいので一番上の文字列をクリックしたらメッセージを出すように。
ココも as で型指定が必要だ、やはり XAML をコンパイルするのとは扱いが違うわな。

うん、XP でも綺麗なフォント表示が可能になっている。
XP だと個々の差は解りにくいけど 7 でははっきり違いが解る。
しかし画像を並べてみると本当に XP の UI って古臭いなぁ…
今日はこのへんで。

追記

ビルドするためのバッチに

build32.bat

C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe ^
/lib:C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF ^
/reference:System.dll ^
/reference:System.Xml.dll ^
/reference:System.Xaml.dll ^
/reference:PresentationCore.dll ^
/reference:PresentationFramework.dll ^
/reference:WindowsBase.dll ^
/target:winexe ^
/out:TextReader.exe ^
/win32icon:icon.ico ^
/optimize+ ^
src.cs
PAUSE

と System.dll と System.Xml.dll への参照を追記したら XP でもビルドできた。
何で 7 と違うのよ…