Using a TW3Image to Display Photographs

This is our first demonstration using Version 3 of Smart Mobile Studio. We are grateful to Gwen Adair, both a full-time art teacher and an artist, for allowing us to use photographs of her oil paintings to enrich this website. She holds the copyright of all photographs used by this program.

Display of an artist's work could form part of an A Level Computing project. We wanted to allow the viewer of the paintings to select first the category and then the actual photo from the name. Relevant artist's notes would be displayed also. You might like to save some of your favourite photos on your phone and classify them using the techniques we demonstrate here.

We have prepared this demonstration for viewing on a PC or maybe a tablet, and look forward to experimenting with the new flow layouts for mobiles available in Smart Mobile Studio. We use direct links to the images on the website rather than adding them as resources to the project, where the storage volume for so many photos may be problematic. We should allow the user (the displayer of the photos) to enter data using a grid so that no programming knowledge is required. We may include this in a later version of the demo. Having the ability to copy, paste and edit at speed we did not find data entry into the dataset by hard-coding too onerous. For Version 3 of Smart Mobile Studio we were promised a debugged W3Dataset and we were relieved to find that the code for the Next and Back routines is no longer identical! Please let us know if you find any documentation for the W3Dataset.

The Smart Passcal code of the unit and the XML code of the form follow the demonstration. Select different categories and images in the edit boxes below and be prepared to be impressed by the variety of expertise of the artist. You can check that:
  • the category and name match the photo displayed;
  • the artists notes always match the category of painting;
  • as additional data such as that for another boat painting is added in code the new painting name will appear in the right hand edit box automatically;
  • paintings are centred horizontally even if reduced in size;
  • if we know the size of the actual painting it is displayed (a stored width and height of 0 indicating that we do not yet know the size).

Demonstration

If the demo does not work in your current browser, try another such as Chrome. If you see no display at school, the security system might have blocked it. You can try instead this direct link to the program running on its own page.

Photos.html

Smart Pascal Code of Form

unit Form1;
{ALL IMAGES COPYRIGHT GWEN ADAIR https://www.facebook.com/gwenadairpainter/ and
https://gwenadair.wixsite.com/painter}
interface

uses 
  System.Dataset, System.Types, System.Types.Convert, system.objects, System.Time,
  System.IOUtils, System.Device.Storage, SmartCL.System, SmartCL.Time,
   SmartCL.Graphics, SmartCL.Components,
  SmartCL.FileUtils, SmartCL.Device.Storage, SmartCL.Forms, SmartCL.Fonts,
  SmartCL.Theme, SmartCL.Borders, SmartCL.Application, SmartCL.Controls.Image,
  SmartCL.Controls.Button, SmartCL.Controls.ListBox, SmartCL.Controls.Label,
  SmartCL.Controls.Memo, System.Lists, SmartCL.Grid;

type
  TForm1 = class(TW3Form)
  const
    PORTRAIT_NOTES = 'I paint from the life and also from photos, and each has ' +
      'merits. In a portrait there should be a relationship between the painter ' +
      'and the sitter, so I believe at least one sitting should be from the life. ' +
      'On the other hand, when the painting is of a stranger and is painted as a ' +
      'study or to create an image I feel is interesting and arresting to look ' +
      'at, I will happily work exclusively from photos, because I find it ' +
      'fascinating that we think we can tell so much about a person from a ' +
      'fraction of a second of their life. We read the expression, then create a ' +
      'whole narrative.';
    FIGURE_NOTES = 'Most of my current work is figure composition, where I love ' +
      'the challenge of creating balanced and interesting compositions with positive ' +
      'forms and negative spaces. The human figure is what I love to paint, and I work ' +
      'from photographic sources, often combined from several images.';
    BIRD_NOTES = 'Birds began to appear in the backgrounds of my paintings, or as ' +
      'props for some of the figures. But I discovered that they have real personalities ' +
      'which are fun to paint.';
    OTHER_NOTES = 'In addition to my main work which is figurative, I also enjoy ' +
      'painting still lives, boats and landscapes. These paintings allow me to explore ' +
      'colour and texture.';
    ZOOM_FACTOR = 0.75;
    procedure lboCategoriesSelected(Sender: TObject; ItemIndex: Integer);
    procedure lboImagesSelected(Sender: TObject; ItemIndex: Integer);

  private
    {$I 'Form1:intf'}
    ImageTable: TW3Dataset;
    Categories: array of string;
  protected
    procedure InitializeForm; override;
    procedure InitializeObject; override;
  end;

implementation

procedure TForm1.lboImagesSelected(Sender: TObject; ItemIndex: Integer);
begin
  ImageTable.First;
  var Found := False;
  repeat
    if ImageTable.Fields.FieldByName('name').asString = lboImages.Text[ItemIndex] then
      begin
        Found := true;
        var w := ImageTable.Fields.FieldByName('painting_width').asInteger;
        var h := ImageTable.Fields.FieldByName('painting_height').asInteger;
        if w > 0 then
          lblSize.Caption := 'Painting size ' + inttostr(w) + 'cm x ' + inttostr(h) + 'cm'
        else
          lblSize.Caption := '';
        photo.Url := '../images/gwen/' + ImageTable.Fields.FieldByName('filename').asString;
        photo.Width :=  ImageTable.Fields.FieldByName('image_width').asInteger;;
        photo.Height := ImageTable.Fields.FieldByName('image_height').asInteger;
        if photo.Width > 425 then
          begin  // Changing the Zoom property worked for Chrome and Edge but not Firefox.
            Photo.Width := round(Photo.Width * ZOOM_FACTOR);
            Photo.Height := round(Photo.Height * ZOOM_FACTOR);
          end;
        photo.Left := (Memonotes.Left - Photo.Width) div 2; // centre horizontally
      end;
    ImageTable.Next;
  until Imagetable.EOF or Found;
end;

procedure TForm1.lboCategoriesSelected(Sender: TObject; ItemIndex: Integer);
begin
  photo.Url := '';
  lboImages.Clear;
  lboImages.BeginUpdate;
  ImageTable.First;
  repeat   // Could try filter instead with FindNext instead of Next
   if ImageTable.Fields.FieldByName('category').asString = lboCategories.Text[ItemIndex] then
      lboImages.AddItem(ImageTable.Fields.FieldByName('name').asString);
    ImageTable.Next;
  until Imagetable.EOF;

  // Select the first image and show the selection
  lboImagesSelected(nil, 0);
  lboImages.SelectedIndex := 0;

  case ItemIndex of   // Populate memo with appropriate notes for the category selected.
    0: MemoNotes.Text := PORTRAIT_NOTES;
    1: MemoNotes.Text := FIGURE_NOTES;
    2, 3, 4: MemoNotes.Text := OTHER_NOTES;
    5: MemoNotes.Text := BIRD_NOTES;
  else
    MemoNotes.Text := OTHER_NOTES;
  end;
end;

procedure TForm1.InitializeForm;
begin
  inherited;
  categories := ['Portrait', 'Figure', 'Still Life', 'Landscape', 'Boat', 'Bird'];
  // Create the dataset and store the data
  ImageTable := TW3Dataset.Create;
  ImageTable.fieldDefs.Add("name", ftString);
  ImageTable.fieldDefs.Add("filename", ftString);
  ImageTable.fieldDefs.Add("category", ftString);
  ImageTable.fieldDefs.add("painting_width", ftInteger);  // in cm
  ImageTable.fieldDefs.add("painting_height", ftInteger); // in cm
  ImageTable.fieldDefs.add("image_width", ftInteger);     // in pixels
  ImageTable.fieldDefs.add("image_height", ftInteger);    // in pixels
  ImageTable.CreateDataset;

  ImageTable.Append;
  ImageTable.Fields.FieldByName('filename').asString := 'Lee.jpg';
  ImageTable.Fields.FieldByName('name').asString := 'Lee';
  ImageTable.Fields.FieldByName('category').asString := 'Portrait';
  ImageTable.Fields.FieldByName('image_width').asInteger := 565;
  ImageTable.Fields.FieldByName('image_height').asInteger := 565;
  ImageTable.Fields.FieldByName('painting_width').asInteger := 0;
  ImageTable.Fields.FieldByName('painting_height').asInteger := 0;
  ImageTable.Post;

  ImageTable.Append;
  ImageTable.Fields.FieldByName('filename').asString := 'SelfPortrait.jpg';
  ImageTable.Fields.FieldByName('name').asString := 'Self Portrait';
  ImageTable.Fields.FieldByName('category').asString := 'Portrait';
  ImageTable.Fields.FieldByName('image_width').asInteger := 407;
  ImageTable.Fields.FieldByName('image_height').asInteger := 406;
  ImageTable.Fields.FieldByName('painting_width').asInteger := 0;
  ImageTable.Fields.FieldByName('painting_height').asInteger := 0;
  ImageTable.Post;

  ImageTable.Append;
  ImageTable.Fields.FieldByName('filename').asString := 'CrazyManMichael.jpg';
  ImageTable.Fields.FieldByName('name').asString := 'Crazy Man Michael';
  ImageTable.Fields.FieldByName('category').asString := 'Figure';
  ImageTable.Fields.FieldByName('image_width').asInteger := 416;
  ImageTable.Fields.FieldByName('image_height').asInteger := 419;
  ImageTable.Fields.FieldByName('painting_width').asInteger := 50;
  ImageTable.Fields.FieldByName('painting_height').asInteger := 50;
  ImageTable.Post;

  ImageTable.Append;
  ImageTable.Fields.FieldByName('filename').asString := 'PublicDecency.jpg';
  ImageTable.Fields.FieldByName('name').asString := 'Public Decency';
  ImageTable.Fields.FieldByName('category').asString := 'Figure';
  ImageTable.Fields.FieldByName('image_width').asInteger := 424;
  ImageTable.Fields.FieldByName('image_height').asInteger := 423;
  ImageTable.Fields.FieldByName('painting_width').asInteger := 30;
  ImageTable.Fields.FieldByName('painting_height').asInteger := 30;
  ImageTable.Post;

  ImageTable.Append;
  ImageTable.Fields.FieldByName('filename').asString := 'ThreeHerrings.jpg';
  ImageTable.Fields.FieldByName('name').asString := 'Three Herrings';
  ImageTable.Fields.FieldByName('category').asString := 'Still Life';
  ImageTable.Fields.FieldByName('image_width').asInteger := 581;
  ImageTable.Fields.FieldByName('image_height').asInteger := 582;
  ImageTable.Fields.FieldByName('painting_width').asInteger := 0;
  ImageTable.Fields.FieldByName('painting_height').asInteger := 0;
  ImageTable.Post;

  ImageTable.Append;
  ImageTable.Fields.FieldByName('filename').asString := 'OrangesAndCherries.jpg';
  ImageTable.Fields.FieldByName('name').asString := 'Oranges and Cherries';
  ImageTable.Fields.FieldByName('category').asString := 'Still Life';
  ImageTable.Fields.FieldByName('image_width').asInteger := 497;
  ImageTable.Fields.FieldByName('image_height').asInteger := 374;
  ImageTable.Fields.FieldByName('painting_width').asInteger := 0;
  ImageTable.Fields.FieldByName('painting_height').asInteger := 0;
  ImageTable.Post;

  ImageTable.Append;
  ImageTable.Fields.FieldByName('name').asString := 'DumfriesLandscape';
  ImageTable.Fields.FieldByName('filename').asString := 'DumfriesLandscape.jpg';
  ImageTable.Fields.FieldByName('category').asString := 'Landscape';
  ImageTable.Fields.FieldByName('image_width').asInteger := 250;
  ImageTable.Fields.FieldByName('image_height').asInteger := 166;
  ImageTable.Fields.FieldByName('painting_width').asInteger := 0;
  ImageTable.Fields.FieldByName('painting_height').asInteger := 0;
  ImageTable.Post;

  ImageTable.Append;
  ImageTable.Fields.FieldByName('name').asString := 'Carrick Shore';
  ImageTable.Fields.FieldByName('filename').asString := 'CarrickShore.jpg';
  ImageTable.Fields.FieldByName('category').asString := 'Landscape';
  ImageTable.Fields.FieldByName('image_width').asInteger := 499;
  ImageTable.Fields.FieldByName('image_height').asInteger := 332;
  ImageTable.Fields.FieldByName('painting_width').asInteger := 0;
  ImageTable.Fields.FieldByName('painting_height').asInteger := 0;
  ImageTable.Post;

  ImageTable.Append;
  ImageTable.Fields.FieldByName('name').asString := 'River Nith at Dumfries';
  ImageTable.Fields.FieldByName('filename').asString := 'RiverNithAtDumfries.jpg';
  ImageTable.Fields.FieldByName('category').asString := 'Landscape';
  ImageTable.Fields.FieldByName('image_width').asInteger := 432;
  ImageTable.Fields.FieldByName('image_height').asInteger := 434;
  ImageTable.Fields.FieldByName('painting_width').asInteger := 70;
  ImageTable.Fields.FieldByName('painting_height').asInteger := 70;
  ImageTable.Post;

  ImageTable.Append;
  ImageTable.Fields.FieldByName('filename').asString := 'OneWeeBoat.jpg';
  ImageTable.Fields.FieldByName('name').asString := 'One Wee Boat, PortPatrick';
  ImageTable.Fields.FieldByName('category').asString := 'Boat';
  ImageTable.Fields.FieldByName('image_width').asInteger := 557;
  ImageTable.Fields.FieldByName('image_height').asInteger := 564;
  ImageTable.Fields.FieldByName('painting_width').asInteger := 30;
  ImageTable.Fields.FieldByName('painting_height').asInteger := 30;
  ImageTable.Post;

  ImageTable.Append;
  ImageTable.Fields.FieldByName('filename').asString := 'WeeBoats.jpg';
  ImageTable.Fields.FieldByName('name').asString := 'Wee Boats, PortPatrick';
  ImageTable.Fields.FieldByName('category').asString := 'Boat';
  ImageTable.Fields.FieldByName('image_width').asInteger := 497;
  ImageTable.Fields.FieldByName('image_height').asInteger := 371;
  ImageTable.Fields.FieldByName('painting_width').asInteger := 50;
  ImageTable.Fields.FieldByName('painting_height').asInteger := 40;
  ImageTable.Post;

  ImageTable.Append;
  ImageTable.Fields.FieldByName('filename').asString := 'Puffins.jpg';
  ImageTable.Fields.FieldByName('name').asString := 'Puffins';
  ImageTable.Fields.FieldByName('category').asString := 'Bird';
  ImageTable.Fields.FieldByName('image_width').asInteger := 378;
  ImageTable.Fields.FieldByName('image_height').asInteger := 192;
  ImageTable.Fields.FieldByName('painting_width').asInteger := 40;
  ImageTable.Fields.FieldByName('painting_height').asInteger := 20;
  ImageTable.Post;

  ImageTable.Append;
  ImageTable.Fields.FieldByName('filename').asString := 'Crow.jpg';
  ImageTable.Fields.FieldByName('name').asString := 'Crow';
  ImageTable.Fields.FieldByName('category').asString := 'Bird';
  ImageTable.Fields.FieldByName('image_width').asInteger := 573;
  ImageTable.Fields.FieldByName('image_height').asInteger := 582;
  ImageTable.Fields.FieldByName('painting_width').asInteger := 0;
  ImageTable.Fields.FieldByName('painting_height').asInteger := 0;
  ImageTable.Post;

  ImageTable.Append;
  ImageTable.Fields.FieldByName('filename').asString := 'Heron.jpg';
  ImageTable.Fields.FieldByName('name').asString := 'Heron';
  ImageTable.Fields.FieldByName('category').asString := 'Bird';
  ImageTable.Fields.FieldByName('image_width').asInteger := 291;
  ImageTable.Fields.FieldByName('image_height').asInteger := 580;
  ImageTable.Fields.FieldByName('painting_width').asInteger := 21;
  ImageTable.Fields.FieldByName('painting_height').asInteger := 43;
  ImageTable.Post;

  lboCategories.BeginUpdate; // populate categories listbox
  for var i := 0 to 5 do
    lboCategories.AddItem(Categories[i]);
  lboCategories.EndUpdate;
  // Mimic selections from each listbox
  lboCategoriesSelected(nil, 3);
  lboImagesSelected(nil, 0);
  // Show the selections so that they match up with the image displayed
  lboCategories.SelectedIndex := 3;
  lboImages.SelectedIndex := 0;
end;

procedure TForm1.InitializeObject;
begin
  inherited;
  {$I 'Form1:impl'}
end;

initialization
  Forms.RegisterForm({$I %FILE%}, TForm1);
end.    

XML Code of Form

<SMART>
  <Form version="3" subversion="0">
    <Created>2019-08-19T14:03:03.872</Created>
    <Modified>2019-09-03T11:36:55.663</Modified>
    <object type="TW3Form">
      <Caption>W3Form</Caption>
      <Name>Form1</Name>
      <object type="TW3ListBox">
        <Width>112</Width>
        <Top>32</Top>
        <Left>440</Left>
        <Height>168</Height>
        <Name>lboCategories</Name>
        <OnSelected>lboCategoriesSelected</OnSelected>
      </object>
      <object type="TW3ListBox">
        <Width>184</Width>
        <Top>32</Top>
        <Left>564</Left>
        <Height>168</Height>
        <Name>lboImages</Name>
        <OnSelected>lboImagesSelected</OnSelected>
      </object>
      <object type="TW3Image">
        <Width>408</Width>
        <Top>16</Top>
        <Height>400</Height>
        <Name>photo</Name>
      </object>
      <object type="TW3Label">
        <Caption><b>Category</b></Caption>
        <Width>80</Width>
        <Left>441</Left>
        <Height>32</Height>
        <Name>lblCategory</Name>
      </object>
      <object type="TW3Label">
        <Caption><b>Name of Painting</b></Caption>
        <Width>176</Width>
        <Left>565</Left>
        <Height>32</Height>
        <Name>lblName</Name>
      </object>
      <object type="TW3Label">
        <Width>188</Width>
        <Top>201</Top>
        <Left>556</Left>
        <Height>23</Height>
        <Name>lblSize</Name>
      </object>
      <object type="TW3Memo">
        <Width>312</Width>
        <Top>256</Top>
        <Left>440</Left>
        <Height>146</Height>
        <Name>memoNotes</Name>
      </object>
      <object type="TW3Label">
        <Caption><b>Artist's Notes</b></Caption>
        <Width>128</Width>
        <Top>224</Top>
        <Left>544</Left>
        <Height>32</Height>
        <Name>lblNotes</Name>
      </object>
    </object>
  </Form>
</SMART>
Programming - a skill for life!

How to use a range of visual components such as edit boxes, combo boxes, grids and charts on one or multiple forms (including a modal form)