4D」カテゴリーアーカイブ

4D ローカル変数名にコロンが含まれていた

4Dでは、メソッド間で、たとえば呼び出す側が2つ引数を渡す時、呼び出される側のメソッドは第1引数を$1で、第2引数を$2で参照する。しかし引数名のまま$1や$2で参照すると、渡される値が何かがわかりにくいし、後で引数の順番が変わった時に不具合の元になるため、次のように記述して、引数をローカル変数に代入してから使うことにしている。こうしておけばREAD ONLY以降のコードを再利用しやすくなる、というオマケも期待できる。

C_LONGINT($1;$tr_id)
$tr_id::=$1
C_OBJECT($2;$objTR)
$objTR:=$2
C_TEXT($0;$numOfRecs)
READ ONLY([TRADEMARK])
QUERY([TRADEMARK];[TRADEMARK]TR_ID=$tr_id)
$numOfRecs:=Records in selection([TRADEMARK])
If ($numOfRecs=1)
  //オブジェクトで値を返す
$objTR.bikou:=[TRADEMARK]TR_BIKOU
End if 
$0:=$numOfRecs

上記のコードをよくみて欲しい。一つ目のローカル変数名にコロンが含まれていたのだ。これが不具合の原因だったのにしばらく気付かなかった。4Dは変数名にコロンを含んでていても構文エラーにならない。

たとえば「$tr_id:」をダブルクリックすると、「$tr_id」部分(コロンなし)が選択される。コピーして検索ダイアログを表示してペーストして検索実行すると、以下のコードの「$tr_id」は検索にヒットするため検索条件は正しく記述されているように見えて誤りに気付きにくい。ダブルクリック時に「$tr_id:」(コロンつき)を選択できていれば、検索実行で以下のコードにコロンつきの変数名がヒットしないことがわかったはず。そこで、変数名のタイプミスに気づくはずだ。

実行時のエラーにもならない。「$tr_id:」という変数に$1の内容を代入、QUERYでは未代入の「$tr_id」(値はゼロ)でクエリーしているだけだからだ。デバッガーで見ると呼び出した側は正しい値を与えているのに、なぜか期待した値がヒットしない、という結果になる。

「変数名にコロンを使える」という4Dの仕様に問題があるような気がする。「=」とか「-」を変数名に含めると構文エラーになるのに。

コロンを変数名に含めて良いのであれば、コロンが含まれた変数名をダブルクリックして選択した場合にコロンも選択範囲に含めてほしい。誤りに気づきにくい仕様になっていると思う。今回たまたま画面解像度が高くて文字が小さくなっていて、さらに焦ってコーディングしていたという事情はある。それにしてもこれまでこの事故が起こらなかったことが不思議だ。目視ですぐに気づいていたのか、こんなところでタイプミスはしなかったのか、老眼が進んでいなかっただけなのか?

試してみたらプロセス変数名も同様にセミコロンを含めることができた。見れば分かるだろというコーディングミスではあるけど要注意、というお話。

今回のハマりレベルは3、一人では抜け出せなかった。ハマりレベルとは、1:自力解決、2:ドキュメントで解決、3:他者によるサポートにより解決、4:翌日以降に解決、5:未解決。

4Dのプロセス変数にゲッターセッターを記述するのはもう古い?v18で提供されたClassesのFunctionが使えそう。

プロセス変数とはプロセス内で参照可能な変数のこと。プロセスというのは同じマシンならNew Processなどで区切られたメモリー上の作業空間とでも言えばいいか。Client/Server環境ではサーバサイドで実行などにより実行マシンが違えば作業空間は異なるので別プロセスだ。別のプロセスの変数はそれ用のコマンドを使わないと参照できない。

人それぞれ違うと思うが、ウチの開発スタイルではプロセス変数は次の2種類がある。

(1)フォームオブジェクトに割り当てるためのプロセス変数
(2)プロセス内でグローバルに参照するために用意する主として制御用のプロセス変数

(1)は4Dの仕様上必要な変数。フォームが表示されている間はメソッドからフォームオブジェクトを参照したくなるはずで、メソッドが終わってもフォームがある限り解放されないプロセス変数であることが合理的だ。個人的に、次のように名前を付けている。

・ vPL01_btnOK:PL01というフォーム上の「OK」ボタンという意味。ボタンの場合はプロセス変数に代入することはない。オブジェクト名を参照しているだけ。

・ vPL01_lstPL:PL01というフォーム上の「lstPL」リストボックスという意味。vPL01_lstPL_IDやvPL01_lstPL_NAMEなどを列として定義、DBのPLというテーブルから持ってきた値を表示する。プロセス変数はプロセス開始時に領域が確保されるが、リストボックスはOnload前は参照できないので注意。

・ vPL01_txtPL_NAME:PL01というフォーム上の「PL_NAME」フィールドで、DBのPL_NAMEから持ってきた値を表示するためのプロセス変数という意味。

変数名とオブジェクト名は別の名前をつけることもできる。どちらもプロセス開始時に領域を確保されてしまう。特に困ったこともないので「オブジェクト名と変数名はいつも同じ」にしている。同じ値のオブジェクトには同じ変数名をつければどちらも同じ値が表示される。が、別々の名前をつけて値はコードで代入し直す方が主流だ。

(2)は、フォーム上に表示されない、プロセス内の制御用の変数。例えばよく使う変数としては、ダイアログを表示するメソッドの場合にどのフォームから呼ばれたかを示すモードのような変数「vPL01_varMode」とか、一覧で選択されていたIDを詳細画面で保持するための変数「vPL_varPL_ID」とか、印刷時に現在のページ数を印字するための変数「vP01_varPageNr」など、がある。

このような変数にはいわゆるゲッター/セッターを用意して直接参照はしないようにする。フォームオブジェクト以外は、基本的に変数はローカル変数にすべきで必要な関数に引数で渡して使う、という考え方がまずあって、オブジェクト型の変数が無かった時代は多くの引数が必要になってしまって面倒すぎるのでグローバル変数を使いたい、という時代背景がある。例えば「vPL01_varMode_get」と「vPL01_varMode_set」である。次のように使う。

//PL01_DefInit
・・・
C_TEXT(vPL01_varMode)
・・・

//vPL01_varMode_get
C_TEXT($0)
$0:=vPL01_varMode

//vPL01_varMode_set
C_TEXT($1)
vPL01_varMode:=$1

//PL01_SetContorolsValues
・・・
//新規追加モードの場合、削除ボタンは非表示にする
C_TEXT($mode)
$mode:= vPL01_varMode_get
if ($mode=“add”)
 OBJECT SET VISIBLE(*;”vPL01_btnDelete”;False)
end if
・・・

このように記述していると、メソッド内にはフォームオブジェクト以外のプロセス変数が現れなくなる。このやり方は、まだ4Dに不慣れな頃に師匠から伝授されたものだ。初めはなんでこんな面倒なことをするのだろうと思っていたが、今でもこのやり方を踏襲している。実はこの種のプロセス変数はそんなに多くない。メソッド数が増えてしまうが、たいして手間ではないし、このようにすることでデバッグタイムが少なくなっていると感じている。教えてくださった先輩に感謝!

この方法の欠点としてメソッド数が増えてしまうと書いた。わずか2行のコードのために新しいメソッドを作る手間も感じていたがv18で改善された。ClassesのFunctionを使えば、オブジェクト表記の延長で「vPL01_varMode.get」などとメンバー関数を記述できる。そしてなんと一つのメソッドエディタ内に複数のFunctionを定義してゲッターとセッターを記述できるようになるのだ。

Laurent Esnault氏のセッション。今年の4D Summit 2020はオンラインで無料。英語がダメでも画面見てれば大体わかる素晴らしいデモに感謝。その内容に感激!4Dライフが大きく変わること必至。

まだv13のプロジェクトもあるが、コーディング規約を改訂するときがきたようだ。

4DのDrug and Drop

4D Drug & Drop

ひとつの画面に複数の画像を表示する仕組みを考えていた。画像は外部ファイルを想定、多くて10個くらいあるのだが、要望としては代表的な画像を2個登録したい、ということだった。例えば画像は2個と限定してピクチャー変数を2つ配置しておけば、ドラッグ&ドロップを2回やれば画像登録ができる。操作も実装も簡単だ。しかしプログラマーとしては、3個以上の画像がありうるのに2個に制限して良しとするのは敗北感がある。

そこで考えた。リストボックスがいいんじゃないか?リストボックスなら画像をいくら追加しても大丈夫。行が増えていけば勝手にスクロールしてくれる。画像の数を制限しなくて済む。画像は別テーブルに保存すれば良い。

いつものようにリストボックスの上に[+]と[ー]のボタンを置いて、[+]クリックで表示されたダイアログにピクチャー変数を配置してドラッグ&ドロップしてもらって、という仕様で実装した。

これだとダイアログボックスを開いて閉じるのが面倒。もともとの要望の通り、ピクチャー変数を2個、配置しておけば、ドラッグ&ドロップを2回やれば画像登録ができる。その方がユーザは簡単だ。同じように簡単な操作で複数の画像を登録する方法はないのだろうか?

リストボックスに画像をドロップすればいいのでは?

リストボックスに画像をドロップしたら行が増えていくようにすれば操作性は悪くない。ドロップのたびに行を追加、削除するときは[ー]ボタン。増やしたり減らしたり、縦スクロールなら実装も簡単そうだ。

ただリストボックスに画像をドロップしても標準的な機能では受け取ってくれない。リストボックスのOnDropイベントにコードを書くことになる。OnDropで配列要素を増やして、画像列に取得した画像を代入すればいい。と、ここまでわかったところで気がついた。一般に4DでDrug&Dropをい実装する場合は、フォームオブジェクトが勝手に処理してくれるからコードを書く必要がない。マニュアルサイトを見ても、イベントを取得したあと、ドロップされたコンテンツをどうやって受け取るか、よくわからない。

とりあえずリストボックスにイベントハンドラを書いて。

ドロップされた画像はドラッグ&ドロップ専用のペーストボードから取得するそうです。

GET PICTURE FROM PASTEBOARD($picture)

OnDropで呼ばれるコードはこれ。

リストボックスに行を追加したり、自動スクロールさせたり、ついでにドロップされたファイルのパスを取り出したり、している。

ドロップされた直後に行を選択して、自動スクロールさせていてる

4D macOSでプロジェクトのパッケージ化をやめる

いつも使っているのがMacBook Pro(13-inch 2018)なので、4DのプロジェクトもmacOSで開発している。Windowsで動かす場合もコーディングはmacOS。Windowsを使うのはたまにフォームを調整する時だけだ。最近わかったことだがメイリオフォントを使うとWindowsとほぼ同じレイアウトになる。例えばリストボックスの列幅にはソートマーク表示用に余裕を持たせるなど、少しのケアで調整はいらなくなる。要はmacOSとWindowsで同じフォントを使えば良いのだ。MSゴシックやMS明朝もあるけどこちらは試していない。

さて本題。4D macOS版でプロジェクトを作ると、ストラクチャファイル(「.4DB」ファイル)は「.4dbase」という拡張子がついたフォルダの中に生成される。この. 4dbaseフォルダがmacOSの「パッケージ」だ。パッケージは特殊なフォルダで、アイコンをつけたり、ダブルクリックで中の所定のファイルを開くことができる、macOSが提供してくれる便利?な機能だ。

プロジェクトフォルダはパッケージなので選択しただけでは中は見えない

例えばmacOSのアプリケーションフォルダに並んでいるアプリはダブルクリックすると起動する。アイコンもついてる。実行ファイルのように見えるが実態は「.app」という拡張子がついたパッケージ。多数のファイルで構成されているアプリでもパッケージでまとまっているためにスッキリしている。アプリを起動する時、アプリケーションフォルダを開いて、そこに並んでいるアプリのフォルダを開いて、その中のアプリ本体を見極めてダブルクリックするよりは、アイコンのついた.appをダブルクリックした方が楽だろう。1階層ほじらなくて済む。

4Dのパッケージに話を戻そう。この.4dbaseのアイコンをダブルクリックすると4Dが起動してプロジェクトが開く。4Dでアプリ開発の続きをやるだけなら1階層ほじって.4DBファイルをダブルクリックする必要はない。

リリース時にはストラクチャファイルやデータファイル(「.4DD」ファイル)を操作することになる。ストラクチャファイルを取り出して圧縮したり、開発用のデータファイルを本番データに入れ替えてから圧縮したりする。プロジェクトのパッケージを右クリックして「パッケージの内容を表示」を実行する。ウインドウに表示された.4DBファイルを選択して、ドラッグしたりして移動させてから圧縮する。

パッケージを右クリック

元のフォルダに戻るときはウインドウタイトルを右クリックしてプルダウンメニューを表示させて、階層が1つ上のフォルダを選択する。このようにmacOSで作業するときはパッケージの中に入ったり出たりしている。

ウインドウタイトルを右クリック

これってもしかして面倒臭い?

Windowsに持っていくとパッケージはフォルダとして認識されるし、そもそもパッケージ化が必要なのかを自問してみると、

1)パッケージをダブルクリックしてプロジェクトを開くことはしない
 → パッケージに中の「.4DB」ファイルをDockの4Dアイコンにドロップして開いている。ちなみに現在のDockにはv15からv18までの4Dと4D Serverが並んでいる。

2)「Resources」フォルダ、「WebFolder」フォルダ、「Components」フォルダの中身を操作することは多い

3)データファイルを操作する
 データが巨大なために「.4DD」ファイルを差し替えてから圧縮するプロジェクトもある。

4)いつものことだが頻繁にリリース作業を行っている
 ビルドしてリリースするものもあるが、「.4DB」ファイルと「.4Dindy」ファイルを圧縮して送ることが多い。パッケージの中に入ったり出たりすることになる。

長年付き合ってきて名残惜しい気もするがパッケージは邪魔でしかないという考えに到達した。やめるのは簡単だ。パッケージについている「. 4dbase」という拡張子を削除するだけ。4D的には何も問題はないらしい。現にWindowsではこの機能は使えないし。

やってみたら快適だった。クリックするだけでプロジェクトフォルダに出たり入ったりできる。当たり前だけど。

普通のフォルダ

少しだけ弊害があった。プロジェクトフォルダがわかりにくくなったのだ。今まではアイコンがついていたので識別しやすかったし、パッケージの中は4Dの管理下にあるファイルでその外側は自分で入れたファイル、という境界をはっきりと意識できていたのが薄れてしまった。なるほどパッケージにも一利あったのだ、と思いつつも面倒が減ったからこれでしばらくやってみよう。

4D v18のテキストベースプロジェクトでgit

4Dはv18から、プロジェクト全体をテキストファイルに書き出してgitで管理できるようになった(涙)。これまでは自力でメソッドを外部テキストファイルに書き出して共通ライブラリメソッドだけgitでバージョン管理していた。フォームはテキストに書き出すことができないし、データ構造は別の仕掛けで書き出していたり面倒だし、なんといっても自力で書き出した情報は一体として動くプロジェクトを保証するものではなかった。

早速やってみよう。今回の目的は、半年間の間に行われたあるプロジェクトの変更内容を調べるというものだ。ローカルリポジトリを使って半年前のプロジェクトと今のプロジェクトを比べる。リモートのリポジトリは不要だ。

ステップ1 プロジェクトに書き出し

20190530のソースv17をv18で開いて「ストラクチャーをプロジェクトに書き出し」を実行。パッケージの中に「Project」フォルダができている(A)。

「Sources」フォルダを見てみると感動。フォーム、メソッド、テーブルフォームとそれらやオブジェクトのメソッドが並んでいる。フォームには「.4DForm」メソッドには「.4dm」という拡張子がついているのですぐわかる。

20191217のソースv17をv18で開いて「ストラクチャーをプロジェクトに書き出し」を実行。同じように「Project」フォルダができている(B)。

ステップ2 gitリポジトリを作る

次にgitフォルダを作る。ここでは「git_work」というフォルダを作っておく。ここにgitのリポジトリを作るのだ。gitのコマンドラインツールを使う方法もあるけど、差分をグラフィカルに見たいので今回は「GitHub」アプリを使った。

File -> New Repositoryを実行。リポジトリ名「dcc_git」、git_workを選択する。

ステップ3 リポジトリに「Project」ファイルを保存してコミット

(A)を「dcc_git」フォルダにコピー。GitHubでリポジトリを開くとソースが並んでいる。

SummaryとDescriptionをタイプインして[Commit to master]をクリック。これで最初のテキストファイル群がgitリポジトリに書き込まれた。

ステップ4 比較対象の「Project」ファイルを保存

最初にコピーした(A)を(B)で上書きして(A)がなくなってしまうのだが、ご心配なく。すでに(A)の内容はgitのコミットコマンドによって.gitファイルにコピーされているのだ。.gitは隠しファイルなのでターミナルアプリを使って確認できる。さあ(A)を上書きして(B)を置き換えよう。

ステップ5 GitHubで変更箇所を確認

GtHubアプリを起動して、リポジトリ「dcc_git」を見ると...。48箇所変更されていると表示されている。4dFormをクリックすると...うう感動。フォームの修正箇所、この場合座標値が変更されているのがわかる。うれしい。

フォームの座標値が変更されたことがわかる。ピクセル単位。
メソッドはこんな風に

修正箇所がすべてリストアップされている。やってくれました4Dv18。これからの4Dライフが大きく変化しそうな瞬間でした。

思えば2014年のDeveloper Summitでフランスに行ったとき「フォームをプログラムで作成したり修正することができないの?」と質問、そのときはv15では予定なしと言われた。今回のテキストベースプロジェクトの仕組みを使えば、メソッドでフォームを生成したり、修正することができる。こんな形になって実装されてくるとは。