Hatena::ブログ(Diary)

×××Diary このページをアンテナに追加 RSSフィード

2014-04-16

進捗表示

古典的ネタでも。

とある会話。

今回vbscriptコードを書いて、ドラッグ&ドロップで処理を行うつもりです。

ただ、プログラムが進んでいるのだかさっぱりわからないのがちょっと難点なんですよねぇ。

と、ぼやいてたので、進捗を表示できるような仕組みを書いてみた。

Class Progress
    Private Status
    Private ExecName
    Private FSO
    Private Percentage
    Private PercentageStr
    Private ProgressBar
    Private ScriptPath
    Private WSH

    Private Sub Class_Initialize
        Set FSO = CreateObject("Scripting.FileSystemObject")
        Set WSH = WScript.CreateObject("WScript.Shell")
        ScriptPath = Wscript.ScriptFullName
    End Sub
    
    Private Sub Class_Terminate
        Set FSO = Nothing
        Set WSH = Nothing
    End Sub
    
    Public Sub CscriptRun()
        
        if Not IsCscript() then
            
            WSH.Run "cscript //nologo """ & ScriptPath & """", 1, False
            Wscript.Quit
            
        end if
        
    End Sub
    
    Private Function IsCscript()
        ExecName = LCase(FSO.GetFileName(WScript.FullName))
        
        if ExecName = "cscript.exe" then
            IsCscript = true
        else
            IsCscript = false
        end if
        
    End Function
    
    Public Sub ShowProgress(Bunsi, Bunbo)
        
        Call MakePercentage(Bunsi, Bunbo)
        Call MakeProgressBar()
        Wscript.StdOut.Write PercentageStr & " |" & ProgressBar & "| " & Bunsi & "/" & Bunbo & vbCr
        Status = "ShowProgress"
    End Sub
    
    Public Sub ShowMessage(Message)
        if Status = "ShowProgress" then Wscript.StdOut.Write vbCrLf
        Wscript.StdOut.Write Message & vbCrLf
        Status = "ShowMessage"
    End SUb
    
    Private Sub MakePercentage(Bunsi, Bunbo)
        Percentage = Cint((Bunsi / Bunbo) * 100)
        PercentageStr = Percentage & "%"
        PercentageStr = String(4 - Len(PercentageStr), " ") & PercentageStr
    End Sub
    
    Private Sub MakeProgressBar()
        ProgressBar = String(Cint(Percentage/5), "=") & ">" & String(20 - Cint(Percentage/5), " ")
    End Sub
    
End Class

cscript.exeで実行することでコンソールに出力できるようにしたいので

cscript.exeで実行していなかったら、cscript.exeで実行しなおすCscriptRunメソッド

 ただ、コンソールに出力するだけのShowMessageメソッド。分子と分母の2数から

進捗表示を出力するShowProgressの3つのメソッドしかないです。


■cscript.exe、wscript.exeについて

両者の違いを確認するためにWScript.Echo "文字列"でも書いて保存して

cscript/wscriptで実行してみましょう。

[sample.vbs]

Option Explicit

WScript.Echo "VBScript"

まずはcscriptで実行

f:id:maeyan:20140417000904p:image

コマンドプロンプト上に出力


次にwscriptで実行

f:id:maeyan:20140417000905p:image

ウィンドウ上に出力


cscriptとは、コマンドプロンプトを使って出力することもできるということです。

msgboxを使うと、cscriptでもウィンドウ上に出力されます。

通常、ファイルクリックして実行される時は、wscriptとして実行されています。

進捗表示をしたい場合、msgboxで出力するとプログラムが停止するので、

コマンドプロンプト上に、出力してプログラム自体止めないようにしないといけません。

そのため、cscriptで実行して、進捗情報コマンドプロンプト上に表示する必要があります。


そんなわけで、CscriptRunメソッドでcscriptで実行していなければcscriptで実行するような処理をするメソッドがあるわけです。これで、コマンドプロンプト上に進捗表示できる状態ができるわけです。


通常出力する時に、何も考えずにvbCrLfと書いていると思いますが、

本来、Crは、同じ行の先頭に戻るという命令で

Lfは、次の行に移動するという命令です。

そのためvbCrだけを使用すれば、同じ行に出力し続けることになります。


後は、前回出力した文字数と同等またはそれ以上出力してあげれば

必ず上書きされて、進捗表示が実現できるというわけです。

進捗表示の部分は、wgetコマンド風にしたててあります。


ということで、実際に使うときはこのクラスコピペして

コードを書いて、コードの先頭でCscriptRunを実行し

あとは、適宜メッセージや、進捗表示してあげればよいわけです。

Option Explicit

Dim ProgressObj, i, MaxValue
Set ProgressObj = New Progress

'Cscriptで動かしているか判定し必要に応じて処理を変える
Call ProgressObj.CscriptRun()

'#処理1
Call ProgressObj.ShowMessage("長い処理 実行!")
MaxValue = 255
For i = 1 To MaxValue
    WScript.Sleep 1
    Call ProgressObj.ShowProgress(i, MaxValue)
Next

'#処理2
Call ProgressObj.ShowMessage("短い処理 実行!")
MaxValue= 10
For i = 1 To MaxValue
    WScript.Sleep 45
    Call ProgressObj.ShowProgress(i, MaxValue)
Next

Call ProgressObj.ShowMessage("Complete!!")
msgbox "終了しました"


Class Progress
    Private Status
    Private ExecName
    Private FSO
    Private Percentage
    Private PercentageStr
    Private ProgressBar
    Private ScriptPath
    Private WSH

    Private Sub Class_Initialize
        Set FSO = CreateObject("Scripting.FileSystemObject")
        Set WSH = WScript.CreateObject("WScript.Shell")
        ScriptPath = Wscript.ScriptFullName
    End Sub
    
    Private Sub Class_Terminate
        Set FSO = Nothing
        Set WSH = Nothing
    End Sub
    
    Public Sub CscriptRun()
        
        if Not IsCscript() then
            
            WSH.Run "cscript //nologo """ & ScriptPath & """", 1, False
            Wscript.Quit
            
        end if
        
    End Sub
    
    Private Function IsCscript()
        ExecName = LCase(FSO.GetFileName(WScript.FullName))
        
        if ExecName = "cscript.exe" then
            IsCscript = true
        else
            IsCscript = false
        end if
        
    End Function
    
    Public Sub ShowProgress(Bunsi, Bunbo)
        
        Call MakePercentage(Bunsi, Bunbo)
        Call MakeProgressBar()
        Wscript.StdOut.Write PercentageStr & " |" & ProgressBar & "| " & Bunsi & "/" & Bunbo & vbCr
        Status = "ShowProgress"
    End Sub
    
    Public Sub ShowMessage(Message)
        if Status = "ShowProgress" then Wscript.StdOut.Write vbCrLf
        Wscript.StdOut.Write Message & vbCrLf
        Status = "ShowMessage"
    End SUb
    
    Private Sub MakePercentage(Bunsi, Bunbo)
        Percentage = Cint((Bunsi / Bunbo) * 100)
        PercentageStr = Percentage & "%"
        PercentageStr = String(4 - Len(PercentageStr), " ") & PercentageStr
    End Sub
    
    Private Sub MakeProgressBar()
        ProgressBar = String(Cint(Percentage/5), "=") & ">" & String(20 - Cint(Percentage/5), " ")
    End Sub
    
End Class

実行するとこんな感じになります

f:id:maeyan:20140416075922p:image

f:id:maeyan:20140416075923p:image

2014-04-13

走る

冬の間にうっかり体重が6kgくらい増えたんで

また、走り始めてみました。

ひとまず、形から入っとこうということで

モチベーションをあげるためにもランニング用の時計を買ってみた。

10年くらい前といえば、この手のメーカーといえば

POLARかSUNTOあたりで、POLAR推しが多かったので

あえて、人柱的にSUNTOを買ったことがあって

その後、GARMINに乗り換えて、

今回は、エプソンに乗り換えてみました。

GARMINGPSの場合、バッテリーは8時間くらいに対し

今回買ったものは、30時間持ちます。

GARMINほどちょくちょく充電する必要はないですね。

それに自転車ウルトラマラソン系の長時間走る人にもうれしいことですね。

ひとまず、5kmくらい走り続けてみます。

今日は1km 6分30秒くらいのペースでした。

学生の頃1km 3分45秒で走ってたことを考えると

見る影もないっすね・・・

2014-03-11

PDFにしおり、ページ番号を追加したい

PDFを編集したいのでいろいろ調べ始めてみる

あれから少しわかったので改めてメモ。


プログラムPDFしおりや、ページ番号を操作したいので

iTextSharpを使って実現したいな。

というお話。


PDF操作するには、内容を書き換えない場合は、Readしてページ数や、しおり名を調べるのに使ったり、新規PDFを作るのであればWriteしてPDFを作るようです。

そして、既存PDFに対して何かしらの変更を加えたい場合は、一度複製しそれに対し、しおりを追加したりといった操作をするそうです。


※重要。

既存PDFを直接書き換えることはしない。


あちこちのブログでよく見かける方法は、PdfCopyFieldクラスを使って、複製しそれに対し何らかの変更を加えるという方法

複製する部分のサンプルでよく見かけるのは下記のようなコード

using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            string pdfPath = "SamplePDF.pdf";
            PdfReader reader = new PdfReader(pdfPath);

            FileStream os = new FileStream("PdfCopy.pdf", FileMode.OpenOrCreate);
            PdfCopyFields copyFields = new PdfCopyFields(os);
            copyFields.AddDocument(reader);

            copyFields.Close();
            os.Close();
            
        
        }

    }
}

でもね、PdfCopyFieldクラスを使うと、そのクラスは推奨されてないんだよ!

と、こっぴどく叱られます・・・

PdfCopyFieldってdeprecatedなんですよね。

f:id:maeyan:20140311004931p:image


叱ってくれるのはいいのだけど、代わりにこれを使いなよ。

的なお話がさっぱりわからなかったので、ひとまず本家のiTextサイトを漁ってみると普通にそれっぽいことが書いてありました。

http://itextpdf.com/release/itext544

From now on you can now merge forms and preserve the tagged PDF structure when using the addDocument() method in PdfCopy. At the same time, we've deprecated PdfCopyFields.

PdfCopyクラスにあるaddDocumentメソッド使えば?

だとさ。


そこで、addDocumentメソッドを使ったコードに書き換えてみる。

using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            string pdfPath = "SamplePDF.pdf";
            PdfReader reader = new PdfReader(pdfPath);

            FileStream os = new FileStream("PdfCopy.pdf", FileMode.OpenOrCreate);

            Document document = new Document();
            PdfCopy copy = new PdfCopy(document, os);
            document.Open();
            copy.AddDocument(reader);

            copy.Close();
            document.Close();
            os.Close();
            
        }

    }
}

コードを簡単に説明すると

#.exeと同じ階層においてあるSamplePDF.pdfを複製元とします。
string pdfPath = "SamplePDF.pdf";

#Pdfの中身を読み込みます。
PdfReader reader = new PdfReader(pdfPath);
FileStream os = new FileStream("PdfCopy.pdf", FileMode.OpenOrCreate);

#AddDocumentは複製というよりDocumentを結合する物って感じなのかな?
#空のDocumentに対し、何かPDFをAddするとあたかも複製したかのように見えるってわけか。
Document document = new Document();
PdfCopy copy = new PdfCopy(document, os);
document.Open();
copy.AddDocument(reader);


さて、次にページ番号でも追加してみましょう。

PDFでページ番号なんて検索するとFooterに追加する話がわさわさ出てきますがそれじゃないです。

サイドバーのページを表示して右クリックでページ番号です。

f:id:maeyan:20140311004932p:image

1ページ目は、アラビア数字を使って、接頭辞:A、 開始:1

4ページめは、アラビア数字を使って、接頭辞:B、 開始:1

とすると上記絵のようなページ番号が出来上がるわけです。

それ意外のページはインクリメントして自動的にふってくれるわけです。

便利な機能ですね。

そこで、上記を再現するコードを書いてみます。

using iTextSharp.text.pdf;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            //ページ番号作る
            PdfPageLabels labels = new PdfPageLabels();
            labels.AddPageLabel(1, PdfPageLabels.DECIMAL_ARABIC_NUMERALS, "A");
            labels.AddPageLabel(4, PdfPageLabels.DECIMAL_ARABIC_NUMERALS, "B");

            string pdfPath = "SamplePDF.pdf";
            PdfReader reader = new PdfReader(pdfPath);

            FileStream os = new FileStream("PdfCopy.pdf", FileMode.OpenOrCreate);

            Document document = new Document();
            PdfCopy copy = new PdfCopy(document, os);
            document.Open();
            copy.AddDocument(reader);

            //ページ番号を追加
            copy.PageLabels = labels;

            copy.Close();
            document.Close();
            os.Close();
            
        }

    }
}

これだけで出来上がります。

ページ番号は、PdfPageLabelsクラスのAddPageLabelメソッドを使えばよいです。

f:id:maeyan:20140311004933p:image

AddPageLabelの使い方

■2引数の時

void PdfPageLabels.AddPageLabel(int page, int numberStyle)

pageに開始の数字を指定

numberStyleにスタイルを指定します。

スタイルの指定について

なし→PdfPageLabels.EMPTY

アラビア数字→PdfPageLabels.DECIMAL_ARABIC_NUMERALS

小文字ローマ数字→LOWERCASE_ROMAN_NUMERALS

大文字ローマ数字→UPPERCASE_ROMAN_NUMERALS

小文字英文字→LOWERCASE_LETTERS

大文字英文字→UPPERCASE_LETTERS

f:id:maeyan:20140311004934p:image

ということで、ページ番号を追加すること自体たいしたことないのですが

問題はしおりの追加です。

これ、いまだに解決できていないです。

あちこちのサイトを見てあたかも動くかのように書いてあるのですが

そのサンプルをコードを使ってもうまくいかないのですよね。

試してみただめなコード

using System.IO;
using System.Collections.Generic;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {
            //しおり
            Dictionary<string, object> bookmark;
            List<Dictionary<string, object>> outlines = new List<Dictionary<string, object>>();
            bookmark = new Dictionary<string, object>();
            bookmark.Add("Title", "Section01");
            bookmark.Add("Page", "2 FitH");
            bookmark.Add("Action", "Goto");
            outlines.Add(bookmark);

            bookmark = new Dictionary<string, object>();
            bookmark.Add("Title", "Section02");
            bookmark.Add("Page", "4 Fit");
            bookmark.Add("Action", "Goto");
            outlines.Add(bookmark);


            string pdfPath = "SamplePDF.pdf";
            PdfReader reader = new PdfReader(pdfPath);

            FileStream os = new FileStream("PdfCopy.pdf", FileMode.OpenOrCreate);

            Document document = new Document();
            PdfCopy copy = new PdfCopy(document, os);
            document.Open();
            copy.AddDocument(reader);

            //しおりを追加
            copy.Outlines = outlines;

            copy.Close();
            document.Close();
            os.Close();
            
        }

    }
}

このコードだと、しおりは追加されるけどアクションがセットされないんですよね・・・

f:id:maeyan:20140311004936p:image

しおりは追加されている。


f:id:maeyan:20140311004935p:image

↑ただ、アクションが追加されていないため、飛びたいページに飛べない。


なんでだろ・・・

お答えしよう!(※2014/4/13追記)

まず、GotoじゃなくてGoTo

それと直接ブックマークトップ階層にぶら下げるのでなく

一旦、何かしらのList<Dictionary<string, object>>に追加してから

トップ階層にぶら下げるということをしてあげればよい。

下記のように書き直せば動きますね。

using System.IO;
using System.Collections.Generic;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {
            //しおり
            Dictionary<string, object> bookmark;
            List<Dictionary<string, object>> outlines;
            List<Dictionary<string, object>> root = new List<Dictionary<string, object>>();

            outlines = new List<Dictionary<string, object>>();
            bookmark = new Dictionary<string, object>();
            bookmark.Add("Title", "Section01");
            bookmark.Add("Page", "2 FitH");
            bookmark.Add("Action", "GoTo");
            bookmark.Add("Kids", outlines);
            root.Add(bookmark);

            outlines = new List<Dictionary<string, object>>();
            bookmark = new Dictionary<string, object>();
            bookmark.Add("Title", "Section02");
            bookmark.Add("Page", "4 FitH");
            bookmark.Add("Action", "GoTo");
            bookmark.Add("Kids", outlines);
            root.Add(bookmark);

            string pdfPath = "SamplePDF.pdf";
            PdfReader reader = new PdfReader(pdfPath);

            FileStream os = new FileStream("PdfCopy.pdf", FileMode.OpenOrCreate);

            Document document = new Document();
            PdfCopy copy = new PdfCopy(document, os);
            document.Open();
            copy.AddDocument(reader);

            //しおりを追加
            copy.Outlines = root;

            copy.Close();
            document.Close();
            os.Close();

        }

    }
}

なお、Pageは何ページ目 + 半角スペース + ページの開き方

を指定することになります。

PdfDestinationには下記項目が指定できるようになっているので

Fit, FitB, FitBH, FitBV, FigH, FitR, FitV, XYZ

http://api.itextpdf.com/itext/com/itextpdf/text/pdf/PdfDestination.html

それぞれ指定すると、しおりプロパティ

アクションタブ、倍率の表示が何になるか調べてみる。

全体表示Fit
100%表示
幅に合わせるFitH
描画領域の幅に合わせるFitBH
ズーム設定維持XYZ
カスタムFitB or FitBV or FitR or FitV

ということで100%表示に該当する物が見当たらなかった…

FitVは、カスタムと表示されますが、高さに合わせる状態になりますね。

FitRは、よくわからないのですが6400%(Max倍率)で表示されました。

FitBは、挙動がよくわからなかったです。


では、

ページ番号は以下のルール

1ページから3ページが、接頭辞「A」、インクリメントはアラビア数字

4ページ以降は、接頭辞「B」、インクリメントはアラビア数字

しおりは、

階層1−A1

階層2−A2

┣階層2−A3

┗階層2−B1

でそれぞれが1から4ページに飛ぶようにするにはこうなる

using System.IO;
using System.Collections.Generic;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            //既に書き込まれているPDFファイル
            string pdfPath = "SamplePDF.pdf";
            PdfReader reader = new PdfReader(pdfPath);
            FileStream os = new FileStream("PdfCopy.pdf", FileMode.OpenOrCreate);

            //空のドキュメントに対し複製する
            Document document = new Document();
            PdfCopy copy = new PdfCopy(document, os);
            document.Open();
            copy.AddDocument(reader);

            //ページ番号を追加
            AddPageNumber(copy);
            //しおりを追加
            AddBookmark(copy);


            copy.Close();
            document.Close();
            os.Close();
        }

        static void AddPageNumber(PdfCopy copy) {
            //ページ番号作る
            PdfPageLabels labels = new PdfPageLabels();
            labels.AddPageLabel(1, PdfPageLabels.DECIMAL_ARABIC_NUMERALS, "A");
            labels.AddPageLabel(4, PdfPageLabels.DECIMAL_ARABIC_NUMERALS, "B");

            //ページ番号を追加
            copy.PageLabels = labels;
        }

        static void AddBookmark(PdfCopy copy) {
            //しおり
            List<Dictionary<string, object>> root = new List<Dictionary<string, object>>();
            List<Dictionary<string, object>> lstOutline;
            Dictionary<string, object> bookmark;


            lstOutline = new List<Dictionary<string, object>>();
            bookmark = new Dictionary<string, object>();
            bookmark.Add("Title", "階層1−A1");
            bookmark.Add("Action", "GoTo");
            bookmark.Add("Page", "1 FitH");
            bookmark.Add("Kids", lstOutline);
            root.Add(bookmark);


            lstOutline = new List<Dictionary<string, object>>();
            bookmark = new Dictionary<string, object>();
            bookmark.Add("Title", "階層2−A3");
            bookmark.Add("Action", "GoTo");
            bookmark.Add("Page", "3 FitH");
            lstOutline.Add(bookmark);

            bookmark = new Dictionary<string, object>();
            bookmark.Add("Title", "階層2−B1");
            bookmark.Add("Action", "GoTo");
            bookmark.Add("Page", "4 FitH");
            lstOutline.Add(bookmark);

            bookmark = new Dictionary<string, object>();
            bookmark.Add("Title", "階層1−A2");
            bookmark.Add("Action", "GoTo");
            bookmark.Add("Page", "2 FitH");
            bookmark.Add("Kids", lstOutline);
            root.Add(bookmark);

            //しおりを追加
            copy.Outlines = root;
        }
    }
}

慣れれば簡単(?)ですね。

2014-03-10

売却してみた。

ということで滅多にしないことなのでいろいろメモ。


前回車の廃棄に依頼した某巨人的な名前の会社にお願いしました。

(たぶん、でかいとこのほうが回転率が高くて高めに

 引き取ってくれるんじゃないかという素人的発想)


車は、ホンダフィットで試乗車として使われていたのか

1000km程度しか走っていなかったのを買ったのが1年前。

保険の切れるタイミングと4月をまたぐと自動車税が発生するのと

今年は車検が切れるタイミングなので、1年のサイクルから判断して

手放してみた。


物自体、走行数が少ないこともあってかなりよい状態で

備品も、カーナビからバックモニターまでついていて申し分のない限りです。

こんな状態なんでそれなりの値はつくのかな?

と、思ってふたを開けてみたら、金額は35万円でした。

買ったのが100万なんで1/3も戻ってこればよいほうなんじゃないかと。


ちなみに、自分が買った時は、3月で決算時期だと少しでも

売り上げをあげたいだろうから、安くなったりするのかな??

と思ってこの時期にしたわけですが、そんな経験があったんで

3月の頭に手放すのは、他の月よりはまだよいだろうと思って

の行動だったわけだけど、実際は1〜2月がメインで3月は

在庫整理期間に突入するので、今から売ると中古ショップ

新たに入荷しようとしないため、3月に手放すのはあまりよくないらしい。

ということで、売る時は1月2月あたりがよいらしい。


もう一つ。

あまり車を乗らない地域にいるのであれば走行数は少ないと

売る時によい印象を与えられるかもしれないので、買う時の考慮にしたいなー

というのを学んだ。



ちなみに維持費のお話。

自分の場合、保険が6万くらいで、自動車税が3.4万で

あとは、走ったガソリン代程度。たかだか1万ちょっとくらいだと思う。

後は駐車場代で10万ちょっとくらい。

車を持ってない人にしてみればそんなにかかるの??

という感じでしょうが、最小限の費用しかかけてないと思います。


参考までに、北関東にいたころ。

年間1万6000km走ってました。

通勤では、2000km程度しかつかっていないので

単純計算だと毎週200km以上走っていたことになり

どんだけ車のっているんですか?というお話なんですけど・・・


当時の車は、1リットル10kmしか走らなかったので

ガソリン代:1リットル150円として計算すると

年間24万円ガソリン代で消えてました。

おそろしいもんです。

おまけに移動するってことはどこかで遊んだりしてるわけなのでその都度出費をするわけなんで、走行距離に比例して移動先での出費もそれなりになるわけです・・・


それプラス保険税金車検駐車場代で20万くらい。

さらに、消耗品の交換(タイヤスタッドレスタイヤブレーキ、オイル、フィルタタイミングベルト)そのたもろもろ。

でいったいいくらだったことやら・・・


まぁ、そんなこんなで60万とか70万とかそんな感じでした。

2014-03-02

DateTime関係のメモ。

自分好みのブログを作っていてメモ。

permanentリンク用に

/年(4桁)/月(2桁)/epoch

としていた場合に、データベースにDateTime型で保存している日付に対し、どうやってもってくるかというメモ。


ctimeがDateTime型で保存している列とします。

SELECT Col1, Col2, Col3 
FROM TableName 
WHERE date_format(ctime, '%Y') = 2014
    AND date_format(ctime, '%m') = '03'
    AND unit_timestamp(ctime) = 1393768339

date_formatでDateTime型から取得したい部分を指定。

シングルクォートで囲んでいるのは、文字列(0付きの2桁)として比較してくれる。ないと数字として比較される。

エポックタイムは、unit_timestampでDateTime型から変換してくれる。