Programming」カテゴリーアーカイブ

SeeMe for Linux v1.0.2

SeeMe をまだ Delphi 5 という化石で作っていた頃の v2 画像が何故か出てきた。

seeme200

ナンジャコリャ…今になって見ると我ながら UI が古すぎる!
よくこんなのを恥ずかしげもなく公開していたものだ、信じられない。

XP のデザイン自体も古いけどウィジェットの絶対値配置ってマジ化石だな。
Vista や GNOME 環境を使っていると絶対値配置の奇妙なウイジェット位置に違和感出まくり。

PyGtk で GtkFixed コンテナを使っている初心者を最近見て死ぬほどガッカリしたんだが…
二年前の私はたいして変わらないな。

ということで今日の Python コード。

SeeMe for Linux で直接編集の指定部分。
何も書かれていなければ関連付け起動、でなければそのエディタで。
んでエディタを別プロセスで起動させたならそのまま SeeMe は終了。
というふうにしたかった、てか Windows 版がそうしている。

エディタ指定をしたならその実効ファイル名の引数に渡せばいいので普通なら下記…

os.system("%s %s" % (exe, self.sset.custome_path) )

これだと値が戻ってくるまで待機してしまう、つまり SeeMe はフリーズ状態に。
これでは使えない、ということで探したら os モジュールに spawn* というのがあるらしい。

16.1. os ? 雑多なオペレーティングシステムインタフェース ? Python v2.6.0 documentation

肝心な引数の書き方が可変個引数でよく解らない、検索してみたけどバラバラだし。
自分で色々試して二番目と三番目引数に実効ファイル名、四番目を開きたいファイルのフルパス

def on_textedit(self, widget, event=None):
    exe = self.deditor.edit_editor.get_text()
    if exe == "":
        os.system("xdg-open %s" % self.sset.custome_path)
        self.close()
    else:
        os.spawnlp(os.P_NOWAIT, exe , exe, self.sset.custome_path)
        self.close()

を入れてなんとか成功、イマイチ納得できないけどこれで動くからイイや!
ということで、Python で GUI アプリを作るのは難しい…
アトリビュート変数名の中身は本体スクリプトで、既に千行近くになっているけど…

ということで SeeMe for Linux v1.0.2 の公開。

追記

os.system("%s %s &" % (exe, self.sset.custome_path) )

こんな方法があった…出した直後に見つけるなよ俺。

SeeMe for Windows v4.0.1

あわわ、v4.0.0 はプロファイル編集ができないじゃんコレ。
ここは弄くっていないので前からか、まぁ普通はデフォルト位置で十分だし。

とはいえ放置もアレなので修正しようかどうか、そういえば

ぱぇぽぃ2 ? Blog Archive ? GetFullPath

のようにファイルドロップでフルパス指定にしようとして忘れていた!
なんたってそうすれば System.Windows.Form への参照を排除できる。
WPF なのに OpenFileDialog のためだけに WindowsForm を参照していたし。
よく見たら System.Xml.Linq まで…こんなの SeeMe で使っていないよ。

ということで AboutDlg のように上記 IronPython コードを C# 化…
しようと思ったけど面倒なのでハンドラの追記だけで済ませた。

ちなみに

ぱぇぽぃ2 ? Blog Archive ? WPF Hyperlink class for IronPython

の C# 化はこうやった。

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Text;
using System.Windows.Media;
using System.Collections.Generic;

namespace SeeMe4
{
    class About : Window
    {
        public About(Window owner)
        {
            this.Owner = owner;
            this.WindowStartupLocation = WindowStartupLocation.CenterOwner;
            this.Title = "About SeeMe for Windows";
            this.SizeToContent = SizeToContent.WidthAndHeight;
            this.ResizeMode = ResizeMode.NoResize;
            this.Icon = owner.Icon;
            // StackPanel
            var panel = new StackPanel();
            // Dialog Icon and Image
            var img = new Image();
            img.Source = owner.Icon;
            img.Width = 32;
            img.Height = 32;
            panel.Children.Add(img);
            // Create TextBlock List
            var v = new List<TextBlock>();
            for (int i = 0; i < 5; i++)
            {
                var b = new TextBlock();
                panel.Children.Add(b);
                v.Add(b);
            }
            v[0].Inlines.Add( new Bold(new Run("SeeMe for Windows 4.0.0\n")));
            v[1].Text = "Windows Version ( " + Environment.OSVersion.Version + " )";
            v[2].Text = "CLR Version ( " + Environment.Version.ToString() + " )\n";
            v[3].Text = "Copyright(c)2003-2009 by sasakima-nao";
            var url = new Hyperlink(new Run("http://palepoli.skr.jp/"));
            url.Click +=new RoutedEventHandler(url_Click);
            v[4].Inlines.Add(url);
            // Align Right button
            var btn = new Button();
            btn.Content = "Close";
            btn.Click += new RoutedEventHandler(btn_Click);
            DockPanel.SetDock(btn, Dock.Right);
            var dp = new DockPanel();
            dp.LastChildFill = false;
            dp.Children.Add(btn);
            panel.Children.Add(dp);
            // Append
            this.Content = panel;
        }

        void btn_Click(object sender, RoutedEventArgs e)
        {
            Close();
        }

        void url_Click(object sender, RoutedEventArgs e)
        {
            System.Diagnostics.Process.Start("http://palepoli.skr.jp/");
        }
    }
}

スクリーンショットを撮って気がついたが About のバージョンが 4.0.0 のままだ…
まあいいや、次の更新で直しておこう。

about_win

型指定がメンドクサイので var を使ったが何もかも IronPython より面倒。
中括弧とセミコロンと new と var が増えリストを System.Collections.Generic です。
特にハンドラ指定の RoutedEventHandler みたいなのってマジ書かなくてもよくないか?
こんなの Visual Studio エディタの助けが無かったら私は書けなかったかもだよ。

やっぱりこれからは IronPython だよ、EmEditor だけでデバッグもできるし。

WPF Hyperlink class for IronPython

SeeMe for Windows v4 beta2 のソースにコッソリ書いているが、
Hyperlink クラスをコードで作る方法が解らなかった。

Hyperlink クラス (System.Windows.Documents)

なんだよ、MSDN に普通に書いてあるや、どこを探していたのやら。
つまり Bold とかと同じように使えばいいということねん。

ぱぇぽぃ2 ? Blog Archive ? 初心者同然

よし、これで Linux 版の GtkAboutDialog と同じレイアウトにしてみる。
Windows 自体というか WPF だけでも専用ダイアログを用意してほしいんだが。

class AboutDlg(Window):
    """
        SeeMe About class
    """
    def __init__(self, owner, path):
        """
            Hyperlink, Run
            from System.Windows.Documents import *
        """
        self.Owner = owner
        self.path = path
        self.WindowStartupLocation = WindowStartupLocation.CenterOwner
        self.Title = "About SeeMe for Windows"
        self.SizeToContent = SizeToContent.WidthAndHeight
        self.ResizeMode = ResizeMode.NoResize
        self.Icon = BitmapFrame.Create(Uri(self.path + "\\img\\seeme.ico"))
        # StackPanel
        panel = StackPanel()
        # Dialog Icon and Image
        img = Image()
        img.Source = BitmapImage(Uri(self.path + "\\img\\seeme.ico"))
        img.Width = 32
        img.Height = 32
        panel.Children.Add(img)
        # Create TextBlock List
        v = []
        for i in range(5):
            b = TextBlock()
            panel.Children.Add(b)
            v.append(b)
        v[0].Inlines.Add(Bold(Run("SeeMe for Windows 5.0.0\n")))
        v[1].Text = "Windows Version ( %s )" % Environment.OSVersion.Version
        v[2].Text = sys.version + "\n"
        v[3].Text = "Copyright(c)2003-2009 by sasakima-nao"
        url = Hyperlink(Run("http://palepoli.skr.jp/"))
        url.Click += self.on_url
        v[4].Inlines.Add(url)
        # Align Right button
        btn = Button()
        btn.Content = "Close"
        btn.Click += self.on_exit
        DockPanel.SetDock(btn, Dock.Right)
        dp = DockPanel()
        dp.LastChildFill = False
        dp.Children.Add(btn)
        panel.Children.Add(dp)
        # Append
        self.Content = panel

    def on_url(self, sender, e):
        Diagnostics.Process.Start("http://palepoli.skr.jp/")

    def on_exit(self, sender, e):
        self.Close()

コレを Show() でオーナーウインドウから呼び出す

def on_about(self, sender, e):
    about = AboutDlg(self, self.path)
    about.Show()

about_dialog

ちょっとデザインが違うけど Windows ではこういう感じのほうが好まれそう。
WPF 使っているのに .NET 2.0 がどうのと出るのが少しだけ気に入らない。
つーかまだアルファにも達していないので自身のバージョン表記が滅茶苦茶だ。

とりあえず Hyperlink クラスの使い方はこれでオケのようだ。
たったコレだけのコードで About 完成、XAML ってマジで無駄だと思った。

今日はココしかやっていないのでバックアップはヤメとこう。

.NET Framework required BOM

デフォルトの search.ini や lng 読み込みで文字化けする理由が判明。
BOM が付いていない UTF-8 文書は UTF-16 文字列に変換読み込みしてくれないということ…

何を今更 BOM での判別といい UNICODE と cp932 の混在といい…
もうイヤだこの変態 OS は、全てが UTF-8 である最近の Linux を見習ってくれ!

Python 組み込みの open() では読み込んだ後に変換しかできないわけだから、方法ある?
System.IO.StreamReader と System.Text.Encoding.UTF8 を普通に使うのが一番だよな。
StreamReader オブジェクトは for ループに…やっぱり無理か。

import os

# 略

class Inifile(InifileBase):
    def __init__(self, filename):
        InifileBase.__init__(self, filename)
        self.header = ""
        self.ini = []
        if os.path.exists(filename):
            f = open(filename)
            x = f.read()
            f.close()
            lines = x.split("\n")
            section = ""
            for line in lines:
                if line == "":
                    continue
                if len(line) > 2 and line[0] =="[" and line[-1] == "]":
                    section = line[1:-1]
                elif section == "":
                    pass # 何もしない
                elif "=" in line:
                    pos = line.index("=")
                    self._add(section, line[:pos], line[pos+1:])

from System.IO import *
from System.Text import *

# 略

class Inifile(InifileBase):
    def __init__(self, filename):
        # ベースクラスでアトリビュートを利用する場合は IronPython でも必要
        InifileBase.__init__(self, filename)
        self.header = ""
        self.ini = []
        #if os.path.exists(filename):
        if File.Exists(self.filename):
            f = StreamReader(self.filename, Encoding.UTF8)
            try:
                section = ""
                while 1:
                    line = f.ReadLine()
                    if line == None:
                        break
                    if line == "":
                        continue
                    if len(line) > 2 and line[0] =="[" and line[-1] == "]":
                        section = line[1:-1]
                    elif section == "":
                        pass # 何もしない
                    elif "=" in line:
                        pos = line.index("=")
                        self._add(section, line[:pos], line[pos+1:])
            finally:
                f.Close()

utf8read

大体同じに仕上げたけど Linux 版とコード共有はもうあきらめるしかない。
組み込みの open とは違い StreamReader は Close 必須なので忘れないでね。
しかし私は無限ループを作って null で抜けるコードばかりだ、一番単純に書けるのよね。
if not line: だと空文字も拾ってしまうのは Python という言語の常識かな。

ついでに継承ベースクラスの __init__() が不要の IronPython も必要な場合があると解った。
ここではベースクラスで self.filename を定義しているだけなんだが、そういう場合に。
だから無意味に呼び出しても結果が同じなのか、ふむふむ。

これならコンストラクタ化されているというのに自由に __init__() を定義できる。
C++ の継承で頭を抱えた経験がある人にしか解らないだろうけど嬉しい仕様だ。

seeme_a405.zip

次はメニューの文字化けか、一部だけ化けるというのが理解に苦しむ。

こんなノロノロ作っている間に Opera 10 が RC になっているわ。
SeeMe for Windows v4 はもうしばらく C# つまり exe 形式のままね。
そのほうがいいという人のほうが多いと知っているけどシラネ!

GtkNoteBook and TabControl

SeeMe for Linux では GtkNoteBook の各ページは Python の List でアクセス。

note = gtk.Notebook()
page = []
for i in range(4):
    tab = gtk.Label(tab_label[i])
    page.append(gtk.VBox())
    self.note.append_page(page[i], tab)
page[0].pack_start(self.sw1)
page[1].pack_start(self.sw2)

という方法を見つけたけど IronPython でも同じようにできるのかな?
.NET Framework では TabControl と TabItem なんていうものを使う。
まあこんなのは実際にやってみるのが早い。

note = TabControl()
self.page = []
for i in range(3):
    page = TabItem()
    page.Header = tab_label[i]
    note.AddChild(page)
    self.page.append(page)
self.page[0].Content = self.custome_listview
self.page[1].Content = self.default_listview

tab1

おぉ同じように使えるじゃないの!
XAML で作っているとこんな添え字でのアクセスは考え付きもしないだろう。
リストの添え字アクセスにメリットを感じるかどうかで人それぞれだと思うけど。

だけど…

tab2

又しても文字化けだよ。
lng ファイルは for Linux で使っている InifileReader クラスで読んでいるののだが…
もしかして一度全部読み込まないと UTF-8 を UTF-16 に自動変換できないのかな?
メニューの文字化けも解決していないしまいった、以下今日のバックアップ。

seeme_a404.zip