[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 325
  • Last Modified:

I am writing a Bass Tab generator (guitar) and need help on image onclick events

Hi team !

My wife is learning the Bass guitar and I am writing a program for her to write her own bass tabs.
Even if you don't know what a tab is, my webpage will show you. http://www.bradymax.com/bass

The page explains what I am trying to achieve however, the format of the tab needs to look similar to below (note the dashes(-) between the numbers...they need to be there but only so the result looks like a tab (they just separate the letters or numbers but don't have any other significance.

|G-D-C---------|  or   |1-3-2--------|
|-------A-B-A--|        |-------6-5-6-|       one show the notes (letters) and one the actual fret they click on that particular string.

That is just a two string example but the bass has 4 strings.  
Any help would be super !

Mark
0
Mark Brady
Asked:
Mark Brady
  • 5
  • 4
  • 3
2 Solutions
 
calinutzCommented:
I suggest that you use a StringGrid containing pictures (your tabs). And this way you can build the tabs anyway you like (tabular). And the events of the onClick, onMouseUp,onMouseUp will help you achieve your goal.
 
0
 
calinutzCommented:
It seems like you have an array of symbols (letters and dashes) with same number of symbols on each row. So the StringGrid would be the most appropiate for your problem.
  You can also have two stringGrids one with pictures and one with the symbols... and use them accordingly.
Did I understand your problem?
0
 
calinutzCommented:
Something like this?

object Form1: TForm1
  Left = 192
  Top = 107
  Width = 486
  Height = 315
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object StringGrid1: TStringGrid
    Left = 12
    Top = 16
    Width = 461
    Height = 129
    ColCount = 7
    TabOrder = 0
    OnClick = StringGrid1Click
    OnDrawCell = StringGrid1DrawCell
  end
  object StringGrid2: TStringGrid
    Left = 12
    Top = 148
    Width = 461
    Height = 95
    ColCount = 7
    DefaultRowHeight = 16
    Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goEditing]
    TabOrder = 1
    OnClick = StringGrid2Click
  end
end
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
calinutzCommented:
And the code...

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Grids, jpeg, ExtCtrls;

type
  TForm1 = class(TForm)
    StringGrid1: TStringGrid;
    StringGrid2: TStringGrid;
    procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
    procedure FormDestroy(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure StringGrid1Click(Sender: TObject);
    procedure StringGrid2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  bmp:TBitMap;

implementation

{$R *.dfm}

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
var
  SRect,DRect : TRect;
begin
  (Sender as TStringGrid).Canvas.FillRect(Rect);
  if (Sender as TStringGrid).Cells[ARow,ACol] = '@' then
    begin
      SRect := Classes.Rect(0,0,Bmp.Width,Bmp.Height);
      DRect.Left := Rect.Left+3;
      DRect.Top := Rect.Top+(Rect.Bottom-Rect.Top-Bmp.Height) div 2;
      DRect.Right := DRect.Left+SRect.Right+1;
      DRect.Bottom := DRect.Top+SRect.Bottom+1;
      (Sender as TStringGrid).Canvas.BrushCopy(DRect,Bmp,SRect,clOlive);
    end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
Bmp.Free;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
 i:integer;
begin
Bmp := TBitmap.Create;
  Bmp.LoadFromFile('C:\== pics\basstab.bmp');
  StringGrid1.ColWidths[1]:=bmp.Width+3;
  StringGrid1.ColWidths[2]:=bmp.Width+3;
  StringGrid1.ColWidths[3]:=bmp.Width+3;
  StringGrid1.ColWidths[4]:=bmp.Width+3;
  StringGrid1.ColWidths[5]:=bmp.Width+3;
  StringGrid1.ColWidths[6]:=bmp.Width+3;

  StringGrid1.Cells[1,1] := '@';
  StringGrid1.Cells[2,1] := '@';
  StringGrid1.Cells[3,1] := '@';
  StringGrid1.Cells[4,1] := '@';

  StringGrid1.Cells[1,2] := '@';
  StringGrid1.Cells[2,2] := '@';
  StringGrid1.Cells[3,2] := '@';
  StringGrid1.Cells[4,2] := '@';

  StringGrid1.Cells[1,3] := '@';
  StringGrid1.Cells[2,3] := '@';
  StringGrid1.Cells[3,3] := '@';
  StringGrid1.Cells[4,3] := '@';

  StringGrid1.Cells[1,4] := '@';
  StringGrid1.Cells[2,4] := '@';
  StringGrid1.Cells[3,4] := '@';
  StringGrid1.Cells[4,4] := '@';

  StringGrid1.Cells[1,5] := '@';
  StringGrid1.Cells[2,5] := '@';
  StringGrid1.Cells[3,5] := '@';
  StringGrid1.Cells[4,5] := '@';

  StringGrid1.Cells[1,6] := '@';
  StringGrid1.Cells[2,6] := '@';
  StringGrid1.Cells[3,6] := '@';
  StringGrid1.Cells[4,6] := '@';

  StringGrid2.Cells[1,1] := '1';
  StringGrid2.Cells[1,2] := '-';
  StringGrid2.Cells[1,3] := '-';
  StringGrid2.Cells[1,4] := '-';

  For i:=1 to StringGrid2.ColCount-1 do
  StringGrid2.ColWidths[i]:=12;

  StringGrid2.Cells[2,1] := '-';
  StringGrid2.Cells[2,2] := '-';
  StringGrid2.Cells[2,3] := '-';
  StringGrid2.Cells[2,4] := '-';

  StringGrid2.Cells[3,1] := '2';
  StringGrid2.Cells[3,2] := '-';
  StringGrid2.Cells[3,3] := '-';
  StringGrid2.Cells[3,4] := '-';

end;

procedure TForm1.StringGrid1Click(Sender: TObject);
begin
StringGrid2.Col:=StringGrid1.Col;
StringGrid2.Row:=StringGrid1.Row;
end;

procedure TForm1.StringGrid2Click(Sender: TObject);
begin
StringGrid1.Col:=StringGrid2.Col;
StringGrid1.Row:=StringGrid2.Row;
end;

end.
0
 
calinutzCommented:
Oh yes... and by the way just make a bitmap of what your tab looks like (I selected a piece from your website) and place it here:
C:\== pics\.....     and name it basstab.bmp
0
 
Mark BradyAuthor Commented:
That might be what I need.  Couple of questions.  With the .bmp do I create a bmp from the graphic bass neck I have on the site (pictured at top).  If so, do I slice it up or does the string grid do that in the code ?  I'll give this code a run when I get home from work so thanks heps.

Mark
0
 
Slick812Commented:
hello  elvin66 , , It seems like having so many TImages for your Bass_Neck Bitmap click is overkill? ?
Here is my version without ANY TImages, it uses 2 TPaintBox for the Bass_Neck bitmap display, and you can Click On the PaintBox5 for the Notes to be recorded, the PaintBox6 will display the "Play" sequence,
there are two button click events, the first records the aryPlay (array of notes played) to a file,
the second button click Reads the file and displays the "Played" notes recorded in the file.



  TNeck = Record
    ret: TRect;
    name: String[2];
    end;

  TPluck = record
    Pos: TPoint;
    xPos: Integer;
    note: String[2];
    end;

  TForm1 = class(TForm)


  private
    { Private declarations }
    BassNeck: TBitmap;
    Necks: Array[0..3,0..9] of TNeck;
    DownRect: TPoint;
    PlayPos: TPoint;
    aryPlay: Array of TPluck;




procedure TForm1.FormCreate(Sender: TObject);
var
i, j: Integer;

const
aryLeft: array[0..10] of Integer = (10,55,102,150,197,243,290,337,384,431,479);
// the above x Position are from your BassNeck bitmap
aryTop: Array[0..3] of Integer = (4,25,46,68);
// the above y pos are fome neck bitmap
aryNotes: Array[0..3,0..9] of String[2] = (
 ('G','A','B'#164,'B','C','C#','D','E'#164,'E','F'),
 ('D','E'#164,'E','F','F#','G','A','B'#164,'B','C'),
 ('A','B'#164,'B','C','C#','D','E'#164,'E','F','F#'),
 ('E','F','F#','G','A','B'#164,'B','C','C#','D'));

begin
BassNeck := TBitMap.Create;
BassNeck.LoadFromFile(ExtractFilePath(ParamStr(0))+'BassNeck.bmp');
// BassNeck.bmp is from your web page jpg, turned into bitmap
PaintBox5.Width := BassNeck.Width;
PaintBox5.Height := BassNeck.Height;

PaintBox6.Width := BassNeck.Width;
PaintBox6.Height := BassNeck.Height;
PaintBox6.Top := PaintBox5.Top+BassNeck.Height+20;
PlayPos.x := 10;
PlayPos.y := 0;

for i := 0 to 3 do
  for j := 0 to 9 do
  begin
// fill up the array with the positions for click rectangles and the note names
  Necks[i,j].ret := Rect(aryLeft[j],aryTop[i],aryLeft[j+1],aryTop[i]+16);
  Necks[i,j].name := aryNotes[i,j];
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeAndNil(BassNeck);
end;

procedure TForm1.PaintBox5Paint(Sender: TObject);
begin
// you mus draw the bitmap on the paintbox
PaintBox5.Canvas.Draw(0,0, BassNeck);
end;


procedure TForm1.PaintBox5MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
Pnt1: TPoint;
i, j: Integer;
begin
// this mouse down draws the note name on the Clickable Paintbox
Pnt1.x := X;
Pnt1.y := Y;
for i := 0 to 3 do
  for j := 0 to 9 do
  if PtInRect(Necks[i,j].ret,Pnt1) then
    begin
    PaintBox5.Canvas.brush.Color := $3B6FE0;
    PaintBox5.Canvas.Rectangle(Necks[i,j].ret);
    DrawText(PaintBox5.Canvas.Handle,@Necks[i,j].name[1],-1, Necks[i,j].ret,
             DT_SINGLELINE or DT_VCENTER or DT_CENTER);
    DownRect.x := i;
    DownRect.y := j;
    Break;
    end;
end;


procedure TForm1.PaintBox5MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
// mouse Up erases the PaintBox5 and draws the PaintBox6
PaintBox5.Canvas.CopyRect(Necks[DownRect.x, DownRect.y].ret,
          BassNeck.Canvas,Necks[DownRect.x, DownRect.y].ret);
PlayPos.y := DownRect.x;
PaintBox6.Canvas.Font.Name := 'Times New Roman';
SetBkColor(PaintBox6.Canvas.handle,$FFFFFF);

// I record the click event in an Array of TPluck, aryPlay
SetLength(aryPlay, Length(aryPlay)+1);
aryPlay[High(aryPlay)].Pos := DownRect;
aryPlay[High(aryPlay)].xPos := PlayPos.x;
aryPlay[High(aryPlay)].note := Necks[DownRect.x, DownRect.y].name;
PaintBox6.Canvas.TextOut(PlayPos.x,(PlayPos.y*21)+8, Necks[DownRect.x, DownRect.y].name);
PlayPos.x := 3+PlayPos.x+ PaintBox6.Canvas.TextWidth(Necks[DownRect.x, DownRect.y].name);
// the above will save the X position as the text is painted
end;


procedure TForm1.PaintBox6Paint(Sender: TObject);
var
i: Integer;
begin
// you need to paint the Neck bitmap and the aryPlay of notes
PaintBox6.Canvas.Font.Name := 'Times New Roman';
PaintBox6.Canvas.Draw(0,0,BassNeck);
SetBkColor(PaintBox6.Canvas.handle,$FFFFFF);
for i := 0 to High(aryPlay) do
  PaintBox6.Canvas.TextOut(aryPlay[i].xPos,(aryPlay[i].Pos.x*21)+8, aryPlay[i].note);
end;



 = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
the following code is for 2 button click events that save and load the aryPlay from file


procedure TForm1.but_SavePlayClick(Sender: TObject);
var
FS1: TFileStream;
begin
if Length(aryPlay) < 1 then
  begin
  ShowMessage('The Play Array has no members');
  Exit;
  end;

FS1 := TFileStream.Create('E:\Play Notes1.pna',
                 fmCreate or fmOpenWrite or fmShareDenyWrite);
FS1.WriteBuffer(aryPlay[0], SizeOf(TPluck)*Length(aryPlay));
FreeAndNil(FS1);
ShowMessage('Play File has been saved');
end;




procedure TForm1.but_LoadPlayClick(Sender: TObject);
var
FS1: TFileStream;
size: Integer;
begin
FS1 := TFileStream.Create('E:\Play Notes1.pna',
                 fmOpenRead or fmShareDenyWrite);
try
size := FS1.Size;
if (size = 0) or (size mod SizeOf(TPluck) <> 0) then
  begin
  ShowMessage('Play File is NOT compatable with this');
  Exit
  end;
size := size div SizeOf(TPluck);
SetLength(aryPlay, size);
FS1.ReadBuffer(aryPlay[0], SizeOf(TPluck)*Length(aryPlay));
finally FreeAndNil(FS1); end;
PaintBox6Paint(PaintBox6);
end;


 = = = = = = = = = = = = = = = = = = = = = = = = = = =

I hope you can get some ideas from this. . .
ask questions if you need more information
0
 
Mark BradyAuthor Commented:
Howdy Slick !

This looks interesting to me as well.  Unfortunately I have tried to utilise this code to see how it works but I can't create the program as I am missing something.

Would you be able to post the complete code for me ?   I am using Borland Developer Studio and have just upgraded from Delphi 5.  I'm not sure about this new version but I am opening new 'Package - Delphi for Win32'      then I get a blank form.  Now where exactly I put the code I'm unsure of.  Can you please post me either a sample of your executable or the whole code starting from  'unit1.pas' so I can try to re-create it on my system.

Sorry for my ignorance but I've been away from programming for 2 years and just getting back into it.

Thank you so much

Mark
0
 
Slick812Commented:
I no longer can afford to do an entire program for all of the EE questions I try to comment on here, this code is in an app that has maybe 12 or 15 other EE question code in it
0
 
Slick812Commented:
most of the time I feel that my code should give Ideas and maybe teach a method to do things, other than copy and paste and not know what the code is doing, , I may have forgotten to tell you to add something to the uses clause?. . . I do not have time today to redo this as a stand alone program for you
0
 
Slick812Commented:
all of the procedures that are in my code are the standard events for the different Objects -

the
procedure TForm1.FormCreate(Sender: TObject);
  is the Form's OnCreate event

the
procedure TForm1.PaintBox5Paint(Sender: TObject);
  is the PaintBox5  OnPaint event

the
procedure TForm1.PaintBox6Paint(Sender: TObject);
  is the PaintBox6  OnPaint event

the
procedure TForm1.PaintBox5MouseDown(Sender: TObject; Button: TMouseButton;
  if the PaintBox5   OnMouseDown   event
0
 
Mark BradyAuthor Commented:
I am going to split the points as both ideas were great.  I am going to run with my original idea though as my question has not been answered in full.  I asked for a way to save these tab files and reopen them as well as having 2 tabs, 1 with the notes and 1 with the fret numbers.  I have tried both codes from the answers and although my code is longer, I can work with it better because of my limited (basic) programming knowledge.  Thank you both for your input though.

Regards
Mark
0

Featured Post

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

  • 5
  • 4
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now