2008年12月23日火曜日

第102回目 やはりエディタは重要(その2)

○第102回目 やはりエディタは重要(その2)

 質問です。前回紹介のマクロはどのくらい便利なのでしょうか。
 名前はなんというのでしょうか。

 名前は、"どこでもコピデル"といいます。コピー等の編集機能のマクロとしては、まあまあ使えると思っています。

 まず、ちょっとなあという点から。

□このマクロでにおける違和感は、二回目以降の処理の時です。コピーを例にとりますと、通常の操作では、あれをここにという発想になります。しかしながら、このマクロは、ここにあれをということになります。1回目は"ここに"の指定が省略されていますので気にならないのですが、2回目以降、ああそうだった、そうだった、となることがあるのです。
=⇒誤った操作をした場合ために、処理を初期化(回数を0とする)する機能をESCとして追加しました。処理区分はそのままです。
□次にあるのは、連続処理とってもほとんど1回か2回で終わってしまうことです。連続処理のメリットが感じられないなあ、ということです。
=⇒これはどうしようもありません。
□また、先頭位置の指定後に、終了させてしまうと、選択が残っているので、通常処理にするには、ESCキーを押す必要があるということがあります。キーが効かないということになります。それは選択状態で終わってしまったということが原因です。
=⇒終了する時に、選択状態である場合は、選択状態を解除するという処理を入れました。
□Shiftを押すと、最後に修正した箇所へ飛びますが、癖でShiftを押してしまうことがあります。すると、思わぬところへ移動してしまうので、元の場所がわからなくなることになります。Shiftの機能をはずすのも忍びないし…。
=⇒大きく移動する時(しばらくはShiftの場合に限定)には、移動前の場所を取得しておき、その場所に戻るという機能をF9として追加しました。デフォルト値は、貼付先とします。
=⇒見出し一覧から移動した場合、元の場所へに戻るというケースにも対応しました。
□これをあそこにコピーしたいといった時、いったんあそこにいってから、ここに戻ってくることになり、カーソル操作に無駄がでる。
=⇒コピーの逆指定機能をF8として追加しました。最初からでも逆指定できます。(やればできるものですね)
□クリップボードのある部分を貼付けられないか。つまり、QTCLIPを起動できないか。
=⇒通常通りのショートカットでQTCLIPを呼び出すことはできます。しかしながら、通常通りの操作では貼付まではできません。しかし、必要な文字列をクリップボードの先頭には動かすことができるので、その後それを貼り付けるという処理を行えばよいようです。クリップボードの内容を貼り付けるという機能をF4として追加できるかもしれません。
=⇒うまくいきません。QTCLIPの画面を出しておき、QTCLIPの中でマウスで必要な文字列を先頭に移します。QXエディタにもどりF4で貼り付ける、という方法が無難です。
□この機能を使えば、こういうことができるのでしょうか。このマクロの中でコピーしたものを複数回簡単に貼り付けること、ができる。どうなんでしょうか。
=⇒直前にコピーしたのはクリップボードの先頭にありますので、クリップボードからの貼付(すなわちF4)をすれば、それが貼付けられることになります。また、先頭になくても、前に紹介とたとおり、QTCLIPで該当文字列を先頭に持ってきて(マクロ中にできます)から処理を行えば、同様に複数回のコピーができます。
□直前のものだけではなく、前に貼付けたものも簡単に貼付けられませんか。
=⇒貼付けたものの履歴を取っておき、それを呼び出し、そこから指定したものを貼付けるという機能は面白いでしょうね。対応してみましょう。
□既存の文字にかぶせる、置き換えることはできますか。削除と一緒にできるといいですね。
=⇒難しそうですね。逆指定の中で何とかできるかもしれません。つまり、貼付先を指定するときに、範囲として指定するのですが、そうした場合、カーソルを改めて動かすことなく、貼付けると置き換わるのではないでしょうか。
=⇒だめですね、範囲の指定がいまのままだとできません。範囲の指定開始という情報を与えなければなりません。
 現状ではできないという整理にしておきます。
□削除の機能で、選択範囲が自動的に増えていくというのはどうでしょうか。適当なところで止めて微調整をし削除する、てな感じです、どうでしょうか。
=⇒面白いことを考えるなあという感想です。これ一辺倒というわけにはいかないので、区分する必要があります。DのかわりにFを押すと、この機能が動くというのどうでしょうか。止める時は矢印キーでもよさそうですね。そしてそれから通常の処理に戻して微調整をするということになるのでしょうか。プログラム上の表現としては、今のループに載せるか、もっと小さなループを作りまわしていくか。今のループの機能が複雑化するので、もっと小さなループを作るという選択でしょうか。
=⇒やってみました。確認できました。このことのノウハウで、見出し一覧から場所を指定する際の指示をenterキーで可能になりました。余禄がありました。
□それができるなら、コピーのときの範囲指定も同様にしてくれませんか。
=⇒徐々に要求が大きくなりますなあ。先ほどのものを基本に考えますと、現在のプログラムではそれほど複雑な処理になりません。通常処理での動作確認はしました。
□コピー元をどんどん蓄積していって、その累積を貼付というのはできませんか。
=⇒いろいろなところからもってきたものを、ここに貼付けるということになりますから用途はあると思います。この機能は別のマクロして整理しています。考え方が違うので、現在のマクロに入れるのは難しいと思っています。別のマクロを使ってくださいという整理でしょうか。多少は補助する機能として、前の場所にもどるという機能がありましたが、これで拡張します。貼付の後、そのあたりの場所で貼付位置を指定します。そして、F9をおすと、前のコピー元があった場所に移動するので、そこからコピー先を探しにいけることになります。少し一括貼付の一部を取り入れたことになるでしょう。
□日付とか、ファイル名とかを入力できませんか。短縮入力にそんなものが入っているんですが、短縮入力が使えればいいと思いますが。
=⇒短縮入力は3つ可能ですが、それらすべてということになると、入力値が多くなりますので、ひとつのみということで対応しましょう。いろいろなところから必要な文字列を引っ張ってこれるというコンセプトを満たしています。F6キーに設定してみます。
=⇒短縮入力の画面は出るのですが、その後の処理ができない。マウスも効かない。見出し一覧の場合とは違う。したがって、対応不能。
□さらに、あのファイルのものが使いたいが、開いていない。新しいファイルを開いてその中のものを使えないか。
=⇒右のエリアにファイルの履歴がありますが、それを開くのはマウスで可能なので開けるのではないかと思います。そのファイルに制御を移すために、ウィンドウのハンドル名を再取得する必要があります。
=⇒マウスは効きません。対応不能なので、マクロを終了させ、該当ファイルを開き、改めでマクロを実行してください。
□しおりの役目として、途中でスペースを入れたいと思う時があるのですが。
=⇒確かに、後で修正したいなというような場所、挿入した箇所をはっきりさせたい場所に、それとわかる記号を入れておきたい時がありますね。スペースがいいのですかね。もっとはっきりさせるために改行はどうでしょうか。いろいろとキーを使っていますので、あまり使えるキーが少なくなっているというのが現状です。Ctrlを押すことによって改行が挿入されることにしましょうか。
※最終形のマクロは各自の課題として内容掲載は省略します。

2008年12月14日日曜日

第101回目 やはりエディタは重要

○第101回目 やはりエディタは重要
 ※QXマクロは、QXエディタ(テキストエディタ)で使用されるマクロのことです。
  QXマクロでは、全角スペースはデータ以外は認められていません。コピーして使う場合は、必ず半角スペースに変えてください。

 第90回から9回ほどエディタでのマクロを紹介しましたが、使っていますと、その重要性を痛感します。
 事務屋としての仕事の半分ぐらいは、エクセルではなく文書の作成の人も多いのではないでしょうか。エディタは文書の体裁という面では弱いのですが、文書の内容を作るという面では非常に活用度が高いと思います。
 そんな思いがつのりますので、紹介したQXエディタマクロの機能拡大を紹介します。
 文書の編集としてのコピー等の機能です。第93回と第97回のものを中心にしています。

大まかな機能
 コピー(移動を含む)と削除を切り替えられる(初期値はコピーとします)。
 一字一字の削除は、処理区分にかかわらずdeleteキーでできる(下の場合を除く)。
 先頭位置の指定後のdeleteキーは範囲指定の終了と見て指定範囲を削除する。
 1回の処理ではなく、連続して処理ができる。
 ウィンドウの移動ができる。
 見出し一覧を表示し、そこから見出し単位で移動できる。
 タスクバーに現在の処理の状態の表示がでる(コピーか移動か削除か。貼付位置の指定か、例えばコピー文字列の先頭の指定か、最後の指定か)。=⇒これは本論ではないので、簡素化するために省略します。

【キーの操作内容】
 KEY_RETURN、KEY_SPACE=⇒場所の確定
 KEY_PRIOR =⇒上に大きく画面を動かす
 KEY_NEXT =⇒下に大きく画面を動かす
 KEY_UP、KEY_DOWN、KEY_LEFT、KEY_RIGHT =⇒上下左右にカーソルを動かす
 KEY_HOME、KEY_END=⇒文頭、文末にカーソルを動かす
 KEY_TAB KEY_BACK =⇒単語単位で左右にカーソルを動かす
 KEY_MULTIPLY =⇒コピー・移動の切替指示
 KEY_SUBTRACT =⇒なし
 KEY_F12  =⇒処理の終了
 KEY_F1   =⇒ウィンドウを左に移動
 KEY_F2   =⇒ウィンドウを右に移動
 KEY_DELETE =⇒一字削除。先頭位置が指定されている場合は、範囲の確定及びその範囲を削除
 KEY_SHIFT =⇒直前に修正した箇所に移動
 &H43(C)   =⇒削除からコピーに処理を切り替える
 &H44(D)   =⇒コピーから削除に処理を切り替える
 &H4D(M)   =⇒見出し一覧を表示する
 
 流れです。
 1.ウィンドウのハンドル名の取得と、現在のウィンドウの番号の取得
 2.最初のカーソル行、場所の取得。使用するキーの指定。初期値の設定
 3.大きなループ。F12が押された場合、終了
 4.小さなループ。コピー、移動、削除。処理を行った後は指定回数を初期化する。

 1.ウィンドウのハンドル名の取得と、現在のウィンドウの番号の取得
  '現在のウインドのハンドル名を取得する
   gen_hwnd = @hwnd
  '全ウインドのハンドル名を取得する
  j = 0
  hwnd[j] = @@GetHwnd(2)
  do while hwnd[j]<>0
      j = j + 1
      hwnd[j] = @@GetHwnd(0)
      if hwnd[j] = gen_hwnd then hwnd_no=j  '現在のウィンドウ番号を取得
  loop
  '全ウインドのハンドル名の数
   hwnd_cnt = j

 2.最初のカーソル行、場所の取得。使用するキーの指定。初期値の設定
 'カーソル行及び場所(バイト数)を記憶
 y_begin = @ScrLineToCrLine(@Line)   '物理行から論理行への変換
 
p1=@BytePosCr
'キーの設定
 call iskeypressed(KEY_RETURN)
 call iskeypressed(KEY_SPACE)
 《省略。【キーの操作内容】参照》
 call iskeypressed(&H44)
 call iskeypressed(&H4D)  'M
'初期値設定
 j = hwnd_no  'ウィンドウの移動は、最初は現在のウィンドウを基準とする
 flag = 1   '初期状態では、貼付場所が設定されているので、1回の指定がなされているという意味
 kubun$ = "1" 'コピーということ
 fugou = 1   '終了処理に利用

 3.大きなループ。F12が押された場合、終了
 do
  小さなループ
 loop while 1

 4.小さなループ。コピー、移動、削除。処理を行った後は諸情報を初期化する。
 どのキーが押されたのかは、3行目のようにif文でiskeypressed(KEY_RETURN)というふうにきいていく。
 do
 call _sleep(50) '無限ループなので、これがないとCPUが回りっぱなしになってしまいます。50とは千分率で、0.05秒休むという意味です。
 if iskeypressed(KEY_RETURN) or iskeypressed(KEY_SPACE) then
  if kubun$ <> "3" and flag=2 then exit do
  if kubun$ = "3" and flag=1 then exit do
  if kubun$ <> "3" and flag=0 then
   y_begin = @ScrLineToCrLine(@Line)   '物理行から論理行への変換
   
p1=@BytePosCr
   gen_hwnd = @hwnd  '現在のウィンドウのハンドル名を取得
  elseif kubun$ = "3" and flag=0 then   '削除処理で何も指定していない時
   y_begin = @ScrLineToCrLine(@Line)   '物理行から論理行への変換
   
p1=@BytePosCr
   gen_hwnd = @hwnd
   @BlockSelect
   FOR i=1 to ll005
    @MoveRightChar
   next
  else
   @BlockSelect
  end if 
  flag++
 elseif iskeypressed(KEY_DELETE) then
  if kubun$ = "3" and flag=1 then exit do
  @DeleteChar
 elseif iskeypressed(KEY_F12) then
  fugou = 9  '終了
  exit do
 elseif iskeypressed(KEY_MULTIPLY) then  '*が効く
  if kubun$ = "1" then
   kubun$ = "2"  '移動
  else
   kubun$ = "1"  'コピー
  end if
 elseif iskeypressed(KEY_F1) then  'ウィンドウを左に移動
  if kubun$ <> "3" or flag <> 1 then '削除で1回指定後は移動できない
   j = j + hwnd_cnt - 1
   j = j mod hwnd_cnt
   @@Activehwnd hwnd[j]
  end if 
 elseif iskeypressed(KEY_F2) then  'ウィンドウを右に移動
  if kubun$ <> "3" or flag <> 1 then '削除で1回指定後は移動できない
   j = j + 1
   j = j mod hwnd_cnt
   @@Activehwnd hwnd[j]
  end if 
 elseif iskeypressed(KEY_SHIFT) then
  @MoveLastModify  '最後の修正があった場所に移動。意味が薄い。
 elseif iskeypressed(&h43) then  'C
  if kubun$ = "3" and flag = 0 then  '3は削除のこと。ここでは、何も指定されていない時に、Cが押された。
   kubun$ = "1"
  elseif kubun$ = "3" and flag = 1 'ここでは、削除中で、先頭が指定されたいる時に、Cが押された。
   kubun$ = "1"
   @BlockSelect  '選択を解除するという意味。
  end if
 elseif iskeypressed(&h44) then  'D
  if kubun$ <> "3" and flag = 0 then
   kubun$ = "3"
  elseif kubun$ <> "3" and flag = 1 then
    kubun$ = "3"
    @BlockSelect
    FOR i=1 to ll005
    @MoveRightChar
   next
  end if
 elseif iskeypressed(&h4D) then  '見出し一覧表示
  @ListCaption
 elseIf iskeypressed(KEY_PRIOR) then
  @ScrollUpHalfPage
 elseif iskeypressed(KEY_NEXT)  then
  @ScrollDownHalfPage
 elseif iskeypressed(KEY_LEFT)  then
  @MoveLeftChar 
 elseif iskeypressed(KEY_UP)  then
  @MoveUpChar
 elseif iskeypressed(KEY_RIGHT) then
  @MoveRightChar 
 elseif iskeypressed(KEY_DOWN)  then
  @MoveDownChar
 elseif iskeypressed(KEY_END)  then 
  @MoveEndLine
 elseif iskeypressed(KEY_HOME)  then
  @MoveBeginningLine
 elseif iskeypressed(KEY_TAB)  then  '逆にした。感覚的なもの
  @MoveLeftWord
 elseif iskeypressed(KEY_BACK)  then  '逆にした。感覚的なもの
  @MoveRightWord
 end if
 loop while 1
 if fugou = 9 then exit do '終了の場合
'指定範囲をクリップボードへ、クリックからコピー。削除は単純に削除する
 if kubun$ = "1" or kubun$ = "2" then
  if kubun$ = "1" then
   @BlockClipboardCopy 
  elseif kubun$ = "2" then
   @BlockClipboardCut 
  end if
  @@Activehwnd gen_hwnd   '貼付先のウィンドウに移動
  @Line = @CrLineToScrLine(y_begin)
  @BytePosCr=p1
  @BlockClipboardPaste
 else
  @BlockDelete
 end if
 flag = 0
(この後に大きなループの loop while 1が続く)

2008年12月6日土曜日

第100回目 複数ファイルの処理

○第100回目 複数ファイルの処理
 複数ファイルを対象としたマクロとしては、『第89回目 ブック(ファイル)を超えての処理の仕方』でフォルダー内のエクセルファイルに対して、置き換えの処理をするという、ひとつの形を紹介しました。
 ここでは、同一フォルダという制約を取り外してものを考えます。すなわち、与えられたフルパス名のファイルを処理対象にします。処理内容は、ある範囲のデータを集めるという処理にしました。データを集約するファイルは、別のものとなります。そのファイルを開いておいて、フルパス名、シート名、シート上のタイトルのある座標、データの座標範囲、貼付先の先頭座標をセルに書き込んで、その範囲を指定してから実行とします。
 簡単にするために、コピー元のファイルは同一形式になっているので、シート名、シート上のタイトルのある座標、データ範囲の座標はすべて同じとします。(ここを変えたいものにするにはセルにその情報を書きこんでおいて、その情報を使用してください。複雑度は増しますが、原理的には簡単だと思います。注意、貼り付ける列の数の取得も忘れずに)

 流れです。
 1.指定された範囲から、フルパス名(コピーもとのファイル、複数)、シート名(同一としたので一つ)、シート上のタイトルのある座標(同一としたので一つ)、データの座標範囲(同一としたので一つ)、貼付先での先頭ファイル用の貼付け座標(順次自動的にずらしていくので一つ)を取得します。ここで、ファイルの数を求めます。
 2.指定されたファイルを開き、シートを指定して、指定されたタイトルをコピーします。
 3.貼付先のファイルに移動し、貼付先の2行目(列はデータと同じ)に貼付ます。
 4.指定されたファイルに移動し、指定された範囲のデータをコピーします。処理が終わったらファイルは閉じておきます。
 5.貼付先のファイルに移動し、貼付先の先頭座標に貼付ます。このときは、値のほかに書式も貼り付けますので、貼付は2回となります。
 6.次のファイルを開いて、2から5をファイルの数だけ繰り返します。
  2回目以降は、貼付先の先頭座標が、貼付けた列の数だけ変わりますので注意してください。

 1.指定された範囲から、フルパス名、シート名、データの座標範囲、貼付先の先頭座標を取得します。ここで、ファイルの数、コピーするデータの個数を求めます。
 先頭座標から下に向かって、フルパス名を取得します。
 シート名以下は、二つ隣の列にはいっています。
 準備処理として、最初のファイルの名前、シート名を取得しておきます。
  Application.DisplayAlerts = False '警告・確認メッセージを表示しない
F_name = ActiveWorkbook.Name
sh_name0 = ActiveSheet.Name

'フルパスファイル名、シート名(一つ)、シート上のタイトルのある座標、コピー元の座標(一つ)、貼り付け座標(一つ)を取得する
  gyo1、retu1は指定した範囲の先頭の座標です。
For i = 1 To 100
If Cells(gyo1 + i - 1, retu1) = "" Then Exit For
FName(i) = Cells(gyo1 + i - 1, retu1)
Next
f_cnt = i - 1 'ファイル数
'シート名(一つ)以下の項目
sh_name = Cells(gyo1, retu1 + 2)    'シート名
title_A1 = Cells(gyo1 + 1, retu1 + 2) 'タイトルの座標
moto_A1 = Cells(gyo1 + 2, retu1 + 2)  'コピーもとのデータの範囲座標
saki_A1 = Cells(gyo1 + 3, retu1 + 2)  'データの貼付先の先頭座標。タイトル、ファイル名もうちますので、十分下に設定してください。
  '貼付けるデータの列数
p1 = InStr(moto_A1, ":")
p2 = Left(moto_A1, p1 - 1)
p3 = Mid(moto_A1, p1 + 1)
retu11 = Range(p2).Column
retu12 = Range(p3).Column
retu_cnt = retu12 - retu11 + 1  '貼付ける列の数
  '貼付先の座標の列番号、行番号化
retu21 = Range(saki_A1).Column
gyo21 = Range(saki_A1).Row

 2 - 6の処理
'ファイルを開き、文字列を変換し、ファイルを閉じる。メッセージは表示させない。
 ll000 = 0 'コピーした間に空白列をおく場合は、1以上を入れる
For i = 1 To f_cnt
'ファイルを開く
Workbooks.Open Filename:=FName(i)
F_name1 = ActiveWorkbook.Name
Worksheets(sh_name).Select
Range(title_A1).Copy
'コピー先ファイルに戻り、タイトル(1行目)、ファイル名(2行目)を貼り付ける
Windows(F_name).Activate
Worksheets(sh_name0).Activate
Cells(1, retu21 + (i - 1) * (retu_cnt + ll000)).PasteSpecial Paste:=xlPasteValuesAndNumberFormats, Operation:= _
xlNone, SkipBlanks:=False, Transpose:=False
Cells(2, retu21 + (i - 1) * (retu_cnt + ll000)) = F_name1
'コピー元ファイルに戻り、データを取得する
Windows(F_name1).Activate
Worksheets(sh_name).Select
Range(moto_A1).Copy
'先ファイルに戻り、データを貼り付ける
Windows(F_name).Activate
Worksheets(sh_name0).Select
Cells(gyo21, retu21 + (i - 1) * (retu_cnt + ll000)).PasteSpecial Paste:=xlPasteValuesAndNumberFormats, Operation:= _
xlNone, SkipBlanks:=False, Transpose:=False
Cells(gyo21, retu21 + (i - 1) * (retu_cnt + ll000)).PasteSpecial Paste:=xlPasteFormats, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
'コピー元ファイルに戻り、ファイルを閉じる
Windows(F_name1).Activate
ActiveWorkbook.Close SaveChanges:=True
Next
 Application.DisplayAlerts = True '警告・確認メッセージを表示する

 ※実際の業務で行った例では、ファイル数は23個でした(1グループあたり)。数が多いことが考えられをますので、ファイルのフルパス名の簡単な取得も考えておくべきだと思います。筆者はエクスプローラは通常使っていませんので、どのようにスムーズに取得するのか不明です。(筆者の使っているファイラーでは、一覧表からキーボードでファイルを指定(スペースキー)して、Ctrl+Cでそれらのファイルのフルパス名がクリップボードにコピーされ、それをエクセルに貼付ければ終わりになります)
 ※データを貼付後、23を4つにわけ集計することになりました。集計項目は人数と金額1,2(ともに平均)でした。問題を簡単にして、人数、金額1,2(ともに平均)がその順番で横に23個つながっています(縦項目は所属で50個あります)。たて項目ごとに4つに集約するには、どうしたらスムーズにいくのかというテーマは頭の体操となるものと思われます。(実際のやり方は、地道に加重平均を取る計算式を作っていけばいいのですが、スムーズに作成するにはどうしたらいいのかということがテーマです。平均して6個(23/4)の加重平均となります。地道にやればできますが結構大変です)


 ※今回紹介したマクロは、非常に自動化の効果がありますので、違うシート、違うデータ範囲のものにまで一般化しておくと、いいと思います。