big_one01
asked on
Image navegation mini window
Im trying to make an application in Delphi 7 that can display large images on screen.
I want to know how to use a mini window to navigate across the large image.
Something like the minimap in games like StarCraft where the huge map is on the main screen (can't see it all),
but in the right corner is a mini stretched version of the huge image, and over this a selection tool (a rectangle), then when you move this rectangle over the mini image, the main screen go to this image portion on the huge image at the backgroud.
Sorry for my poor english skills.
Thanks a lot.
I want to know how to use a mini window to navigate across the large image.
Something like the minimap in games like StarCraft where the huge map is on the main screen (can't see it all),
but in the right corner is a mini stretched version of the huge image, and over this a selection tool (a rectangle), then when you move this rectangle over the mini image, the main screen go to this image portion on the huge image at the backgroud.
Sorry for my poor english skills.
Thanks a lot.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Thaks.
I solve the issue using a ScrollBox with the huge TImage inside and
a frame over the minimap, then when I pan this frame, I change the scrollbars position to move across the huge image.
But now I need the inverse process, I need an event of the scrollbox where I can check if the scroll bar position has been changed by the user, in order to update the frame position over the minimap, but I cant find something like OnScroll, do you know about maybe some Windows Message that can be checked to know if the scroll bars has been "scrolled".
I solve the issue using a ScrollBox with the huge TImage inside and
a frame over the minimap, then when I pan this frame, I change the scrollbars position to move across the huge image.
But now I need the inverse process, I need an event of the scrollbox where I can check if the scroll bar position has been changed by the user, in order to update the frame position over the minimap, but I cant find something like OnScroll, do you know about maybe some Windows Message that can be checked to know if the scroll bars has been "scrolled".
You will need to create a descendant class of the TScrollBox and override the message handlers like this:
TNotifyScrollBox = class(TScrollBox)
protected
procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL;
procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
end;
Make your TScrollBox a TNotifyScrollBox instead, then you can respond to the messages by changing the location on your mini map. You can change your scroll box either by registering the control, or you can create it dynamically when your form is created. You will want to use the message responses to activate new events.
be very careful. This could set up a loop that will hang your front end. If A is changed and tells B to update then B (having changed) tells A to Update, etc, etc...You will likely need to set up 2 variables to indicate if one control or the other is currently updating the other, then in each, check and only do the noptification of change if it is not currently in the middle of another update.
I do not have time to do the whole componet for you, and the points do not justify that work anyway.
Good Luck.
TNotifyScrollBox = class(TScrollBox)
protected
procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL;
procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
end;
Make your TScrollBox a TNotifyScrollBox instead, then you can respond to the messages by changing the location on your mini map. You can change your scroll box either by registering the control, or you can create it dynamically when your form is created. You will want to use the message responses to activate new events.
be very careful. This could set up a loop that will hang your front end. If A is changed and tells B to update then B (having changed) tells A to Update, etc, etc...You will likely need to set up 2 variables to indicate if one control or the other is currently updating the other, then in each, check and only do the noptification of change if it is not currently in the middle of another update.
I do not have time to do the whole componet for you, and the points do not justify that work anyway.
Good Luck.
ASKER
Thanks.
I already solve the problem using a third party TScrollBox component.
This have the OnHScrooll and the ONVscroll events, justs as I needed.
I already solve the problem using a third party TScrollBox component.
This have the OnHScrooll and the ONVscroll events, justs as I needed.
I gave you code that did the "mini map" portion of it for you, I even described what would be needed for the "inverse process" if you wanted to do it yourself. Now you want to claim you solved it yourself because you found a third party control that does just the last part of it?!? I think I deserve the points on this one.
ASKER
I try the code that you give to me, but this code dont help me with the problem.
In the first part as you cant see I solve the issue using a ScrollBox with the huge TImage inside, because in your words " All I did was show you how the mini map portion would work. You should see a small box on the mini map portion that you are able to move with the mouse. Did that much work for you?" As any that try the code can see, you just draw a rectangle with a TPaintBox, thats not a solution!
And in the second part, you say that I have to create my own component because in you words "I do not have time to do the whole componet for you, and the points do not justify that work anyway." That is not a solution either
Thanks anyway.
In the first part as you cant see I solve the issue using a ScrollBox with the huge TImage inside, because in your words " All I did was show you how the mini map portion would work. You should see a small box on the mini map portion that you are able to move with the mouse. Did that much work for you?" As any that try the code can see, you just draw a rectangle with a TPaintBox, thats not a solution!
And in the second part, you say that I have to create my own component because in you words "I do not have time to do the whole componet for you, and the points do not justify that work anyway." That is not a solution either
Thanks anyway.
There are two parts to your problem, the mini map and the scrolling of the main image. I gave you working code for the mini map and told you everything that would be needed in order to tie it to the scrolling part of it.
The paint box drawing a square is the solution to the mini map portion. You are able to move the box with your mouse. Moving it with your mouse generates changes to an X and Y coordinate that can be used to change the scroll box position. The code I gave you works, evidently you did not try it out.
I was trying to help you solve a problem, not necessarily do all the work for you. I did part of the work and told you how the rest could be done by you. That is all that should be required to be considered an answer.
The paint box drawing a square is the solution to the mini map portion. You are able to move the box with your mouse. Moving it with your mouse generates changes to an X and Y coordinate that can be used to change the scroll box position. The code I gave you works, evidently you did not try it out.
I was trying to help you solve a problem, not necessarily do all the work for you. I did part of the work and told you how the rest could be done by you. That is all that should be required to be considered an answer.
OK - PAQ'd the solutions to developmentguru, then re-added the 125 points to big_one01's account.
Vee_Mod
Experts Exchange Moderator
Vee_Mod
Experts Exchange Moderator
well, thank Vee_Mod to make it possible
At the Meantime i tried myself a solution
instead of using a scrollbox i used a panel as container (-> so no scrollbars)
it can be navigated through the MiniMap or by dragging with the mouse
the dfm
At the Meantime i tried myself a solution
instead of using a scrollbox i used a panel as container (-> so no scrollbars)
it can be navigated through the MiniMap or by dragging with the mouse
the dfm
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 447
ClientWidth = 535
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object PanMiniMap: TPanel
Left = 0
Top = 0
Width = 101
Height = 447
Align = alLeft
TabOrder = 0
object MiniMap: TImage
Left = 0
Top = 0
Width = 100
Height = 100
Proportional = True
OnMouseDown = MiniMapMouseDown
OnMouseMove = MiniMapMouseMove
OnMouseUp = MiniMapMouseUp
end
object MiniMapNavigator: TShape
Left = 0
Top = 0
Width = 41
Height = 41
Margins.Left = 0
Margins.Top = 0
Margins.Right = 0
Margins.Bottom = 0
Brush.Style = bsClear
Pen.Mode = pmNotXor
OnMouseDown = MiniMapNavigatorMouseDown
OnMouseMove = MiniMapNavigatorMouseMove
OnMouseUp = MiniMapNavigatorMouseUp
end
object BtnLoad: TButton
Left = 15
Top = 106
Width = 75
Height = 25
Caption = '&Load'
TabOrder = 0
OnClick = BtnLoadClick
end
end
object PanBigPic: TPanel
Left = 101
Top = 0
Width = 434
Height = 447
Align = alClient
TabOrder = 1
OnResize = PanBigPicResize
ExplicitLeft = 111
ExplicitTop = 161
ExplicitWidth = 535
ExplicitHeight = 342
object BigPic: TImage
Left = 0
Top = 0
Width = 105
Height = 105
AutoSize = True
OnMouseDown = BigPicMouseDown
OnMouseMove = BigPicMouseMove
OnMouseUp = BigPicMouseUp
end
end
object OpenPictureDialog1: TOpenPictureDialog
Left = 192
Top = 8
end
end
the unit
unit BigPicWithMiniMap_u;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, ExtDlgs;
type
TForm1 = class(TForm)
PanMiniMap: TPanel;
PanBigPic: TPanel;
OpenPictureDialog1: TOpenPictureDialog;
BigPic: TImage;
MiniMap: TImage;
BtnLoad: TButton;
MiniMapNavigator: TShape;
procedure BtnLoadClick(Sender: TObject);
procedure PanBigPicResize(Sender: TObject);
procedure BigPicMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure BigPicMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure BigPicMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormCreate(Sender: TObject);
procedure MiniMapMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure MiniMapMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure MiniMapMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure MiniMapNavigatorMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure MiniMapNavigatorMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure MiniMapNavigatorMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
{ Private-Deklarationen }
PicLoaded : Boolean; //Act only if there is one Picture loaded
BigPicDragX : Integer;
BigPicDragY : Integer;
BigPicInMove : Boolean;
MiniMapNavigatorInMove : Boolean;
MiniMapDisplayWidth : Integer;
MiniMapDisplayHeight : Integer;
procedure CalcMiniMapBounds;
procedure CalcMiniMapNavigatorBounds;
procedure CalcMiniMapNavigatorPosition(ATop,ALeft : Integer);
procedure CalcBigPicBounds(ATop,ALeft : Integer);
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses jpeg; //add JPeg-Support
//Init
procedure TForm1.FormCreate(Sender: TObject);
begin
//Avoid Dragging Flicker
PanBigPic.DoubleBuffered := true;
PanMiniMap.DoubleBuffered := true;
//No Picture loaded yet
PicLoaded := False;
end;
//Handle BigPic Mouse-Events
procedure TForm1.BigPicMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if PicLoaded then
begin
BigPicInMove := True;
BigPicDragX := X;
BigPicDragY := Y;
end;
end;
procedure TForm1.BigPicMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var l,t : Integer;
begin
if BigPicInMove then
begin
if BigPicDragY - y <> 0 then
t := BigPic.Top - (BigPicDragY - y)
else
t := BigPic.Top;
if BigPicDragX - x <> 0 then
l := BigPic.Left - (BigPicDragX - x)
else
l := BigPic.Left;
CalcBigPicBounds(t,l);
end;
end;
procedure TForm1.BigPicMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
BigPicInMove := False;
end;
//Handle MiniMap Mouse-Events
procedure TForm1.MiniMapMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if PicLoaded then
begin
MiniMapNavigatorInMove := True;
CalcMiniMapNavigatorPosition(y,x);
end;
end;
procedure TForm1.MiniMapMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if MiniMapNavigatorInMove then
CalcMiniMapNavigatorPosition(y,x);
end;
procedure TForm1.MiniMapMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
MiniMapNavigatorInMove := False;
end;
//Handle MiniMapNavigator Mouse-Events
procedure TForm1.MiniMapNavigatorMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var P : TPoint;
begin
if PicLoaded then
begin
MiniMapNavigatorInMove := True;
P := MiniMap.ScreenToClient(MiniMapNavigator.ClientToScreen(Point(x,y)));
CalcMiniMapNavigatorPosition(p.y,P.x);
end;
end;
procedure TForm1.MiniMapNavigatorMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
var P : TPoint;
begin
if MiniMapNavigatorInMove then
begin
P := MiniMap.ScreenToClient(MiniMapNavigator.ClientToScreen(Point(x,y)));
CalcMiniMapNavigatorPosition(p.y,P.x);
end;
end;
procedure TForm1.MiniMapNavigatorMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
MiniMapNavigatorInMove := False;
end;
//Handle FormResize
procedure TForm1.PanBigPicResize(Sender: TObject);
begin
if PicLoaded then
CalcBigPicBounds(BigPic.Top,BigPic.Left);
end;
procedure TForm1.BtnLoadClick(Sender: TObject);
begin
if OpenPictureDialog1.execute then
begin
MiniMap.Picture.LoadFromFile(OpenPictureDialog1.filename);
BigPic.Picture.LoadFromFile(OpenPictureDialog1.filename);
PicLoaded := True;
BigPic.Top := 0;
BigPic.Left := 0;
CalcMiniMapBounds;
CalcBigPicBounds(BigPic.Top,BigPic.Left);
end;
end;
//Depending Calculations
procedure TForm1.CalcMiniMapBounds;
begin
//Get the real Height and Width, because the MinMap is proportianal stretched
//Init, As we have a Square as MiniMap
MiniMapDisplayHeight := MiniMap.Height;
MiniMapDisplayWidth := MiniMap.Width;
//But is the Loded not a Square, correct it dependent
if MiniMap.Picture.Height > MiniMap.Picture.Width then
MiniMapDisplayWidth := round(MiniMap.Width * (MiniMap.Picture.Width/MiniMap.Picture.Height))
else
if MiniMap.Picture.Height < MiniMap.Picture.Width then
MiniMapDisplayHeight := round(MiniMap.Height * (MiniMap.Picture.Height/MiniMap.Picture.Width));
end;
procedure TForm1.CalcBigPicBounds(ATop,ALeft : Integer);
begin
//keep image in bounds
//Calc Left
If (BigPic.Width < PanBigPic.ClientWidth) or
(ALeft > 0) then
BigPic.Left := 0
else
if abs(ALeft) + PanBigPic.ClientWidth > BigPic.Width then
BigPic.Left := PanBigPic.ClientWidth - BigPic.Width
else
BigPic.Left := ALeft;
//Calc Top
If (BigPic.Height < PanBigPic.ClientHeight) or
(ATop > 0) then
BigPic.Top := 0
else
if abs(ATop) + PanBigPic.ClientHeight > BigPic.Height then
BigPic.Top := PanBigPic.ClientHeight - BigPic.Height
else
BigPic.Top := ATop;
//Adjust MiniMapNavigator
CalcMiniMapNavigatorBounds;
end;
procedure TForm1.CalcMiniMapNavigatorBounds;
Var
h,w : Integer;
begin
MiniMapNavigator.Left := trunc(abs(BigPic.left)*(MiniMapDisplayWidth/BigPic.Width));
MiniMapNavigator.Top := trunc(abs(BigPic.Top)*(MiniMapDisplayHeight/BigPic.Height));
MiniMapNavigator.Width := trunc(PanBigPic.ClientWidth*(MiniMapDisplayWidth/BigPic.Width)) + 1;
MiniMapNavigator.height := trunc(PanBigPic.ClientHeight*(MiniMapDisplayHeight/BigPic.Height)) + 1;
//Keep the MiniMapNavigator UpToDate
MiniMapNavigator.Repaint;
end;
procedure TForm1.CalcMiniMapNavigatorPosition(ATop,ALeft : Integer);
var t,l : Integer;
begin
t := ATop - (MiniMapNavigator.Height div 2);
l := ALeft - (MiniMapNavigator.Width div 2);
t := -trunc(t/(MiniMapDisplayHeight/BigPic.Height));
l := -trunc(l/(MiniMapDisplayWidth/BigPic.Width));
CalcBigPicBounds(t,l);
end;
end.
ASKER
I try you code but does not work.
I have a TImage over a form, the TImage is loaded with a huge image.
I can navegate the huge image using the form scrollbars.
Im trying to use a "minimap" box (another TImage) in the form loaded with a stretched version of the huge image. I use your code to create a navegation rectangle in the "minimap", but has no efect of scroll or pan in the huge image at the main form.
What Im doing wrong with your code?