Advertisement
deced

Untitled

Mar 29th, 2021
128
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.20 KB | None | 0 0
  1. unit Main;
  2.  
  3. interface
  4.  
  5. uses
  6. Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  7. System.Generics.Collections,
  8. System.Classes, Vcl.Graphics,
  9. Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Grids, Vcl.ExtCtrls,
  10. Node,
  11. Tree, Vcl.Menus, StrUtils;
  12.  
  13. type
  14. TMainForm = class(TForm)
  15. AddButton: TButton;
  16. AddEdit: TEdit;
  17. Image: TImage;
  18. Label1: TLabel;
  19. MainMenu1: TMainMenu;
  20. FileMenu: TMenuItem;
  21. OpenMenu: TMenuItem;
  22. SaveMenu: TMenuItem;
  23. AboutMenu: TMenuItem;
  24. HeadLabel: TLabel;
  25. OutputLabel: TLabel;
  26. OpenDialog: TOpenDialog;
  27. SaveDialog: TSaveDialog;
  28. Label2: TLabel;
  29. Label3: TLabel;
  30. DeleteButton: TButton;
  31. DeleteEdit: TEdit;
  32. procedure GetChildsCount(Current: TNode; var Count: Integer);
  33. procedure AddButtonClick(Sender: TObject);
  34. procedure FormCreate(Sender: TObject);
  35. procedure PrintTree(Current: TNode; Middle, Width, Height: Integer);
  36. procedure DrawRoot(var Root: TNode);
  37. procedure PrintDifferentNodes(Current: TNode);
  38. procedure OpenMenuClick(Sender: TObject);
  39. procedure GetTreeFromFile(Path: String);
  40. function CheckInput(Input: String): Boolean;
  41. procedure SaveMenuClick(Sender: TObject);
  42. procedure FileMenuClick(Sender: TObject);
  43. procedure AddEditKeyPress(Sender: TObject; var Key: Char);
  44. procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  45. procedure DeleteEditKeyPress(Sender: TObject; var Key: Char);
  46. procedure DeleteButtonClick(Sender: TObject);
  47.  
  48. private
  49. { Private declarations }
  50. public
  51. { Public declarations }
  52. end;
  53.  
  54. procedure SaveToFile(Path, Text: String); external 'SaveToFileLibrary.dll';
  55.  
  56. var
  57. MainForm: TMainForm;
  58. Tree: TTree;
  59. Centre: Integer;
  60. NodeIndex: Integer;
  61. // отступ при печати между значением ноды и её номером
  62. NumPadding: Integer = 20;
  63.  
  64. implementation
  65.  
  66. {$R *.dfm}
  67.  
  68. // эта процедура печатает номера узлов, у которых на единицу отличается число потоков
  69. // в поддеревьях
  70. procedure TMainForm.PrintDifferentNodes(Current: TNode);
  71. var
  72. LeftCount, RightCount: Integer;
  73. begin
  74.  
  75. if (Current.Left <> nil) then
  76. begin
  77. // существует левая нода - значит в левом поддереве как минимум один элемент
  78. LeftCount := 1;
  79. // вызываем процедуру, которая посчитает все
  80. // дочерние элементы левого дочернего элемента
  81. // тут LeftCount передаётся по ссылке, поэтому если оно изменится внутри
  82. // процедуры GetChildsCount, то и здесь оно тоже изменится
  83. GetChildsCount(Current.Left, LeftCount)
  84. end
  85. else
  86. // если левой ноды не существует, то число элементов в этом поддереве
  87. // равно 0
  88. LeftCount := 0;
  89. if (Current.Right <> nil) then
  90. begin
  91. // существует правая нода - значит в правом поддереве как минимум один элемент
  92. RightCount := 1;
  93. // вызываем процедуру, которая посчитает все
  94. // дочерние элементы правого дочернего элемента
  95. // тут LeftCount передаётся по ссылке, поэтому если оно изменится внутри
  96. // процедуры GetChildsCount, то и здесь оно тоже изменится
  97. GetChildsCount(Current.Right, RightCount);
  98. end
  99. else
  100. // если право ноды не существует, то число элементов в этом поддереве
  101. // равно 0
  102. RightCount := 0;
  103. // если полученные значения отличаются на единицу, то дописываем в лейбл
  104. // переменную NodeIndex, в которой у нас номер текущей ноды
  105. if Abs(LeftCount - RightCount) = 1 then
  106. OutputLabel.Caption := OutputLabel.Caption + ' (' +
  107. IntToStr(NodeIndex) + ')';
  108.  
  109. end;
  110.  
  111. procedure TMainForm.PrintTree(Current: TNode; Middle, Width, Height: Integer);
  112. begin
  113. // cразу проверяем разность кол-ва нод в поддеревьях и печатаем, если нужно
  114. PrintDifferentNodes(Current);
  115. if Current.Left <> nil then
  116. // если левая дочерняя нода существует
  117. begin
  118. // увеличиваем номер ноды на 1
  119. Inc(NodeIndex);
  120. // переходим к координатам, указанным в скобках
  121. Image.Canvas.MoveTo(Middle, Height - 40);
  122. // от координат, указанных выше рисуем прямую к координатам, указанным ниже
  123. Image.Canvas.LineTo(Middle - Width, Height);
  124. // пишем значение левой дочерней ноды
  125. Image.Canvas.TextOut(Middle - Width, Height - 20,
  126. IntToStr(Current.Left.Value));
  127. // печатаем номер ноды
  128. Image.Canvas.TextOut(Middle - Width + NumPadding, Height - 20,
  129. '(' + IntToStr(NodeIndex) + ')');
  130. Centre := Middle - Width;
  131. // вызываем отрисовку для левой дочерней ноды на 40 пикселей ниже
  132. // по оси Х отступ будем делать в два раза меньше (из-за этого делаем Width div 2)
  133. PrintTree(Current.Left, Centre, Width div 2, Height + 40);
  134. end;
  135. if Current.Right <> nil then
  136. // если правая дочерняя нода сущетсвует
  137. begin
  138. // увеличиваем номер ноды на 1
  139. Inc(NodeIndex);
  140. // переходим к координатам, указанным в скобках
  141. Image.Canvas.MoveTo(Middle, Height - 40);
  142. // от координат, указанных выше рисуем прямую к координатам, указанным ниже
  143. Image.Canvas.LineTo(Middle + Width, Height);
  144. // пишем значение правой дочерней ноды
  145. Image.Canvas.TextOut(Width + Middle, Height - 20,
  146. IntToStr(Current.Right.Value));
  147. // печатаем номер ноды
  148. Image.Canvas.TextOut(Width + Middle + NumPadding, Height - 20,
  149. '(' + IntToStr(NodeIndex) + ')');
  150.  
  151. Centre := Middle + Width;
  152. // вызываем отрисовку для правой дочерней ноды, на 40 пискелей ниже
  153. // по оси Х отступ будем делать в два раза меньше (из-за этого делаем Width div 2)
  154. PrintTree(Current.Right, Centre, Width div 2, Height + 40);
  155. end;
  156. end;
  157.  
  158. procedure TMainForm.DrawRoot(var Root: TNode);
  159. var
  160. Middle, Width, Height: Integer;
  161. begin
  162. Image.Canvas.FillRect(Image.Canvas.ClipRect);
  163. // координаты центра по оси X - это размер элеметна TImage/2
  164. Centre := Image.Width div 2;
  165. Width := Centre;
  166. // начинаем рисовать с 40 пикселя по оси Y (точка (0;0) в верхнем левом углу)
  167. Height := 20;
  168. // первая нода имеет номер 1 :)
  169. NodeIndex := 1;
  170. // пишем значение первой ноды (корня по указанным координатам)
  171. Image.Canvas.TextOut(Width, Height, IntToStr(Root.Value));
  172. // также пишем её номер
  173. Image.Canvas.TextOut(Width + NumPadding, Height,
  174. '(' + IntToStr(NodeIndex) + ')');
  175. // вызываем отрисовку дочерних нод, на 60 пикселей ниже
  176. PrintTree(Root, Centre, Width div 2, Height + 60);
  177. end;
  178.  
  179. // процедура вычисляет количество дочерних нод
  180. // количество будет хранится во втором параметре Count
  181. // он передается по ссылке, так как записан с var
  182. procedure TMainForm.GetChildsCount(Current: TNode; var Count: Integer);
  183. begin
  184. // если существует левая нода
  185. if Current.Left <> nil then
  186. begin
  187. // то увеличиваем количество дочерних нод на 1
  188. Inc(Count);
  189. // и рекурсивно проверяем количество дочерних у левой ноды
  190. GetChildsCount(Current.Left, Count);
  191. end;
  192. // если существует правая нода
  193. if (Current.Right <> nil) then
  194. begin
  195. // то увеличиваем количество дочерних нод на 1
  196. Inc(Count);
  197. // и рекурсивно проверяем количество дочерних у правой ноды
  198. GetChildsCount(Current.Right, Count);
  199. end;
  200. end;
  201.  
  202. procedure TMainForm.GetTreeFromFile(Path: String);
  203. var
  204. InputFile: TextFile;
  205. Line: String;
  206. StrArr: TArray<String>;
  207. I: Integer;
  208. begin
  209. try
  210. // перед открытием файла очистим дерево
  211. Tree.Clear();
  212. // привязываем файл
  213. AssignFile(InputFile, Path);
  214. Reset(InputFile);
  215. // читаем строку из файла
  216. Readln(InputFile, Line);
  217. // разбиваем эту строку на несколько строк (запятая будет разделителем)
  218. StrArr := SplitString(Line, ',');
  219. for I := 0 to High(StrArr) do
  220. // поочередно добавляем все элементы в дерево, предварительно конвертируя их в число
  221. Tree.Add(StrToInt(StrArr[I]));
  222. except
  223. // если вдруг что-то сломалось, то выводим сообщение об ошибке
  224. MessageBox(Application.Handle, 'Произошла ошибка при чтении файла' +
  225. #13#10'Файл должен содержать числа, разделённые запятыми',
  226. 'Ошибка', MB_OK);
  227. // и удаляем то, что уже успели добавить в дерево
  228. Tree.Clear();
  229. end;
  230. end;
  231.  
  232. procedure TMainForm.SaveMenuClick(Sender: TObject);
  233. begin
  234. if SaveDialog.Execute then
  235. begin
  236. SaveToFile(SaveDialog.FileName, HeadLabel.Caption + #13#10 +
  237. OutputLabel.Caption);
  238. MessageBox(Application.Handle, 'Файл успешно сохранён',
  239. 'Подтверждение', MB_OK);
  240. end;
  241.  
  242. end;
  243.  
  244. procedure TMainForm.OpenMenuClick(Sender: TObject);
  245. var
  246. Width, Height: Integer;
  247. begin
  248. if OpenDialog.Execute then
  249. begin
  250. // читаем дерево из файла
  251. GetTreeFromFile(OpenDialog.FileName);
  252. // если корень не равен nil, значит дерево прочиталось успешно и надо
  253. // его нарисовать
  254. if Tree.RootNode <> nil then
  255. DrawRoot(Tree.RootNode);
  256. end;
  257. end;
  258.  
  259. procedure TMainForm.AddEditKeyPress(Sender: TObject; var Key: Char);
  260. begin
  261. if ((Length(AddEdit.Text) > 2) or not(Key in ['0' .. '9', '-'])) and
  262. (Key <> #8) then
  263. Key := #0;
  264.  
  265. end;
  266.  
  267. procedure TMainForm.DeleteEditKeyPress(Sender: TObject; var Key: Char);
  268. begin
  269. if ((Length(DeleteEdit.Text) > 2) or not(Key in ['0' .. '9', '-'])) and
  270. (Key <> #8) then
  271. Key := #0;
  272. end;
  273.  
  274. function TMainForm.CheckInput(Input: String): Boolean;
  275. begin
  276. try
  277. // пытаемся конвертировать ввод к числу, если не получится то будет исключение
  278. // и функция проверки вернёт false
  279. StrToInt(AddEdit.Text);
  280. Result := True;
  281. except
  282. Result := False;
  283. end;
  284. end;
  285.  
  286. procedure TMainForm.AddButtonClick(Sender: TObject);
  287. begin
  288. // проверяем ввод
  289. if CheckInput(AddEdit.Text) then
  290. begin
  291. // добавляем элемент в дерево через ранее написанный метод Add
  292. // в скобках указываем значение, которое хотим добавить
  293. Tree.Add(StrToInt(AddEdit.Text));
  294. OutputLabel.Caption := '';
  295. // вызываем процедуру визуализации начиная от корня
  296. DrawRoot(Tree.RootNode);
  297. end
  298. else
  299. MessageBox(Application.Handle,
  300. 'Элемент дерева может содержать только число', 'Ошибка', MB_OK);
  301.  
  302. end;
  303.  
  304. procedure TMainForm.DeleteButtonClick(Sender: TObject);
  305. begin
  306. // проверяем ввод
  307. if CheckInput(DeleteEdit.Text) then
  308. begin
  309. // добавляем элемент в дерево через ранее написанный метод Add
  310. // в скобках указываем значение, которое хотим добавить
  311. Tree.Delete(StrToInt(DeleteEdit.Text));
  312. OutputLabel.Caption := '';
  313. // вызываем процедуру визуализации начиная от корня
  314. DrawRoot(Tree.RootNode);
  315. end
  316. else
  317. MessageBox(Application.Handle,
  318. 'Элемент дерева может содержать только число', 'Ошибка', MB_OK);
  319. end;
  320.  
  321. procedure TMainForm.FileMenuClick(Sender: TObject);
  322. begin
  323. // по нажатию кнопки "файл" определяем, будет ли активна кнопка сохранения
  324. // если дерево пустое, то сохранять ничего не надо
  325. SaveMenu.Enabled := Tree.RootNode <> nil;
  326. end;
  327.  
  328. procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  329. begin
  330. CanClose := MessageDlg('Вы уверены, что хотите выйти из программы?' + #10#13
  331. + 'Все несохранённые данные будут утеряны.', mtConfirmation,
  332. [mbYes, mbNo], 0) = mrYes;
  333. end;
  334.  
  335. procedure TMainForm.FormCreate(Sender: TObject);
  336. begin
  337. // при старте формы создаем наше дерево
  338. // в переменной Tree будет наше дерево
  339. Tree := TTree.Create;
  340. // настройки цвета, шрифта, и всякого другого
  341. Image.Canvas.Pen.Style := psDashDotDot;
  342. Image.Canvas.Pen.Color := clBlack;
  343. Image.Canvas.Pen.Width := 2;
  344. Image.Canvas.Font.Size := 10;
  345. Image.Canvas.Font.Color := clFuchsia; // почти розовый
  346. end;
  347.  
  348. end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement