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

Linux shift-jis zip file

とある ZIP を展開しようと思ったら Shift-JIS ファイル名を使っていて展開できない。
Ubuntu 日本語 Remix 付属の /usr/bin/zip なら展開できるのだけど Fedora だし。
仮想マシンの Ubuntu から頂いて、と思ったけど我が Fedora は 64bit だった…

仮想マシンな Windows XP から共有を利用して展開。
とソレだけの理由で仮想 Windows を立ち上げるのも面倒だ。
そういえば Python には zipfile モジュールがある、作ってみよう。

とりあえず日本語のドキュメントを探してみた。
13.4. zipfile ? ZIP アーカイブの処理 ? Python v2.6.2 documentation
extractall なんて便利なメソッドがあるじゃん、これは簡単そうだ。
ということで Nautilus スクリプトにしてみた。

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

import zipfile
import os

filenames = os.environ["NAUTILUS_SCRIPT_SELECTED_FILE_PATHS"].split("\n")

for f in filenames:
    z = zipfile.ZipFile(f)
    z.extractall()
    z.close()

こんなに単純なコードでいいみたい。
ファイル名は見事に化けてしまいますが展開だけは成功する。

そういえばパスワード付きの場合もある。
GTK+ でダイアログでも出して入力すればいいかな。

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

import zipfile
import os
import gtk

filenames = os.environ["NAUTILUS_SCRIPT_SELECTED_FILE_PATHS"].split("\n")

for f in filenames:
    z = zipfile.ZipFile(f)
    dlg = gtk.Dialog("password", None, 0, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
    def dlg_ok(self):
        dlg.response(gtk.RESPONSE_ACCEPT)
    entry = gtk.Entry()
    entry.connect("activate", dlg_ok)
    dlg.vbox.pack_start(entry)
    dlg.show_all()
    dlg.run()
    p = entry.get_text()
    dlg.destroy()
    z.extractall(pwd=p)

展開が終了するまでダイアログが閉じないのは何故だろう…
それと展開は死ぬほど遅い…

せっかく作ったけど素直に仮想 Windows から展開するとかのほうが良さそう。
まあ使いたくなる場合もあるだろうからスクリプト登録だけはしておくか。

ところで今頃気がついた。
Nautilus スクリプト位置にディレクトリを作れば入れ子にできるんだね。

まだまだ増やしても大丈夫そうだ。

gi.repository GTK+ Version

Fedora 15 と Ubuntu 11.04 の PyGtk について。

その前に VirtualBox ose 上で Ubuntu を使っているけど \ や | が打てない。
Ubuntu日本語フォーラム / Virtualbox 4.0 で一部のキーが使えない?
VirtualBox ose の Update を待とう…
今回はホストの fedora からクリップボードコピペでなんとかした。

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

from gi.repository import Gtk

message = "「はい」を押すと終了"

while 1:
    dlg = Gtk.MessageDialog(
            None,  
            Gtk.DialogFlags.MODAL,  
            Gtk.MessageType.WARNING, 
            Gtk.ButtonsType.YES_NO,  
            message)  
    r = dlg.run()  
    dlg.destroy()
    if r == Gtk.ResponseType.YES:
        break
    elif r == Gtk.ResponseType.NO:
        message = "それは「いいえ」だろ!"
    elif r == Gtk.ResponseType.DELETE_EVENT:
        message = "閉じるボタンじゃネェ!"
    else:
        message = "不明な動作..."

このコードで Fedora 15, Ubuntu 11.04 のどちらでも動く。
Ubuntu 10.10 以前では例外になる、つか gi から無いわけで。
Fedora のダイアログは「閉じる」ボタンが出ないので右クリックで「閉じる」とやる。

import が変更になり gtk を Gtk に全置換。
以前は定数だったものの大半が enum 名のアトリビュートに変わっている。
それ以外はほとんど同様と思っていいみたい。

つまり Ubuntu 11.04 も GTK3 なのか。
と思ったけどインタラクティブシェル上でコレをやってみる。

from gi.repository import Gtk
Gtk._version

Ubuntu 11.04 の場合はこの形式で書いても GTK2 になるってことなのか。
/usr/lib64/python2.7/site-packages/gi/overrides/Gtk.py
がバージョン違いを振り分けているみたい、とにかくそういうこと。
ソースコードを見てもなんだかよくワカンネェ!

なんにしても早めにこの形式なコードへ書き換えしたほうがいいみたい。
API ドキュメントは既に GTK+ 3.0 になっているのだし。
PyGtk ドキュメントが遅いのはいつものこと。
GNOME 開発センター

ついでに

EyeOfGnome/Plugins – GNOME Live!

このとうりにやっても全然 eog が自作プラグインを認識しない…
~/.local/share/eog/plugins や ~/.local/lib64/eog/plugins
にも試しに置いてみたけど無意味だった。
/usr/lib64/eog/plugins に置くしか無いのだろうか。
ついでに IAge 指定が 2 のままじゃん、Gedit と違ってヤル気が無い感じ。
おかげで自作 eog プラグインの更新ができないよ、まいった。

Fedora 15 006

Fedora 15 (GNOME 3) 64bit 生活六日目。

※ Gedit Plugin

Gedit/PythonPluginHowTo – GNOME Live!

Gedit 3 のプラグインは妙に複雑になった感じだけどやってみると簡単。
Gedit プラグインの作り方 – L’Isola di Niente
私の公開しているこのページを Gedit 3 用に改造してみる。

*.gedit-plugin 拡張子であったファイルは *.plugin と拡張子を変更。
セクション名を [Plugin] に、IAge を 3 に変更、v2 との違いはコレだけだ。

肝心のコード

#-*- coding:utf-8 -*-

import gedit
import gtk

class TestPligin(gedit.Plugin):
    def __init__(self):
        gedit.Plugin.__init__(self)

    def activate(self, window):
        self.window = window

    def deactivate(self, window):
        pass

    def update_ui(self, window):
        pass

#-*- coding:utf-8 -*-

from gi.repository import GObject, Gedit, Gtk

class TestPligin(GObject.Object, Gedit.WindowActivatable):
    __gtype_name__ = "TestPligin"
    window = GObject.property(type=Gedit.Window)
    def __init__(self):
        GObject.Object.__init__(self)

    def do_activate(self):
        pass
        
    def do_deactivate(self):
        pass

    def do_update_state(self):
        pass

ということみたい、コールバックに引数は無くなったんだね。
__gtype_name__ は class 名と同じにしなければいけないみたい。

多重継承の敬称元を Gedit.AppActivatable とか3つから選べになっている。
普通なら Gedit.WindowActivatable だけでいいだろう。
下のほうにあるサンプルコードは継承元を書き忘れているじゃん…

後は self.window のメソッドを辿って弄くっていく。
v2 のコードから gtk → Gtk に全置換する必要がある。
それと activate のコールバックは引数を3つにしておく必要があった。
user_data を指定しなくても None が送られてくるみたい、注意ね。

ついでに GtkMessageDialog の引数は enum 形式に変更されていた。
指定がよく解らない人は dir() で辿って(私はそうやった)

testtest.plugin

[Plugin]
Loader=python
Module=testtest
IAge=3
Name=testtest
Name[ja]=テストテスト
Description=plugin test
Description[ja]=プラグインのテスト
Authors=sasakima-nao 
Copyright=Copyright © 2011 sasakima-nao 
Website=http://palepoli.skr.jp/

testtest.py

#-*- coding:utf-8 -*-

from gi.repository import GObject, Gedit, Gtk

ui_str = """<ui>
  <menubar name="MenuBar">
    <menu name="EditMenu" action="Edit">
      <placeholder name="EditOps_3">
        <menuitem name="testtest" action="testtest"/>
      </placeholder>
    </menu>
  </menubar>
</ui>
"""

class TestTest(GObject.Object, Gedit.WindowActivatable):
    __gtype_name__ = "TestTest"
    window = GObject.property(type=Gedit.Window)
    def __init__(self):
        GObject.Object.__init__(self)

    def do_activate(self):
        # GtkUIManager を得る
        manager = self.window.get_ui_manager()
        # GtkActionGroup を新規で作成
        self._action_group = Gtk.ActionGroup("TestTestActions")
        # GtkActionEntry を作成
        # name, stock_id, label, accelerator, tooltip, callback
        actions = [("testtest", None, "引用に変換", None, "すてーたすばー", self.on_testtest_activate)]
        # GtkActionGroup に挿入
        self._action_group.add_actions(actions)
        # GtkUIManager に追加
        manager.insert_action_group(self._action_group, -1)
        self._ui_id = manager.add_ui_from_string(ui_str)
        
    def do_deactivate(self):
        manager = self.window.get_ui_manager()
        manager.remove_ui(self._ui_id)
        manager.remove_action_group(self._action_group)
        manager.ensure_update()
        

    def do_update_state(self):
        pass
    
    def on_testtest_activate(self, action, data=None):
        view = self.window.get_active_view()
        buf = view.get_buffer()
        try:
            begin, end = buf.get_selection_bounds()
        except:
            self.messagebox("変換したいテキストを選択してください")
            return
        text = begin.get_text(end)
        lines = text.split("\n")
        # list の join に変更
        result = []
        for line in lines:
            result.append("> {0}".format(line))
        buf.delete_selection(True, True)
        buf.insert_at_cursor("\n".join(result))
            
        
    def messagebox(self, text):
        dlg = Gtk.MessageDialog(
                self.window,  
                Gtk.DialogFlags.MODAL,  
                Gtk.MessageType.WARNING, 
                Gtk.ButtonsType.OK,  
                text)  
        r = dlg.run()  
        dlg.destroy()

と2つのファイルを作成して ~/.local/share/gedit/plugins に置く。
Gedit を再起動して設定からこのプラグインを選択。

GtkUIManager のメソッドや XML UI 指定は v2 と変わっていないようだ。
これで Gedit 3 のプラグイン作りはなんとかなりそう。

ついでに、外部ツールで実行前にファイルを保存するように指定すると動作しない。
gedit って更新毎に必ず一ヶ所はおかしな所があるよね。

gdiplus save

Keyleigh をそろそろなんとかしたいと考えた。
拾ったデルヒャァコンポーネントを使っているだけのクソソフトだが。
せっかくなので画像コンバータ機能とかでも追加すれば面白くなるかも。

GDI+ を使うのが一番だよなぁ。
C# で使えば死ぬほど簡単だ。

using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

namespace iconpoli2
{
	class Program
	{
		static void Main(string[] args)
		{
			foreach (var s in args)
			{
				string t = Path.ChangeExtension(s, "jpg");
				Bitmap b = new Bitmap(s);
				b.Save(t, ImageFormat.Jpeg);
			}
		}
	}
}

コンバートならマジでこれだけだ。
いや、せっかくなので C++ でやりたいぞ。
と思ったけど

Gdiplus::Bitmap * img = new Gdiplus::Bitmap(__wargv[i]);
img->Save(outname, &Gdiplus::ImageFormatJPEG);

とやっても全然上手くいかない。

Transforming a JPEG Image Without Loss of Information (Windows)
Retrieving the Class Identifier for an Encoder (Windows)

どうやら自力で CLSID を取ってくるしかないようで。
つか ImageCodecInfo って class なのにサイズを得て malloc なの?
試しに普通な new で作ったら見事にヒープ領域不足、どういう設計だよ。

とにかく MSDN の方法で上記と同じになるよう C++ で作ってみる。
C++ じゃ ChangeExtension なんて便利なものは無いし GDI+ 初期化も必須。

stdafx.h

#pragma once

// 面倒なのでココで参照追加
#pragma comment(lib, "gdiplus.lib")

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600 // Vista
#endif

#include <stdio.h>
//#include <tchar.h>
#include <wchar.h>
#include <windows.h>
#include <gdiplus.h>

凄く無駄だと解っているけど tchar を利用しないで UNICODE 関数をまんま使う。
てか今となっては Ansi 関数を併用する必要は無いと思うんだが。
WCHAR や UINT のマクロも利用したくない。
だって Visual Studio は大文字マクロの型名では色が付かないんだよね。
というかなるべく小文字にしたいと思うのは多分 Python のやりすぎ。

iconpoli.cpp

#include "stdafx.h"

using namespace Gdiplus;

int GetEncoderClsid(const wchar_t* format, CLSID* pClsid)
{
	unsigned int  num = 0;
	unsigned int  size = 0;

	ImageCodecInfo * pImageCodecInfo = NULL;

	GetImageEncodersSize(&num, &size);
	if(size == 0)
		return -1;  // Failure

	// コレだとヒープ領域不足になる
	//ImageCodecInfo * pImageCodecInfo = new ImageCodecInfo();
	pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
	if(pImageCodecInfo == NULL)
		return -1;  // Failure

	GetImageEncoders(num, size, pImageCodecInfo);

	for(unsigned int j = 0; j < num; ++j)
	{
		if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
		{
			*pClsid = pImageCodecInfo[j].Clsid;
			free(pImageCodecInfo);
			return j;  // Success
		}    
	}

	free(pImageCodecInfo);
	return -1;  // Failure
}

bool ChangeExtension(const wchar_t* src, wchar_t* dst, const wchar_t* ext)
{
	wchar_t path[4][256];
	_wsplitpath_s(src, path[0], 256, path[1], 256, path[2], 256, path[3], 256);
	_wmakepath_s(dst, 1024, path[0], path[1], path[2], ext);
	return TRUE;
}

int wmain(int argc, wchar_t* argv[])
{
	// GDI+ 初期化
	GdiplusStartupInput gdiplusStartupInput;
	ULONG_PTR gdiplusToken;
	GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

	for (int i=1; i < __argc; i++)
	{
		// makepath
		wchar_t outname[1024];
		ChangeExtension(__wargv[i], outname, L".jpg");

		Gdiplus::Bitmap * img = new Gdiplus::Bitmap(__wargv[i]);

		CLSID encoderClsid;
		int r = GetEncoderClsid(L"image/jpeg", &encoderClsid);
		if (r == -1)
		{
			wprintf(L"no CLSID");
			return 0;
		}
		// コレは上手くいかない
		//int result = img->Save(outname, &Gdiplus::ImageFormatJPEG);
		int result = img->Save(outname, &encoderClsid);
		if (result == Gdiplus::Ok)
			wprintf(L"Encode Success");
		else
			wprintf(L"Encode Failure");
		delete img;
	}
	// 後片付け
	Gdiplus::GdiplusShutdown(gdiplusToken);
	return 0;
}

長い。。。。。

たったこれだけのモンにえらく時間が掛かった、やっぱり C# のほうがいいかなと。
それと Python や C# ばかりやっていると開放処理をつい忘れそうになるよね。

CsharpRepl

mono にはこんなのがあるらしい。
ようするに C# 言語のインタラクティブシェルだ。

CsharpRepl – Mono

Ubuntu 10.10 には最初から入っていたので試してみる。
公式の解説が思いっきり LINQ だけど普通に foreach とかはどうだ。

using System; は初期状態から行われている。
for 文は一行で終わらせるか中括弧を語尾に付けないとエラーになる。
日本語は入力時に化けるけど UNICODE として処理しているようだ。
class なんかは作れない。
終了は quit; となっているが普通に Ctrl+D でいい。

Ctrl+D に慣れすぎて Windows の cmd.exe で EXIT を打つのが面倒…
というか Windows でも cmd.exe のほうがある程度の処理は簡単な体に…
それはどうでもよくて。

インタラクティブシェルだと嫌でも Python と比較してしまう。
静的言語を無理やり使うのだから Python レベルを求めるのは無謀か。