2

I have a Chat program written in Delphi7. As a special "effect" we display a few bullet holes when a particular gunshot sound file is played. We did this by drawing a new form in the shape of the bmp image file of the bullet hole, with a timed delay of a few secs for it to be visible and then fade away.

All of this works, however, while the bullet hole images are onscreen, the program is effectively locked up... returning focus back to the user when the last of the images has faded away.

My programmer isn't real well versed in graphics and believes this is just the price you have to pay to get this effect, but I'm hoping that's not quite true... any suggestions of a better way to randomly display bullet hole images across the screen?

2
  • 1
    You may want to re-think your subject title as "bullet holes" is a little cryptic, something like "How to stop a Delphi from locking up when a second form is shown" may be more appropriate, and would get you better reply. Commented May 2, 2009 at 16:05
  • 8
    I bet "bullet holes" gets more views. ;-) Commented May 2, 2009 at 16:43

7 Answers 7

3

It's hard to say without seeing code and knowing exactly how your opening/loading the second form, but it sounds like you are opening them with ShowModal which will lock the parent form until the modal form returns a result.

If that is the case then you can simple open it with the Show method and then set the focus back to the main form like so.

procedure TForm1.Button1Click(Sender: TObject); var obj:TForm2; begin obj := TForm2.Create(nil); try obj.FormStyle := fsStayOnTop; obj.show; Self.SetFocus; //set focus back to the form1 except FreeAndNil(obj); end; end; 

The above also amuses that you are creating the form dynamically at runtime and that the second form is responsible for freeing itself.

Sign up to request clarification or add additional context in comments.

6 Comments

I have not tried this, but won't Form2 end up hidden by Form1 with this approach?
not if you set it to be always on top. (Not sure if D7 can do that, but later versions can.)
+1 then, but someone (ReOsless or someone with high rep) should edit the code with that property setting.
The VCL's PopupMode and PopupParent properties can be used to fix a Form right in front of the MainForm. The BulletHole Form wouldn't be transparent for the Mouse though.
I was thinking that as it was a chat client it would not take up the whole screen, and the bullet holes would be else were one the screen (ie not over the chat window), but your right about stayOnTop so i have added that to the code.
|
2

Overlaying grahics on a Form isn't exactly trivial but I think this is what the TCustomTransparentPanel (name from memory) is for.

But anyway, it shouldn't slow down or freeze the program.

Comments

1

Opening a second form really is not the way to do this. Perhaps a picture control with a partially transparent image sized to just be bigger than your bullet hole that you could place anywhere on the form. If you can't do a transparent control, you could take a snapshot of that section of the form and place the bullet hole on top.

  1. When the bullet should appear, you make the picture visible and bring to front
  2. Start a timer for two seconds and tell the picture control to refresh
  3. When the timer goes off, hide the bullet picture

If you want to animate the fade out, just replace the picture with different ones at different times using the timer going off at different times

  • At 0 seconds make picture visible
  • At 1.5 seconds replace image with a faded version of the picture
  • At 2 seconds hide the picture

Using this, there is no need for another form, and the app remains responsive.

Comments

0

I believe what is needed would be to use the SetWinRegion method to draw an outline around the "bullet hole". That way they can be clicked thru, but the holes themselves would be floating on top (provided the previous answer of fsStayOnTop is used).

Even a flash image would require a form of some type to act as the container for the image. It all comes down to creating a window and blasting the image to that window. It doesn't have to "lock" the screen, if you set it to always on top and just "SHOW" it (not ShowModal) it should appear and float. I would then use a timer to control when it is destroyed. The advantage of the SetWinRegion, is you can create a window that has an irregular shape, and the only place the user would not be able to click would be the portion of your window which contained the actual form (inside the region).

1 Comment

Thanks for the responses everyone... I was hoping for a possibly different approach? like a separate thread or a different way to show images than to draw a form, Like displaying a flash image of the bullethole or something? suggestions/ideas would be most appreciated... there are other effects I wold like to implement but can't if it's going to lock up the screen.. again thanks to everyone.. this is my first time using this and it's amazing you all take the time and effort to help out like this.. Mark Gundy www.magchat.com
0

If the animations of bullet holes are written as a loop that executes until done then that might be the issue. If so, then try inserting Application.ProcessMessages somewhere in the loop to allow other things to happen while the animation is occurring.

Comments

0

Here's a solution that works. You'd want to wrap the firing process into a timer or some other event driven process.

Form Definition:

 TForm1 = class(TForm) btnTurnOn: TButton; ListBox1: TListBox; btnTurnOff: TButton; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure btnTurnOnClick(Sender: TObject); procedure btnTurnOffClick(Sender: TObject); private { Private declarations } public { Public declarations } F:TForm; Bullet:TBitmap; x,y:integer; procedure SubFormPaint(Sender: TObject); end; 

Code:

procedure TForm1.btnTurnOnClick(Sender: TObject); begin if F=nil then begin F:=TForm.Create(self); F.Parent:=self; F.FormStyle:=fsStayOnTop; F.BorderStyle:=bsNone; F.Left:=x; F.Top:=y; F.Width:=Bullet.Width; F.Height:=Bullet.Height; F.OnPaint:=Self.SubFormPaint; F.Show; end else begin inc(x,5); inc(y,5); F.Left:=x; F.Top:=y; F.Invalidate; end; end; procedure TForm1.btnTurnOffClick(Sender: TObject); begin F.Free; F:=nil; end; procedure TForm1.FormCreate(Sender: TObject); begin F:=nil; Bullet:=TBitmap.Create; Bullet.LoadFromFile('C:\source\glyfx\glyfx\Emoticon\BMP\16x16\wink_16.bmp'); x:=1; y:=1; end; procedure TForm1.FormDestroy(Sender: TObject); begin Bullet.Free; end; procedure TForm1.SubFormPaint(Sender: TObject); begin if F<>nil then F.Canvas.Draw(1,1,Bullet); end; 

Comments

0

See http://msdn.microsoft.com/en-us/library/dd183353(VS.85).aspx, it has a good example of the API calls for alpha blending bitmaps together. For each frame of an animation, you'd probably need to grab a copy of the image that represents your main screen, and then blend in your glyphs with this at the appropriate transparency level.

As previously posted, you could do Application.ProcessMessages repeatedly, although I prefer doing it backwards and using PostMessage to trigger the next frame of the animation.

You'd use ProcessMessages in a loop, but that call could cause other things to occur while you're in that loop - window moving, minimizing, closing, or triggering your animation event again - before the first animation is finished. This is all fine as long as you're mindful that you're not always necessarily operating within the scope of just your loop.

For that reason I prefer having each frame of animation do a PostMessage to trigger the next frame. It just helps me keep things in perspective.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.