Delphi. Как указать папку "по умолчанию" для новых проектов

Надоело мне, что Delphi предлагает каждый новый проект сохранить в папке My documents. И задался я вопросом, а как бы эту папку изменить. Оказалось – очень просто. Настолько просто, что даже и рассказывать тут не о чем. Но я всё-таки расскажу так как я (почему-то) долгое время считал, что такой опции просто нет.
Главное меню –> Tools –> Options –> Environment Options –> Default Project
Или, с помощью IDE insight: Ctrl+. ввести default project + Enter

В Delphi XE-XE5 эти настройки хранятся в реестре:
HKEY_CURRENT_USERSoftwareEmbarcaderoBDS12.0GlobalsDefaultProjectsDirectory
Тип данных: REG_SZ

[[ This is a content summary only. Visit my website for full links, other content, and more! ]]

Читать на сайте автора.

1. Начало работы с RAD Studio

После успешной инсталляции на вашем компьютере RAD Studio можно начать изготавливать полезные программы с помощью этой замечательной системы быстрой (и качественной) разработки программ.

Для запуска на выполнение RAD Studio XE3 нужно щелкнуть мышью по кнопке Пуск, выбрать Все программы, затем Embarcadero RAD Studio XE3, в появившемся меню выбрать элемент RAD Studio XE3. Появится главное окно программы.

Для создания нового проекта (программы) в Delphi нужно в этом окне щелкнуть мышью по элементу главного меню File, в выпадающем списке выбрать New и в появившемся субменю выбрать элемент VCL Forms Application — DelphiБудет создана пустая форма.


В центре располагается форма, в левом нижнем углу — Инспектор объектов (Object Inspector), в правом нижнем углу — Панель инструментов (Tool Palette).

Инспектор объектов позволяет изменять характеристики выбранного на форме компонента. Он содержит две вкладки — Properties (свойства) и Events (события).

Во вкладке Properties содержится список свойств текущего компонента формы или самой формы. Во вкладке Events присутствует список событий, допустимых для данного компонента.

Панель инструментов содержит большое количество компонентов, как визуальных, так и невизуальных, которые позволяют очень удобно создавать программы любой сложности и с хорошим интерфейсом. Насколько разумной будет функциональность программы и сколь хороший интерфейс — это уж зависит от разработчика.

Созданную пустую форму можно тут же запустить на выполнение. Для этого нужно щелкнуть по кнопке 

или нажать клавиши Shift+Ctrl+F9.

Через несколько секунд после трансляции появится пустая форма. Ее можно перемещать по экрану обычным образом, изменять размеры, максимизировать, сворачивать.

Для закрытия формы нужно щелкнуть мышью по кнопке закрытия в правом верхнем углу формы.

Сейчас давайте установим некоторые свойства формы, создадим классическую программу, которую вначале создают практически все специалисты, обучающие программистов началам использования языков программирования и средств быстрой разработки программ. Я имею в виду программу Hello world, иными словами — здравствуй мир.

Установите заголовок формы — текст «Привет всем!». Для этого в Инспекторе объектов найдите свойство Caption и наберите в правой части этого свойства текст Привет всем!.

Далее сделайте так, чтобы форма при запуске на выполнение располагалась в центре экрана. Для этого в Инспекторе объектов для свойства Position из выпадающего списка справа выберите значение poScreenCenter.

Перейдите к Панели инструментов (Tool Palette). Раскройте список Standard, щелкните мышью по строке TLabel, а затем по любому месту на форме. На форме будет размещен компонент Метка (label). Установите его свойство Caption в значение «Привет ребята (и девчата)». Переместите на форме мышью компонент так, чтобы он выглядел достаточно презентабельным, то есть, в центре окна.






Запустите программу на выполнение и получите удовольствие от созданного простого, но доброжелательного проекта.




Для того чтобы создать такой же проект в C++ Builder, нужно в меню File выбрать Newа затем элемент VCL Forms Application — C++.

Также будет создана новая пустая форма. Инспектор объектов и Панель инструментов выглядят таким же образом, как и в случае создания проекта Delphi (на начальном уровне разработки).

При запуске на выполнение программы будет точно такая же форма с теми же характеристиками.

Задайте форме те же свойства и поместите на форму компонент метки, что и при работе с формой Delphi, и запустите на выполнение. Вы видите, что внешний вид точно такой же.

* * *

В следующий раз мы рассмотрим многие наиболее важные свойства формы и поэкспериментируем с ними, чтобы увидеть, какие полезные возможности нам дает использование RAD Studio.

Читать на сайте автора.

Запуск “самого себя” с правами NT AUTHORITY

Читать на сайте автора.

0. Введение

Здесь я собираюсь поделиться своими знаниями и некоторым опытом в области программирования с другими программистами и с теми, кто собирается стать программистом.

Программирование вообще — это огромная область человеческой деятельности. Нельзя научиться с блеском писать гениальные программы на различных языках в различных средах разработки для различных операционных систем. По этой причине серьезным разработчикам следует посвятить немалое время для изучения и использования конкретных систем.

Я хочу рассказать о двух системах, с которыми давно и, должен признаться, с удовольствием работаю. Это системы Delphi и C++Builder. Их поддержкой и развитием на сегодняшний день занимается компания Embarcadero. В Интернете сведения о компании и ее продуктах можно найти на сайте www.embarcadero.com.

Delphi и C++Builderназываются интегрированными средами разработки (IDE, Integrated Development Environment). В основе Delphi лежит расширенный язык Объектный Паскаль, в основе C++Builder — язык C++. Их интегрированные среды очень похожи, а используемые языки различаются. Какой язык выбрать, решает разработчик или руководство его компании.

Я собираюсь неспеша начать рассматривать средства разработки программ, компоненты и языковые конструкции с тем, чтобы вы получили необходимые сведения для создания реально нужных общественному прогрессу программ. Надеюсь, этот материал будет полезен и моим студентам, рвущимся к знаниям в области программирования.

Приступим.

Читать на сайте автора.

2. Пишем детскую программку под Android: основа

Продолжаем писать программку для детей под Android (требования мы сформулировали в первом посте). Начнём с того, что сделаем совсем простой проект – программку, которая умеет

Читать на сайте автора.

2. Пишем детскую программку под Android: основа

Продолжаем писать программку для детей под Android (требования мы сформулировали в первом посте).
Начнём с того, что сделаем совсем простой проект – программку, которая умеет листать картинки при нажатии на кнопку. Картинки запакуем в ресурсы программы.
Создаём новый проект: Firemonkey Mobile application –> Blank application Разрешения
Отключим ненужные permissions. Так как по умолчанию включено слишком много всего.
Project –> Options –> Uses permissions. (Или через Help Insight: Ctrl+. –> ввести Permission + Enter, но это как-то странно работает). Сверху выберем All configurations Android Platform, так как список Permissions доступен только для Android платформы.

Как вы видите на скриншоте жирным false отмечены те настройки, у которых значение отличается от…

[[ This is a content summary only. Visit my website for full links, other content, and more! ]]

Читать на сайте автора.

Delphi плюс Android? Есть идея! Tap#4

Эта заметка является продолжением предыдущих: Tap#1, Tap#2, Tap#3. И в ней я поговорю об анимации.

В моей будущей игре у персонажа будет два основных состояния. Назовём их “Открытый” и “Закрытый”. В первом состоянии внешний вид персонажа соответствует тому, который разработан в дизайнере и сохранён в переменной DesignPositions. Во втором, вид персонажа будет другой. Для робота я решил так: меняем цвет на серый, чуть-чуть уменьшаем общий размер, втягиваем ноги, руки и антенны, закрываем глаза.

Соответственно переход из состояния в состояние будет анимированным.

Делаю я следующим образом: сразу после создания фреймы, в методе SaveDesignPositions, произвожу необходимые трансформации и сохраняю положения и размеры объектов в переменную FClosedPositions. Вот так:

type
  TframeRobot = class(TNPC)
  private
    FClosedPositions: TDesignPositions;
  public
    procedure SaveDesignPositions; override;
//...
procedure TframeRobot.SaveDesignPositions;
  procedure SwitchToClose;
  const
    kBody = 9/10;
    kEye = 1/4;
    kAnt = 1/4;
    kHand = 5/6;
    kHand2 = 1/2;
    kLeg = 1/3;
  begin
    // HINT: мы смотрим роботу в лицо, поэтому левая рука - справа, правая - слева

    // уменьшаем Body
    Body.Position.X := Body.Position.X + Body.Width / 2 * (1 - kBody);
    Body.Width := Body.Width * kBody;
    Body.Position.Y := Body.Position.Y + Body.Height / 2 * (1 - kBody);
    Body.Height := Body.Height * kBody;
    // смещаем соединительные линии между телом и ногами
    LegLeftCon.Position.Y := LegLeftCon.Position.Y - LegLeftCon.Height * kBody;
    LegRightCon.Position.Y := LegRightCon.Position.Y - LegRightCon.Height * kBody;

    // уменьшаем Head
    Head.Position.X := Head.Position.X + Head.Width / 2 * (1 - kBody);
    Head.Width := Head.Width * kBody;
    Head.Position.Y := Head.Position.Y + Head.Height / 2 * (1 - kBody);
    Head.Height := Head.Height * kBody;

    // приближаем Head к Body
    HeadCenter.Position.Y := HeadCenter.Position.Y + Head.Height * (1 - kBody);

    // закрываем глазки
    EyeLeft.Position.Y := EyeLeft.Position.Y * kBody + EyeLeft.Height * kEye;
    EyeLeft.Height := EyeLeft.Height * kEye;
    EyeRight.Position.Y := EyeRight.Position.Y * kBody + EyeRight.Height * kEye;
    EyeRight.Height := EyeRight.Height * kEye;

    // уменьшаем и прячем антенны
    AntenaRight.Position.Y := AntenaRight.Position.Y * kBody + AntenaRight.Height * kBody;
    AntenaRight.Height := AntenaRight.Height * kBody * kAnt;
    AntenaRight.Position.Y := AntenaRight.Position.Y - AntenaRight.Height;

    AntenaLeft.Position.Y := AntenaLeft.Position.Y * kBody + AntenaLeft.Height * kBody;
    AntenaLeft.Height := AntenaLeft.Height * kBody * kAnt;
    AntenaLeft.Position.Y := AntenaLeft.Position.Y - AntenaLeft.Height;

    // прячем руки
    HandRightCenter.RotationAngle := 90;
    HandRightCenter.Position.X := HandRightCenter.Position.X + HandRight.Height * kHand;
    HandRightCenter.Position.Y := HandRightCenter.Position.Y + HandRight.Width * kHand2;

    HandLeftCenter.RotationAngle := -90;
    HandLeftCenter.Position.X := HandLeftCenter.Position.X - HandLeft.Height * kHand;
    HandLeftCenter.Position.Y := HandLeftCenter.Position.Y + HandLeft.Width * kHand2;

    // прячем ноги
    LegRightCenter.Position.Y := LegRightCenter.Position.Y - LegRight.Height * kLeg;
    LegLeftCenter.Position.Y := LegLeftCenter.Position.Y - LegLeft.Height * kLeg;
  end;
begin
  inherited;

  SwitchToClose;
  FClosedPositions := TDesignPositions.Create;
  SavePositionsTo(FClosedPositions);
end;

Здесь TframeRobot – это наш персонаж, про TNPC и SaveDesignPositions я писал в предыдущей заметке. Вот так выглядит наш робот до и после трансформации:

DesignPositions соответствуют картинке слева, ClosedPositions – картинке справа. Это начальное и конечное состояние в анимации. Сама анимация протекает во времени. Если взять начало времени за ноль, а конец – за единицу, то положение объекта легко рассчитать по формуле:  f(t) = a + (b – a) * t, где t – время от 0 до 1, а – начальная координата, b – конечная координата, f – искомая координата. В библиотеке FMX такая функция уже реализована и называется InterpolateSingle.

Вот так у меня выглядит процедура для изменения размеров и положений объектов робота:

procedure TframeRobot.ProcessOpenCloseAnimation(const AProgress: Single);
var
  BodyColor: TAlphaColor;
  i: Integer;
  CP, DP: TDesignPosition;
  AControl: TControl;
begin
  BodyColor := InterpolateColor($FF99CC00, TAlphaColors.Gray, AProgress);

  // цикл от 1, т.к. саму фрейму масштабировать не надо
  for i := 1 to DesignPositions.Count - 1 do
  begin
    AControl := Controls[i];
    CP := ClosedPositions.Items[i];
    DP := DesignPositions.Items[i];

    // цвет
    if (AControl is TShape) and (AControl <> EyeLeft) and (AControl <> EyeRight) then
      if TShape(AControl).Fill.Color <> TAlphaColorRec.Null then
        TShape(AControl).Fill.Color := BodyColor;

    // положение
    AControl.Position.X := InterpolateSingle(DP.Left, CP.Left, AProgress) * ScaleFactor;
    AControl.Position.Y := InterpolateSingle(DP.Top, CP.Top, AProgress) * ScaleFactor;

    // размер применяем ко всем, кроме "опорных" точек
    if Pos('Center', AControl.Name) = 0 then
    begin
      AControl.Width := InterpolateSingle(DP.Width, CP.Width, AProgress) * ScaleFactor;
      AControl.Height := InterpolateSingle(DP.Height, CP.Height, AProgress) * ScaleFactor;
    end;

    // поворот
    AControl.RotationAngle := InterpolateSingle(DP.RotationAngle, CP.RotationAngle, AProgress);
  end;
end;

(Про ScaleFactor я писал в предыдущей заметке.)

Как видите, ничего тут сложного нет.

Теперь создаём TFloatAnimation-объект, назовём его faOpenClose:

  object faOpenClose: TFloatAnimation
    Duration = 0.300000000000000000
    OnProcess = faOpenCloseProcess
    PropertyName = 'OpenCloseValue'
  end

Далее у фреймы:

type
  TframeRobot = class(TNPC)
  ..
    procedure faOpenCloseProcess(Sender: TObject);
  private
    FOpenCloseValue: Single;
    FClosed: Boolean;
  ..
  public
    property OpenCloseValue: Single read FOpenCloseValue write FOpenCloseValue;
  ..
procedure TframeRobot.faOpenCloseProcess(Sender: TObject);
begin
  ProcessOpenCloseAnimation(FOpenCloseValue);
end;

И осталось запустить анимацию в прямом:

procedure TframeRobot.CloseWithAnimation;
begin
  FClosed := True;
  faOpenClose.Stop;
  faOpenClose.StartValue := 0;
  faOpenClose.StopValue := 1;
  faOpenClose.Start;
end;

, либо обратном направлении:

procedure TframeRobot.OpenWithAnimation;
begin
  FClosed := False;
  faOpenClose.Stop;
  faOpenClose.StartValue := 1;
  faOpenClose.StopValue := 0;
  faOpenClose.Start;
end;

 

Ну и теперь, мы можем реализовать метод ScaleControls, который в прошлой заметке остался не реализованным. Выглядит он у меня так:

procedure TframeRobot.ScaleControls;
  procedure ScalePositions;
  begin
    // устанавливаем расположение контролов в зависимости от текущего состояния
    // (процедура анимации учтёт ScaleFactor)
    if FClosed then
      ProcessOpenCloseAnimation(1)
    else
      ProcessOpenCloseAnimation(0)
  end;

  procedure ScaleStrokeThickness;
  var
    i: Integer;
  begin
    for i := 1 to Length(Controls) - 1 do
      if Controls[i] is TShape then
        if TShape(Controls[i]).Stroke.Kind = TBrushKind.bkSolid then
          TShape(Controls[i]).Stroke.Thickness := 12 * ScaleFactor;
  end;

  procedure RecalcBodyRadius;
  begin
    Body.XRadius := (LegLeft.LocalRect.Width - LegLeft.Stroke.Thickness) / 2;
    Body.YRadius := Body.XRadius;
  end;

  procedure RecalcAntenaRadius;
  begin
    AntenaLeft.XRadius := (AntenaLeft.Width - AntenaLeft.Stroke.Thickness) / 2;
    AntenaLeft.YRadius := AntenaLeft.XRadius;
    AntenaRight.XRadius := AntenaLeft.XRadius;
    AntenaRight.YRadius := AntenaLeft.XRadius;
  end;

begin
  ScalePositions;
  ScaleStrokeThickness;
  RecalcBodyRadius;
  RecalcAntenaRadius;
end;

 

Заметка получилась больше, чем я предполагал, поэтому закругляюсь. Напоследок скажу, что кроме анимации по смене состояния, у меня будет ещё как минимум одна анимация. Анимация-приветствие. Всё вместе выглядит так:

TapTap

Читать на сайте автора.

Delphi плюс Android? Есть идея! Tap#3

Эта заметка является продолжением предыдущих. Tap#1, Tap#2.

И прежде чем приступить к анимации, стоит определиться с масштабированием персонажа. То есть необходимо настроить фрейм таким образом, чтобы, задавая его размеры “сверху”, все объекты персонажа автоматически подгоняли свой размер, и персонаж (по своим внешним размерам) оказался вписанным во фрейм.

Сделать это можно очень просто. Для самой фреймы устанавливаем свойство Align = alFit (чтобы сохранить пропорцию у соотношения сторон фреймы), а всем контролам на фрейме –  Align = alScale.  Тогда, при изменении размеров фреймы, контролы будут масштабироваться автоматически. Оговорюсь, что размеры и положения контролов в FM задаются вещественными числами, поэтому масштабирование будет плавным.

Однако есть такой нюанс: платформа FM масштабирует контролы относительно их текущих размеров. Что это значит? А это значит, что при задании маленьких размеров фреймы, наш персонаж может оказаться очень маленьким. Настолько маленьким, что при последующем увеличении фреймы персонаж перестанет масштабироваться.

Правда если рассчитывать только на мобильное устройство, в котором размер экрана фиксирован и приложение всегда развёрнуто на весь экран, то можно и не беспокоиться. Автоматическое масштабирование нам подойдёт, т.к. оно будет происходить:

  • один раз при запуске приложения;
  • при повороте экрана.

Однако я хочу, чтобы приложение можно было так же запускать и в окне в Windows. А в оконном режиме существует ненулевая вероятность того, что пользователь захочет “поиграться” с размерами окна и уменьшит его до очень маленького размера…

Поэтому масштабирование всех контролов я буду делать вручную, в обработчике OnResize. При этом, я хочу ориентироваться на исходный размер контролов, т.е. после загрузки фреймы из fmx-ресурса, необходимо сохранить позиции и размеры контролов.

Для этого создаём простую запись:

type
  TDesignPosition = record
    Left, Top, Width, Height: Single;
    RotationAngle: Single;
    FontSize: Single;
  end;

И класс-наследник от TList<T>:

type
  TDesignPositions = class(TList<TDesignPosition>)
  private
    function IndexByControl(AControl: TControl): Integer;
    function GetItemByControl(AControl: TControl): TDesignPosition;
  public
    procedure AddByControl(AControl: TControl);
    property ItemByControl[AControl: TControl]: TDesignPosition read GetItemByControl; default;
  end;

{ TDesignPositionsList }

function TDesignPositions.IndexByControl(AControl: TControl): Integer;
begin
  Result := AControl.Tag;
end;

function TDesignPositions.GetItemByControl(AControl: TControl): TDesignPosition;
begin
  Result := Items[IndexByCOntrol(AControl)];
end;

procedure TDesignPositions.AddByControl(AControl: TControl);
var
  DP: TDesignPosition;
begin
  DP.Left := AControl.Position.X;
  DP.Top := AControl.Position.Y;
  DP.Width := AControl.Width;
  DP.Height := AControl.Height;
  DP.RotationAngle := AControl.RotationAngle;
  if AControl is TTextControl then
    DP.FontSize := TTextControl(AControl).Font.Size;
  Add(DP);
end;

Тут TDesignPositions – это простой список записей типа TDesignPosition. Так как я не планирую в Run-Time добавлять или удалять контролы, то (для простоты) соответствие между элементом в списке и контролом задаётся свойством TControl.Tag.

Заполняется список так:

procedure TNPC.SavePositionsTo(AList: TDesignPositions);
var
  i: Integer;
begin
  AList.Clear;
  i := 0;
  ForEach(
    procedure (AControl: TControl)
    begin
      AControl.Tag := i;
      Inc(i);
      AList.AddByControl(AControl);
    end
  );
end;

Здесь TNPC – это базовый фрейм для персонажа, от него будут наследоваться все персонажи в будущем. ForEach – это специальный метод, который вызывает переданную анонимную функцию и перебирает все контролы фреймы. Метод я объявил в специальном хелпере, т.к. планирую его использовать ещё не один раз. (Идею с хелпером я подсмотрел у Bioan Mitov.)

type
  TControlHelper = class helper for TControl
  public
    procedure ForEach(AProc: TProc<TControl>);
  end;

{ TControlHelper }

procedure TControlHelper.ForEach(AProc: TProc<TControl>);
var
  i: Integer;
begin
  AProc(Self);
  for i := 0 to ControlsCount - 1 do
    Controls[i].ForEach(AProc);
end;

Т.к. мы контролы вкладывали друг в друга, то здесь используется рекурсия. Метод ForEach сначала вызывает указанную процедуру, а потом перебирает дочерние контролы. Таким образом, в список TDesignPositions сначала добавляются размеры самой фреймы, а потом уже размеры её контролов. Я этим воспользуюсь, чтобы определить коэффициент масштабирования:

procedure TNPC.RecalcScaleFactor;
var
  dw, dh: Single;
begin
  dw := Width / FDesignPositions.Items[0].Width;
  dh := Height / FDesignPositions.Items[0].Height;
  if dw < dh then
    FScaleFactor := dw
  else
    FScaleFactor := dh;
end;

На текущий момент базовый фрейм для персонажа у меня выглядит примерно так:

  TNPC = class(TFrame)
  private
    FControls: TControls;
    FDesignPositions: TDesignPositions;
    FScaleFactor: Single;
    FIsLoaded: Boolean;
  protected
    procedure Loaded; override;
    procedure Resize; override;
    procedure SaveDesignPositions; virtual;
    procedure SaveControls;
    procedure ScaleControls; virtual;
  public
    procedure SavePositionsTo(AList: TDesignPositions);
    property IsLoaded: Boolean read FIsLoaded;
    property ScaleFactor: Single read FScaleFactor;
    // Controls - простой массив для быстрого доступа ко всем контролам на фрейме
    //  нулейвой элемент соответствует самой фрейме
    //  у каждого контрола свойство Tag соответствует индексу в этом массиве
    property Controls: TControls read FControls;
    // DesignPositions - координаты и размеры всех контролов, заданных в дизайнере
    //  нулейвой элемент соответствует самой фрейме
    property DesignPositions: TDesignPositions read FDesignPositions;
  end;
..
procedure TNPC.Loaded;
begin
  inherited;
  SaveDesignPositions;
  SaveControls;
  FIsLoaded := True;
end;

procedure TNPC.SaveDesignPositions;
begin
  FDesignPositions := TDesignPositions.Create;
  SavePositionsTo(FDesignPositions);
end;

procedure TNPC.SaveControls;
var
  i: Integer;
begin
  SetLength(FControls, DesignPositions.Count);
  i := 0;
  ForEach(
    procedure (AControl: TControl)
    begin
      FControls[i] := AControl;
    end
  );
end;

procedure TNPC.Resize;
begin
  inherited;

  if IsLoaded then
  begin
    BeginUpdate;
    try
      RecalcScaleFactor;
      ScaleControls;
    finally
      EndUpdate;
    end;
  end;
end;

procedure TNPC.ScaleControls;
begin
  // do nothing
end;

Самый интересный метод – ScaleControls, он отвечает за масштабирование. Как раз здесь можно использовать DesignPositions и ScaleFactor. Но на уровне базовой фреймы я решил, что он будет пустым, т.к. у персонажа будет ещё анимация и будут внутренние состояния. И именно от текущего состояния будут рассчитываться положения объектов. Другими словами, свойствами DesignPositions и ScaleFactor мы будем активно пользоваться в наследниках.

А в следующий раз уже поговорим об анимации.

UPD. Для удобства я сделал ещё такую вещь: все контролы с фреймы сохранил в линейный массив Controls.

Читать на сайте автора.