Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- unit Main;
- interface
- uses
- Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
- System.Generics.Collections,
- System.Classes, Vcl.Graphics,
- Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Grids, Vcl.ExtCtrls,
- Node,
- Tree, Vcl.Menus, StrUtils;
- type
- TMainForm = class(TForm)
- AddButton: TButton;
- AddEdit: TEdit;
- Image: TImage;
- Label1: TLabel;
- MainMenu1: TMainMenu;
- FileMenu: TMenuItem;
- OpenMenu: TMenuItem;
- SaveMenu: TMenuItem;
- AboutMenu: TMenuItem;
- HeadLabel: TLabel;
- OutputLabel: TLabel;
- OpenDialog: TOpenDialog;
- SaveDialog: TSaveDialog;
- Label2: TLabel;
- Label3: TLabel;
- DeleteButton: TButton;
- DeleteEdit: TEdit;
- procedure GetChildsCount(Current: TNode; var Count: Integer);
- procedure AddButtonClick(Sender: TObject);
- procedure FormCreate(Sender: TObject);
- procedure PrintTree(Current: TNode; Middle, Width, Height: Integer);
- procedure DrawRoot(var Root: TNode);
- procedure PrintDifferentNodes(Current: TNode);
- procedure OpenMenuClick(Sender: TObject);
- procedure GetTreeFromFile(Path: String);
- function CheckInput(Input: String): Boolean;
- procedure SaveMenuClick(Sender: TObject);
- procedure FileMenuClick(Sender: TObject);
- procedure AddEditKeyPress(Sender: TObject; var Key: Char);
- procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
- procedure DeleteEditKeyPress(Sender: TObject; var Key: Char);
- procedure DeleteButtonClick(Sender: TObject);
- private
- { Private declarations }
- public
- { Public declarations }
- end;
- procedure SaveToFile(Path, Text: String); external 'SaveToFileLibrary.dll';
- var
- MainForm: TMainForm;
- Tree: TTree;
- Centre: Integer;
- NodeIndex: Integer;
- // отступ при печати между значением ноды и её номером
- NumPadding: Integer = 20;
- implementation
- {$R *.dfm}
- // эта процедура печатает номера узлов, у которых на единицу отличается число потоков
- // в поддеревьях
- procedure TMainForm.PrintDifferentNodes(Current: TNode);
- var
- LeftCount, RightCount: Integer;
- begin
- if (Current.Left <> nil) then
- begin
- // существует левая нода - значит в левом поддереве как минимум один элемент
- LeftCount := 1;
- // вызываем процедуру, которая посчитает все
- // дочерние элементы левого дочернего элемента
- // тут LeftCount передаётся по ссылке, поэтому если оно изменится внутри
- // процедуры GetChildsCount, то и здесь оно тоже изменится
- GetChildsCount(Current.Left, LeftCount)
- end
- else
- // если левой ноды не существует, то число элементов в этом поддереве
- // равно 0
- LeftCount := 0;
- if (Current.Right <> nil) then
- begin
- // существует правая нода - значит в правом поддереве как минимум один элемент
- RightCount := 1;
- // вызываем процедуру, которая посчитает все
- // дочерние элементы правого дочернего элемента
- // тут LeftCount передаётся по ссылке, поэтому если оно изменится внутри
- // процедуры GetChildsCount, то и здесь оно тоже изменится
- GetChildsCount(Current.Right, RightCount);
- end
- else
- // если право ноды не существует, то число элементов в этом поддереве
- // равно 0
- RightCount := 0;
- // если полученные значения отличаются на единицу, то дописываем в лейбл
- // переменную NodeIndex, в которой у нас номер текущей ноды
- if Abs(LeftCount - RightCount) = 1 then
- OutputLabel.Caption := OutputLabel.Caption + ' (' +
- IntToStr(NodeIndex) + ')';
- end;
- procedure TMainForm.PrintTree(Current: TNode; Middle, Width, Height: Integer);
- begin
- // cразу проверяем разность кол-ва нод в поддеревьях и печатаем, если нужно
- PrintDifferentNodes(Current);
- if Current.Left <> nil then
- // если левая дочерняя нода существует
- begin
- // увеличиваем номер ноды на 1
- Inc(NodeIndex);
- // переходим к координатам, указанным в скобках
- Image.Canvas.MoveTo(Middle, Height - 40);
- // от координат, указанных выше рисуем прямую к координатам, указанным ниже
- Image.Canvas.LineTo(Middle - Width, Height);
- // пишем значение левой дочерней ноды
- Image.Canvas.TextOut(Middle - Width, Height - 20,
- IntToStr(Current.Left.Value));
- // печатаем номер ноды
- Image.Canvas.TextOut(Middle - Width + NumPadding, Height - 20,
- '(' + IntToStr(NodeIndex) + ')');
- Centre := Middle - Width;
- // вызываем отрисовку для левой дочерней ноды на 40 пикселей ниже
- // по оси Х отступ будем делать в два раза меньше (из-за этого делаем Width div 2)
- PrintTree(Current.Left, Centre, Width div 2, Height + 40);
- end;
- if Current.Right <> nil then
- // если правая дочерняя нода сущетсвует
- begin
- // увеличиваем номер ноды на 1
- Inc(NodeIndex);
- // переходим к координатам, указанным в скобках
- Image.Canvas.MoveTo(Middle, Height - 40);
- // от координат, указанных выше рисуем прямую к координатам, указанным ниже
- Image.Canvas.LineTo(Middle + Width, Height);
- // пишем значение правой дочерней ноды
- Image.Canvas.TextOut(Width + Middle, Height - 20,
- IntToStr(Current.Right.Value));
- // печатаем номер ноды
- Image.Canvas.TextOut(Width + Middle + NumPadding, Height - 20,
- '(' + IntToStr(NodeIndex) + ')');
- Centre := Middle + Width;
- // вызываем отрисовку для правой дочерней ноды, на 40 пискелей ниже
- // по оси Х отступ будем делать в два раза меньше (из-за этого делаем Width div 2)
- PrintTree(Current.Right, Centre, Width div 2, Height + 40);
- end;
- end;
- procedure TMainForm.DrawRoot(var Root: TNode);
- var
- Middle, Width, Height: Integer;
- begin
- Image.Canvas.FillRect(Image.Canvas.ClipRect);
- // координаты центра по оси X - это размер элеметна TImage/2
- Centre := Image.Width div 2;
- Width := Centre;
- // начинаем рисовать с 40 пикселя по оси Y (точка (0;0) в верхнем левом углу)
- Height := 20;
- // первая нода имеет номер 1 :)
- NodeIndex := 1;
- // пишем значение первой ноды (корня по указанным координатам)
- Image.Canvas.TextOut(Width, Height, IntToStr(Root.Value));
- // также пишем её номер
- Image.Canvas.TextOut(Width + NumPadding, Height,
- '(' + IntToStr(NodeIndex) + ')');
- // вызываем отрисовку дочерних нод, на 60 пикселей ниже
- PrintTree(Root, Centre, Width div 2, Height + 60);
- end;
- // процедура вычисляет количество дочерних нод
- // количество будет хранится во втором параметре Count
- // он передается по ссылке, так как записан с var
- procedure TMainForm.GetChildsCount(Current: TNode; var Count: Integer);
- begin
- // если существует левая нода
- if Current.Left <> nil then
- begin
- // то увеличиваем количество дочерних нод на 1
- Inc(Count);
- // и рекурсивно проверяем количество дочерних у левой ноды
- GetChildsCount(Current.Left, Count);
- end;
- // если существует правая нода
- if (Current.Right <> nil) then
- begin
- // то увеличиваем количество дочерних нод на 1
- Inc(Count);
- // и рекурсивно проверяем количество дочерних у правой ноды
- GetChildsCount(Current.Right, Count);
- end;
- end;
- procedure TMainForm.GetTreeFromFile(Path: String);
- var
- InputFile: TextFile;
- Line: String;
- StrArr: TArray<String>;
- I: Integer;
- begin
- try
- // перед открытием файла очистим дерево
- Tree.Clear();
- // привязываем файл
- AssignFile(InputFile, Path);
- Reset(InputFile);
- // читаем строку из файла
- Readln(InputFile, Line);
- // разбиваем эту строку на несколько строк (запятая будет разделителем)
- StrArr := SplitString(Line, ',');
- for I := 0 to High(StrArr) do
- // поочередно добавляем все элементы в дерево, предварительно конвертируя их в число
- Tree.Add(StrToInt(StrArr[I]));
- except
- // если вдруг что-то сломалось, то выводим сообщение об ошибке
- MessageBox(Application.Handle, 'Произошла ошибка при чтении файла' +
- #13#10'Файл должен содержать числа, разделённые запятыми',
- 'Ошибка', MB_OK);
- // и удаляем то, что уже успели добавить в дерево
- Tree.Clear();
- end;
- end;
- procedure TMainForm.SaveMenuClick(Sender: TObject);
- begin
- if SaveDialog.Execute then
- begin
- SaveToFile(SaveDialog.FileName, HeadLabel.Caption + #13#10 +
- OutputLabel.Caption);
- MessageBox(Application.Handle, 'Файл успешно сохранён',
- 'Подтверждение', MB_OK);
- end;
- end;
- procedure TMainForm.OpenMenuClick(Sender: TObject);
- var
- Width, Height: Integer;
- begin
- if OpenDialog.Execute then
- begin
- // читаем дерево из файла
- GetTreeFromFile(OpenDialog.FileName);
- // если корень не равен nil, значит дерево прочиталось успешно и надо
- // его нарисовать
- if Tree.RootNode <> nil then
- DrawRoot(Tree.RootNode);
- end;
- end;
- procedure TMainForm.AddEditKeyPress(Sender: TObject; var Key: Char);
- begin
- if ((Length(AddEdit.Text) > 2) or not(Key in ['0' .. '9', '-'])) and
- (Key <> #8) then
- Key := #0;
- end;
- procedure TMainForm.DeleteEditKeyPress(Sender: TObject; var Key: Char);
- begin
- if ((Length(DeleteEdit.Text) > 2) or not(Key in ['0' .. '9', '-'])) and
- (Key <> #8) then
- Key := #0;
- end;
- function TMainForm.CheckInput(Input: String): Boolean;
- begin
- try
- // пытаемся конвертировать ввод к числу, если не получится то будет исключение
- // и функция проверки вернёт false
- StrToInt(AddEdit.Text);
- Result := True;
- except
- Result := False;
- end;
- end;
- procedure TMainForm.AddButtonClick(Sender: TObject);
- begin
- // проверяем ввод
- if CheckInput(AddEdit.Text) then
- begin
- // добавляем элемент в дерево через ранее написанный метод Add
- // в скобках указываем значение, которое хотим добавить
- Tree.Add(StrToInt(AddEdit.Text));
- OutputLabel.Caption := '';
- // вызываем процедуру визуализации начиная от корня
- DrawRoot(Tree.RootNode);
- end
- else
- MessageBox(Application.Handle,
- 'Элемент дерева может содержать только число', 'Ошибка', MB_OK);
- end;
- procedure TMainForm.DeleteButtonClick(Sender: TObject);
- begin
- // проверяем ввод
- if CheckInput(DeleteEdit.Text) then
- begin
- // добавляем элемент в дерево через ранее написанный метод Add
- // в скобках указываем значение, которое хотим добавить
- Tree.Delete(StrToInt(DeleteEdit.Text));
- OutputLabel.Caption := '';
- // вызываем процедуру визуализации начиная от корня
- DrawRoot(Tree.RootNode);
- end
- else
- MessageBox(Application.Handle,
- 'Элемент дерева может содержать только число', 'Ошибка', MB_OK);
- end;
- procedure TMainForm.FileMenuClick(Sender: TObject);
- begin
- // по нажатию кнопки "файл" определяем, будет ли активна кнопка сохранения
- // если дерево пустое, то сохранять ничего не надо
- SaveMenu.Enabled := Tree.RootNode <> nil;
- end;
- procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
- begin
- CanClose := MessageDlg('Вы уверены, что хотите выйти из программы?' + #10#13
- + 'Все несохранённые данные будут утеряны.', mtConfirmation,
- [mbYes, mbNo], 0) = mrYes;
- end;
- procedure TMainForm.FormCreate(Sender: TObject);
- begin
- // при старте формы создаем наше дерево
- // в переменной Tree будет наше дерево
- Tree := TTree.Create;
- // настройки цвета, шрифта, и всякого другого
- Image.Canvas.Pen.Style := psDashDotDot;
- Image.Canvas.Pen.Color := clBlack;
- Image.Canvas.Pen.Width := 2;
- Image.Canvas.Font.Size := 10;
- Image.Canvas.Font.Color := clFuchsia; // почти розовый
- end;
- end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement