VBからの入門者でVBから移植作業の最中です。(Win-XP + Delphi6 パーソナル)
HELPで見てシェープコントロールなどを生成できることを知り、実験ではForm上に上手く生成できましたが、これをImage1の上に生成したいと思っていますが方法がわかりませんので教えてください。(下記コードでForm上にはできました)
それから同時にこのシェープを配列化する方法があったら教えてください。よろしくお願いします。
procedure TForm1.Button3Click(Sender: TObject);
var
i: Integer;
const
NamePrefix = 'Myshape';
begin
for i := 1 to 20 do begin
Tshape.Create(Self).Name := NamePrefix + IntToStr(i);
with Tshape(FindComponent(NamePrefix + IntToStr(i))) do
begin
height:=15;
width:=15;
shape:=stcircle;
brush.Color:=rgb(random(255),random(255),random(255));
Left := 180;
Top := i * 20;
Parent := self;
end;
end;
end;
> これをImage1の上に生成したいと思っていますが方法がわかりませんので
> それから同時にこのシェープを配列化する方法
TImage も TShape もウィンドウをもたない TGraphicControl の派生クラスです
から、共通の Parent に対して、後に作成された方が上にくるはずです。
var
ShapeList: array[1..20] of TShape;
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
begin
for i := 1 to 20 do begin
ShapeList[i] := TShape.Create(self);
with ShapeList[i] do
begin
height:=15;
width:=15;
shape:=stcircle;
brush.Color:=rgb(random(255),random(255),random(255));
Left := 180;
Top := i * 20;
Parent := self;
end;
end;
end;
で、設計時に画像を読み込んだ Image1 より上にきます。
> with Tshape(FindComponent(NamePrefix + IntToStr(i))) do
> begin
> height:=15;
BringToFront; //これとか?
えーと様、大変ありがとうございます。極めてシンプルなコードでおかげさまで一気に配列化されたコントロールが出来上がり、後々簡単にサイズや色を変更できるようになり悩みが解消しました。
最初に質問させていただいた「Image1上に」という意味はShapeをImage1上に貼り付けた状態(Image1を移動すると同時に移動し、VisibleもImage1と同時にfalseになる)にしたいと考えておりました。名前から見てParentあたりかと思ってちょっといじりましたが分かりませんでした。宜しくご教示ください。
あるControl.Parent = Image1
としたかったのでしょうか?
Parentは TWinControlから派生したオブジェクトで無いとだめです。
TImageは、TWinControlからは派生していないので、他のコントロールの
親になることができません。
Delphiのヘルプの TImage の 継承をクリックしてみてください。
そのように、ほとんどのクラスは、何かから派生しており
機能や値、外観などを受け継ぎつつ、下にいくにつれて
値が書き換えられたり、機能が変わったり、追加されたり
していきます。
(たぶん、VBでも一緒ですよね?)
TWinControlから派生したTImageもどきを作るか
Visibleの同期を取るコードを別に書く必要があるでしょう。
あるいは、TImageや他のコントロールの親をフォームにするので
はなく、TPanelあたりを ひとつ、フォームに張っておいて、
それの AlignをalClientにして、
その上に TImageや、他のコントロールを載せるようにすれば
TPanel.Visible:=False で一緒に消えると思います。
こんなところで、いかがでしょうか。
forever様、ご教示をありがとうございます。やりたいことはforeever様の書かれた通りです。
VBの場合は例えばPicture1(など)の上にデザイン時に何かコントロールを貼り付けてやれば親子関係が自動的に発生し「派生、継承」といったキーワードを意識しないで使っておりました。
早速パネルを貼ってその上にShapeを置き、単にFormの上に置いたものとプロパティを比較してみましたが全部同じでした。具体的にはどのようにコード上で親子関係を築けるのか教えてください。(根本的なことは時間を掛けないと理解できそうにないため申し訳ありませんが)
(えーと様のコードでテストした結果成功だったのですがこれを開発中もものに載せ変え(コピペ)したら 未定義の識別子'self' と出てしまいました。uses節も見ましたがテストのコードと同じです。どうしてこのようなエラーとなるのか教えてください)
TPanelを各コントロールの親ウィンドウにするには、
TPanelに Panel1 と名前がついている場合なら
えーとさんの書かれたコードの
> with ShapeList[i] do
> begin
省略
> Parent := self;
> end;
のParentの部分を
Parent := Panel1;
とすればよいです。
Image1の Parent は、デザイナで Panel1の上に貼り付けた時点で
Panel1 になっているはずです。
ちなみに Self というのは、Form1のメソッド(関数や手続き)を記述する
ブロック内であるならば、Form1 自身のことを指します。
self が未定義と出るのは、その関数や手続きが、クラスに属していない
のでは無いかと思います。
>procedure TForm1.Button1Click(Sender: TObject);
これがソースコード上部の interface 部の
type 以下
TForm1 のメンバーに加わっていないと思います。
一度、implementation 以下の Button1Clickハンドラを
CTRL + X でカットし、
デザイナで Button1 を選択し、
オブジェクトインスペクタ(通常左側にあるウィンドウ)
「イベント」から 一度、OnClickイベントを作った上で
CTRL + P で Delphiが生成した Button1Clickハンドラと
入れ替えてあげれば OK。
(デザイナを使う場合における話です)
forever様、今回計画していたことは全て解決しました。
①Parentを(image1を止めて)panelに設定したら上手くいきました。
②selfを具体的に「Form1」にしたら通りました。
以上、本質的に理解できるレベルではありませんが目的は達成できました。数行の問題に何日も立ち止まりましたがおかげさまで連休中に次のステップに進めて大感謝です。本当にありがとうございました。
(クリエートしたコントロールでイベントを発生させることはできるのでしょうか? 別スレを出すべきか迷いましたが・・・)
>(クリエートしたコントロールでイベントを発生させることはできるのでしょうか? 別スレを出すべきか迷いましたが・・・)
「発生させる」の意味がどこか難しいところですが、しばらくDelphiを経験した方にはタイマーとかで能動的に発生させることは可能です。
....そのように書けるのですが、通常イベントは、(能動的に)発生させるものではなく、(受動的に)起こったイベントを処理するものです。それがイベントハンドラです。マウスが通過した/クリックされた/キーが押された....。それらのイベントを受けることができるのか、そのハンドラへ処理を移す(ハンドラの起動とも言えます)ことができるのかと言う意味であれば、それも当然できます。(それができないと何もできないでしょうね...)
具体的な方法は、他の方に譲ります。
>ハンドラの起動とも言えます
「あえて言えば....」を頭につけて読んでください。恐らく「発生させる」の意味はこれかと想像しますが、99パーセント、このような表現はしません。イベントを受ける、イベントを処理する.....普通はそのように言います。
ことばって難しいですね。「99パーセント」は、「ほとんどの場合」としておきます。感覚的な表現の方が、この場合適切のようです。与える/受ける、させる/(処理)する....これらは、主体次第で表現が変わってきますよね。今回の場合、主体は「クリエートしたコントロール」ですから、やはり、「受ける」になるでしょう。
タバスコ様、用語の定義につきまして厳密な表現ができなくて申し訳ありませんでした。
要は教えていただいた技でチェックボックスなどができてしまうのでこの手で作ってみたところ簡単にできてしまいました。でも「貼り付けた」コントロールではないのでコードを書く所や書き方がわからず質問させていただいたものです。 例えば CheckBox[i].OnMouseMove みたいなことが書けたらと思い立ったものです。最初はCreateなどでできるとは思っていませんでしたので30個程度のCheckBoxを貼り付けて一つずつコードを書こうと思っていたのですが、これを教えていただいた配列方式でVBみたいにインデックスを使ってコードがまとめられれば、と欲が出てしまい質問させていただきました。
以前、この手の処理を書いたことがあります。Create する CheckBox のそれぞれの Tag プロパティでナンバリングする方法でしたけど、
case (Sender As TCheckBox).Tag of ....などで個別判定していました。
また、同じ動作なら
procedure TForm1.LabelClick(Sender: TObject);
begin
if (Sender as TLabel).Font.Color = clBlack then (Sender as TLabel).Font.Color := clYellow
else if (Sender as TLabel).Font.Color = clYellow then (Sender as TLabel).Font.Color := clBlack;
end;
.....すべてのラベルに、このハンドラを設定したり...。
これで解りますでしょうか?
ちょっと違いましたね。
case Index of
0:CheckBox[i].checkd := True;
....こんな感じでしょうか。あと、複数 Create する場合は、CheckBox から派生させたクラスの検討などもしてみるといいかも知れません。その方法をマスターするといろいろ応用がききますので、便利です。
var
ShapeList: array[1..20] of TShape;
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
begin
for i := 1 to 20 do begin
ShapeList[i] := TShape.Create(self);
with ShapeList[i] do
begin
height:=15;
width:=15;
shape:=stcircle;
brush.Color:=rgb(random(255),random(255),random(255));
Left := 180;
Top := i * 20;
Parent := Panel1;
onClick:=LabelClick;
end;
end;
end;
にすればいいと思いますよ。
難しく考えることなかったですね。
if CheckBox[i].checkd = True then .....
サンデープログラマー様、タバスコ様
間違えました。
onClick:=LabelClick;
の部分をそのコンポーネントに設定させるイベントと、
処理する手続きに書き換えて下さい。
関係ないコード同士を繋げてしまい深くお詫び申し上げる所存であります。
>関係ないコード同士を.....
一番肝心なとこが.....ですね。
こちらも、質問の意味を取り違えてたようですみません。まあ、参考にでも....。
一般的に、下のようになります。適当に打ったのでミスってるかもしれませんが、やりかたとして。こんな感じです。
for i = 0 to 20 do
begin
Comopos[i] := TCompo.Create(self);
Comopos[i].Palent := self;
Compos[i].OnClick := ClickCode;
Compos[i].Top := 20 * i;
(以下略)
忘れ物
(経験者はすぐピンとくるので忘れててもどうと言うことないんですが、やはり
はじめての方には提示しておかないと)
type
TForm1 = class(TForm)
......
procedure ClickCode(Sender: TObject);
忘れ物2
procedure TForm1.ClickCode(Sender: TObject);
begen
// ここで意味を取り違えたコードを活かしてください。
end;
スレと関係ないですが タバスコさんのコードの応用です。
つい2006/5/1に製作していた、某TV局用のプログラムの一部です。
設計時にシェイプのタグに好きな色の数値を入れて下さい。
procedure TForm1.Shape1Click(Sender: TObject);
begin
TShape(Sender).Brush.Color:=TColor(Integer(TShape(Sender).Brush.Color xor clwhite)+TComponent(Sender).Tag);
end;
これで白と好きな色が変更になります。
タバスコ様、プロプログラマー2年目様、ご回答をありがとうございます。
アドバイスをいただいて以来「できました」と回答させていただこうかと数時間、見よう見真似で格闘してきましたがだめでした。Createで沢山のコントロールが生成できるスマートな方法を知ってしまいましたので何とか取り入れてみたいと思いました。
やりたいことは、例えばコントロールを1つづつ貼り付けたら
procedure TForm1.Shape1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
iSTUDENT:= 1;
Edit1.Text:= IntToStr(iSTUDENT);
end;
procedure TForm1.Shape1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
iSTUDENT:= 1;
Edit1.Text:= IntToStr(iSTUDENT);
end;
・
・
みたいなものをコントロール配列にして処理できないかと思ったものです。
var
ShapeList: array[1..20] of TShape;
procesure TForm1.MouseMoveCode(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
beep;
Edit1.text:=IntToStr(ShapeList.Tag);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
begin
for i := 1 to 20 do begin
ShapeList[i] := TShape.Create(self);
with ShapeList[i] do
begin
height:=15;
width:=15;
shape:=stcircle;
brush.Color:=rgb(random(255),random(255),random(255));
Left := 180;
Top := i * 20;
Parent := Panel1;
Tag:=i;
onMouseMove:=LabelMouseMove;
end;
end;
end;
原理もわからない低レベルなものにお付き合いいただくのは大変恐縮ですが今回だけでも教えてください。
>サンデープログラマー 2006/05/06(土) 10:21:05
>②selfを具体的に「Form1」にしたら通りました。
Self は そのままで、OKです。
Form1のメソッドのつもりなのに、明示的にForm1と書かないと、
動かないようでしたら、そのように想定して書いたわけではない限り、
どこかおかしくなっています。
>procedure TForm1.Shape1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
OnMouseMoveハンドラは、アサインされた数だけ書く必要はありません。
Sender を使って、どのオブジェクトからこのハンドラが呼び出されたのかは、特定できます。
procedure TForm1.Shape1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
shape: TShape;
begin
shape:=Sender as TShape; //Tshapeじゃなければここで例外が発生します
shape.Left:= 0;
end;
>配列
配列は管理が大変なので、おすすめしません。
TListがあるので、TListを使ったほうが良いと思います。
ですが、TListを使うまでもありません。(TListを使うのは効率を追求する場合や
TList自体を継承して機能を拡張する場合などにおすすめします)
TShapeはTCompoment,TControlから派生しているので、VCLの機構をつかって
自動的に親に格納されています。
TComponentは、OwnerのComponentsプロパティに自動的に格納されます。
Ownerとは、
TShape.Create(Self);
とした場合、
Selfが TShape の Ownerです。
Selfが Form1 なら、 Onwer は Form1 となります。
TShapeは、Form1.Components[]に入っています。
(TShape.Create(nil) としない限り、TShapeをComponentsプロパティで参照できます。)
他に、TControlは、TWinControlのControlsプロパティに自動的に格納されます。
TShape.Parent:= Panel1;
ならば
TShape は、Panel1.Controls[]に入っています。
実際にComponentsを使ってアクセスする場合、
Form1のハンドラなら、Form1.識別子は省略できるので以下のようになります。
ただし、Componentsには、TShape以外のものも含まれているので、その点注意が
必要です。(オーバーヘッドとか気にしないのなら、この方法で通常問題ありません)
for i:=0 to Components.Count-1 do
begin
if Components[i] is TShape then
begin
shape:= TShape(Components[i]);
shape.Left:= 0;
end;
end;
Controls[]も同様です。
(物によりますが、ControlsのほうがComponentsより数が少ないはずなので
微妙に効率はよいでしょう。)
詳細なComponentsとControlsの違いはヘルプを参照してください
(書くと長くなるし、私の言いたい事もヘルプと一緒です)
イベントハンドラに結びついているなら、とくに配列や Components[] を
使わなくても Sender をキャストするだけのような気がしますが。配列が
必要な理由って何ですか?
この際答える様、えーと様、下記コードを追加して何とかCreateで生成されたShapeにマウスを移動するとその番号が表示されるという機能はテストプログラムでは見事に成功しました。(Type節にProcedure LabelMouseMoveを追加して)詳細な解説、具体的なコードをありがとうございます。
procedure TForm1.labelMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
shape: TShape;
begin
shape:=Sender as TShape; //Tshapeじゃなければここで例外が発生します
beep;
edit1.Text:=inttostr(shape.tag);
//shape.Left:= 0;
end;
ところがこれをテストプログラムから開発中のものに載せ変えると'未定義の識別子:LabelMouseMove' と出てきて通りません。Uses節も問題なさそうですしType節にも登録したしスペルの問題もなさそうです。どのような点をチェックすべきかを色々いじっていますが解決できません。チェックすべき箇所などアドバイスをいただければ幸甚です。
フォームのクラスが TForm1 じゃないとか
「忘れ物2」相当を書き込んだ訳ですから、「忘れ物」のほうをやってないか違っているかじゃないんでしょうか?
>>フォームのクラスが TForm1 じゃないとか
Type節で TForm1 = class(TForm) と自動的に宣言していますがこれだけではだめなのでしょうか?
しかし前のスレで書きましたがコントロール作成時に ShapeList[i] := TShape.Create(self); では通らず、selfをForm1にしたら動きました。 このあたりで何か問題を生じているような予感がしまがどこかで致命的な誤りを犯しているのでしょうか。宜しくご教示ください。
LabelMouseMoveの宣言はありますか?
// 下記の状態で未定義エラーはないはずです。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls;
type
TForm1 = class(TForm)
procedure labelMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.labelMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
// 略
end;
end.
HOta様 ありがとうございます。
Type節に下記の宣言をしています。(これはタバスコ様の忘れ物2のことかと思います)
Type
procedure labelMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
現在の症状はテストソフトでは動くが移植すると動かない、というものです。
ShapeList[i] := TShape.Create(self); のselfでは通らない、即ちフォームのクラスがTForm1ではなくなってしまっている(?)可能性があるのではないかと疑っていますがこれをどのように確認するのかわからないでHELPなどを読んでいるところです。何か手がかりがあったらお願いします。
タバスコ様、行き違いになってしまい申し訳ありませんでした。
完全なサンプルコードをいただき恐縮しています。
テストプログラムでは14:29の時点で成功しましたが、これを本番ソフトに移植した時点で次の問題が残ってしまいました
1.ShapeList[i] := TShape.Create(self); のselfでは通らない==>>selfをForm1にして一応問題回避
2.LabelMouseMoveが未定義である、とのエラーが出てしまう。というものです。子疎を理解する必要性を痛感します。
>Type
>
>procedure labelMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
この通りになっているとダメです。Tform1 のクラス内に記載しないと....。
>1.ShapeList[i] := TShape.Create(self); のselfでは通らない==>>selfをForm1にして一応問題回避
この配列がグローバルになってそうな雰囲気です。TForm1 内のどこかにないと....
。
逆かも知れません。
グローバル変数はグローバルメソッドで使う、
クラス内部の変数はクラス・メソッドで使う。
これらの一致が「互い違い」になっていそうです。
>グローバル変数はグローバルメソッドで使う、
は、どちらかと言うと間違いです。失礼しました。
self でダメで、TForm1 にするとOKと言うことなので、扱おうとしている変数は TForm1 に所属していると思われます。
self でダメなのは、Create のコードを記載したプロシジャーが、TForm1 に所属していないから・・・のような感じです。
タバスコ様、いろいろと手懸りをご提示いただきありがとうございました。
>>self でダメなのは、Create のコードを記載したプロシジャーが、TForm1
>>に所属していないから・・・のような感じです。
アドバイスを拝見していてこのあたりが何か怪しい感じがしてきました。
Createを記述したのは procedure sMapDraw; というところですがこれがTForm1に所属しているかいないかをどのように確認するのか、あるいは所属させるにはどうしたら良いか教えてください。
(長い連休はDelphi三昧でしたがこの間沢山のアドバイスをいただき勉強でし本当にありがとうございました)
procedure sMapDraw;
この記述ではグローバル・メソッドになります。
所属させるには、
procedure TForm1.sMapDraw;
.....なんですが、変数の場合はともかく、TForm1 クラス定義の中(メソッド類の列挙部分)にだけ
procedure sMapDraw;
があって実装部が、
procedure sMapDraw;
.....となっていた場合、「TForm1.sMapDrawが見つかりません」の意のエラーが出てるはずなんですが....。
要は、名簿(クラス定義)と各人がちゃんと揃えばいいのです。
各人それぞれ(つまり実装部)が、廊下とか校庭とか(グローバル)になっていると、教室で点呼しても返事がない(xxxさんが居ません(見つからないエラー)).....。それに似た状況になると言うことです。
タバスコ様、できました。(感激)感謝あるのみです。
>>procedure TForm1.sMapDraw;
のようにTFORM1を加えて全て解決しました。
変数のスコープもTForm1の及ぼす及ぼす範囲など誤解していましたがこれでだいたい輪郭が分かった気がします。通らなかった'self'の問題もTForm1を加えただけで解決しました。
初心者に対してご親切にアドバイスをいただいたえーと様、うん様、forever様、2年目様、そしてタバスコ様に感謝いたします。勉強します。
修正と補足。
インターフェース部は、通常「宣言」と言います。
型を宣言する、クラスを宣言する、変数/定数を宣言する。。
従って、
>要は、名簿(クラス定義)と各人がちゃんと揃えばいいのです。
は
「要は、名簿(クラス宣言)と各人(実際の定義)がちゃんと揃えばいいのです。」
....とするのが正しい表現ですね。「定義(する)」は内容を決めることですから、「実装」とほぼ同義ですね。
グローバルメソッドの場合、今回のように(無理に)TForm1 と付けて使うことも一応可能ですが、通常は、各クラスメンバに直接アクセスしない方向で作ります。ほとんどの場合、値を返す、function になるでしょう。
グローバルメソッドも、できるだけインターフェース部に宣言を入れるようにして組むと、後方参照などで悩むこともなくなるのでいいと思います。
# 仕組みがわかっていれば、実装部に記述するだけでも使えたりしますけど.....。
タバスコ様、ありがとうございます。見よう見まねでDelphiを始めてからインターフェース、メンバやSenderなどの意味もわからず専ら真似で作ってきましたがやはりある程度の知識までは至らなくてもイメージを持つことが必須であると痛感しました。現在HELPを見ながらやっていますがそのHELPも見慣れないタームが羅列してあります。少しずつわかってきて芋づる式に疑問が氷解するのを夢見ています。(本が少ないですね?)
これに関連してまた問題が次々出てきましたので別スレで質問させていただきます。お礼まで。
ツイート | ![]() |