Динамическое создание компонентов

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Динамическое создание компонентов

Сообщение kuzmich92 » 23.02.2011 00:21:37

всем привет. Прошу помощи в решении задачи.
Задача такова - есть форма form1 на ней компонент TPageControl1 , tabsheet1, на tabsheet tmemo.
нужно динамически создать вкладку с тмемо по нажатию на кнопку и по двойному клику по tabsheet1 закрыть ту вкладку по которой кликнули.
Вкладки я создаю так

Код: Выделить всё
tab:=TTabSheet.Create(self);
  tab.PageControl:=PageControl1;
  tab.Caption:='Новая  вкладка '+IntToStr(PageControl1.PageCount);
  tab.PageControl.ActivePage:=tab;
  memo:=TMemo.Create(self);
  memo.Parent:=Tab;
  memo.Align:=alClient;

Но как сделать что бы вкладки закрывались ума приложить не могу.
И еще, если я создал таб больше 1 штуки, то как программа может узнать какой таб закрывать, если я получается создаю компоненты с одним и темже именем!?
Заранее огромное спасибо!
kuzmich92
незнакомец
 
Сообщения: 4
Зарегистрирован: 23.02.2011 00:11:13

Re: Динамическое создание компонентов

Сообщение Maxizar » 23.02.2011 12:32:48

TPageControl имеет такое сво-во как ActivePage, скажем нам нужно его удалить, делаем так:
Код: Выделить всё
PageControl1.ActivePage.Free;

Если нужно удлаить определенную (не активную панель)
Код: Выделить всё
PageControl1.Pages[Индекс панели].Free;

Как узнать сколько сейчас панелей:
PageControl1.PageCount - и есть число панелей.
PageControl1.TabIndex:=2; - сделает активной 2 панель.
Как удалять по кнопке или через меню, ну тут уже выбор за Вами.
Если через меню, стоит обратить внимание (можете проверить в самом Lazarus) что при клике правой к мыши, изменения TabIndex не происходит, т.е если выделана панель 1 а вызвали меню на 3 и выбрали подменю закрыть, то закроем панель 1... Да это не айс, чтобы было все хорошо, нужно отловить нажатие правой к. мыши, и через координаты определить над какой панели было нажатие, выделить ее и.т.п. Можно прочитать в DRKB (вроде там было про это написано)
Maxizar
постоялец
 
Сообщения: 385
Зарегистрирован: 20.03.2010 19:48:14

Re: Динамическое создание компонентов

Сообщение kuzmich92 » 23.02.2011 17:44:44

Maxizar писал(а):Да это не айс, чтобы было все хорошо, нужно отловить нажатие правой к. мыши, и через координаты определить над какой панели было нажатие, выделить ее и.т.п. Можно прочитать в DRKB (вроде там было про это написано)

Я скачал DRKB поискал там, в инете поискал, все прелогают решение типо этого:
Код: Выделить всё
procedure TForm1.PageControl1ContextPopup(Sender: TObject;
  MousePos: TPoint; var Handled: Boolean);
begin
  PageControl1.ActivePageIndex := PageControl1.IndexOfTabAt(MousePos.X, MousePos.Y);
end;


Только вот Лазарь не знает ни что такое IndexOfTabAt ни GetHitTestInfoAt
каким еще образом отловить нажатие мыши на табе?

Добавлено спустя 2 минуты 19 секунд:
Maxizar писал(а):PageControl1.Pages[Индекс панели].Free;

Хотя я еще не научился определять индекс по клику, но заранее спрошу, а что произойдет при PageControl1.Pages[Индекс панели].Free с элементами что были на табе?
kuzmich92
незнакомец
 
Сообщения: 4
Зарегистрирован: 23.02.2011 00:11:13

Re: Динамическое создание компонентов

Сообщение Maxizar » 23.02.2011 19:13:24

Ну вот более детально:
Форма, на ней TPageControl, кнопка и меню. При нажатии кнопки добавляем новую вкладку. При нажатии меню (точнее одного из пункта меню (MenuItem1)) закрываем активную вкладку. Это меню присваиваем как всплывающее всему TPageControl-> это позволит появлятся меню при нажатии ПКМ, на язычке таба.

kuzmich92 писал(а):Только вот Лазарь не знает ни что такое IndexOfTabAt ни GetHitTestInfoAt
каким еще образом отловить нажатие мыши на табе?

Просто в Lazarus это функция имеет название TabIndexAtClientPos
Код формы:
Код: Выделить всё
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  ComCtrls, Menus;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    MenuItem1: TMenuItem;
    PageControl1: TPageControl;
    PopupMenu1: TPopupMenu;
    procedure Button1Click(Sender: TObject);
    procedure MenuItem1Click(Sender: TObject);
    procedure PageControl1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
VAR tab: TTabSheet;
    memo:TMemo;
begin
  tab:=TTabSheet.Create(self);
  tab.PageControl:=PageControl1;
  tab.Caption:='Вкладка '+IntToStr(PageControl1.PageCount);
  tab.PageControl.ActivePage:=tab;
  memo:=TMemo.Create(self);
  memo.Parent:=Tab;
  memo.Align:=alClient;

end;

procedure TForm1.MenuItem1Click(Sender: TObject);
begin
  if (PageControl1.ActivePageIndex>=0) and
     (PageControl1.ActivePageIndex<PageControl1.PageCount) then
   PageControl1.ActivePage.Free;  //Удаляем вкладку.
end;

procedure TForm1.PageControl1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var SelectPoint:TPoint;
begin
  //Смысл в том, что хотим выделить таб, при нажатии на нем правой клавишей мыши (вызов меню)
  SelectPoint.x:=X;
  SelectPoint.y:=Y;
  PageControl1.ActivePageIndex:=PageControl1.TabIndexAtClientPos(SelectPoint);
end;

end.


kuzmich92 писал(а):а что произойдет при PageControl1.Pages[Индекс панели].Free с элементами что были на табе?

Так как мы делаем вот так:
Код: Выделить всё
memo.Parent:=Tab;
.
При этом для полного счастья можно и вот это:
Код: Выделить всё
memo:=TMemo.Create(self);

заменить на:
Код: Выделить всё
memo:=TMemo.Create(tab);

По этому при уничтожении таба (родителя мемо) мемо удаляется (должно по крайней мере), для пущей надежности можно подключить проверку утечек памяти в проекте, для этого идем в меню: Проект->Параметры проекта ->Параметры компилятора->Компановка и включаем параметр: Использовать модуль Heaptrc.
Это подключает проверку на утечки. После закрытия программы, вы должны будите увидеть окно, с сообщение что освобождено столько памяти, выделено столько, если они равны, значит утечек нету. У меня их нету, значит все корректно.
Но это не работает для ваших классов, которые вы захотите связать с табом, тут Вы сами в ответе.
Удачи.
Maxizar
постоялец
 
Сообщения: 385
Зарегистрирован: 20.03.2010 19:48:14

Re: Динамическое создание компонентов

Сообщение kuzmich92 » 23.02.2011 20:19:27

Error: Identifier not found "SelectPoint"
Что то еще нужно добавить в модуль?

Добавлено спустя 19 минут 20 секунд:
Все сообразил:)))

Добавлено спустя 20 минут 52 секунды:
Поробовал делать по вашему примеру. почему то закрывает не нужную мне вкладку а последнюю активную

Добавлено спустя 8 минут 26 секунд:
Добавлю, может важно - ОС ubunto 10.04 GTK2
А можно их закрывать по нажатию closebutton на табе?
kuzmich92
незнакомец
 
Сообщения: 4
Зарегистрирован: 23.02.2011 00:11:13

Re: Динамическое создание компонентов

Сообщение WAYFARER » 23.02.2011 22:12:09

kuzmich92 писал(а):А можно их закрывать по нажатию closebutton на табе?


Код: Выделить всё
procedure Tf_main.pc_rightCloseTabClicked(Sender: TObject);
var tab: TTabSheet;
begin
   Tab := (sender as TTabSheet);
   tab.Free;
end;
Аватара пользователя
WAYFARER
энтузиаст
 
Сообщения: 518
Зарегистрирован: 09.10.2009 00:00:04
Откуда: г. Курган

Re: Динамическое создание компонентов

Сообщение Maxizar » 23.02.2011 22:16:53

kuzmich92 писал(а):А можно их закрывать по нажатию closebutton на табе?

Это будет работать только в линукс, выставлении опции (показывать кнопку закрытия на табе) в Windows вообще ничего не будет, даже кнопки не появится.
Мне кажется что я написал вариант, который должен работать и в Windows и в Linux. Исходники в архиве zip
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Maxizar
постоялец
 
Сообщения: 385
Зарегистрирован: 20.03.2010 19:48:14

Re: Динамическое создание компонентов

Сообщение WAYFARER » 23.02.2011 22:49:43

Maxizar писал(а): который должен работать и в Windows и в Linux

Ага, перетащил Ваш код к себе в проект как альтернативный вариант закрытия вкладочек - не работает. все происходит именно так как написалkuzmich92
kuzmich92 писал(а): почему то закрывает не нужную мне вкладку а последнюю активную


А вот если выбрать библиотеку виджетов win32, то все работает правильно.
Maxizar писал(а):Это будет работать только в линукс, выставлении опции (показывать кнопку закрытия на табе) в Windows вообще ничего не будет, даже кнопки не появится.

:D совсем не обязательно.
gtk2!
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Аватара пользователя
WAYFARER
энтузиаст
 
Сообщения: 518
Зарегистрирован: 09.10.2009 00:00:04
Откуда: г. Курган

Re: Динамическое создание компонентов

Сообщение kuzmich92 » 24.02.2011 00:55:11

Спасибо огромное! Видимо придется использовать оба метода, так как первый неправильно работает в Linux а второй вообще не работает в Windows, а программа должна быть кросплатформенной.

Еще вопрос, а как мне перед закрытием вкладки сохранить содержимое tmemo в файл? Т.е. я кликаю закрыть, выскакивает мессджбок с предложением сохранить текст, если я выбираю сохранить - то записывает в файл?
помогите пожалуйста!
Если бы не динамические вкладки, я бы сам сделал. А так не получается ни самому сделать, не в инете найти, а из документации только справочник по Delphi7.
kuzmich92
незнакомец
 
Сообщения: 4
Зарегистрирован: 23.02.2011 00:11:13

Re: Динамическое создание компонентов

Сообщение Maxizar » 24.02.2011 12:14:22

WAYFARER писал(а):Ага, перетащил Ваш код к себе в проект как альтернативный вариант закрытия вкладочек - не работает. все происходит именно так как написал kuzmich92

Gtk под win, ну это решать каждому. Вот так же можно использовать и Qt, но для винды надо будет таскать DLL 12 метров :(…. Опять же дело вкуса.

Это на лицо, ошибка как раз в обработке вот этого куска кода:
Код: Выделить всё
procedure TForm1.PageControl1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var SelectPoint:TPoint;
begin
  //Смысл в том, что хотим выделить таб, при нажатии на нем правой клавишей мыши (вызов меню)
  SelectPoint.x:=X;
  SelectPoint.y:=Y;
  PageControl1.ActivePageIndex:=PageControl1.TabIndexAtClientPos(SelectPoint);
end;

Именно он и делает при нажатии ПКМ, активную вкладку (должен делать) и в винде он это делает.
Тобишь в Linux а если быть точным в Linux + GTK2, функция
Код: Выделить всё
TabIndexAtClientPos(SelectPoint);

Отработала с косяками,
Мое IMHO:
Нужно как то преобразовать SelectPoint в систему координат монитора (рабочего стола) либо наоборот в систему координат формы, возможно в GTK, это дело перепутали или что то в этом роде, а может функция вообще ничего не возвращает для Linux… сейчас снес виртуалку с Linux, не могу проверить… Но налицо либо баг, либо мы передаем не те координаты, в эту функцию. Так что ждем, тех кто более опытнее, может подскажут.

kuzmich92 писал(а):Еще вопрос, а как мне перед закрытием вкладки сохранить содержимое tmemo в файл? Т.е. я кликаю закрыть, выскакивает мессджбок с предложением сохранить текст, если я выбираю сохранить - то записывает в файл?


Опять 25. У Memo есть св-во Lines а вот у нее функции: SaveToFile и LoadFromFile. Пишем и грузим из файла текст. Именно при закрытии вкладки… нужно понимать как обратится именно к тому мемо. Для этого нужно каждое мемо, как то обозвать либо, записать в таб и в мемо в сво-во Tag одно и тоже уникальное число, по которому можно будет идентифицировать и таб и его мемо, или обработать каждый WinControls через
Списко контролов.
Вот как сделал бы Я:
Код: Выделить всё
procedure TForm1.MenuItem1Click(Sender: TObject);
var I:Integer;
begin
  if (PageControl1.ActivePageIndex>=0) and
     (PageControl1.ActivePageIndex<PageControl1.PageCount) then

   for I:=0 to PageControl1.ActivePage.ControlCount-1 do
    if PageControl1.ActivePage.Controls[I] is TMemo then
        TMemo(PageControl1.ActivePage.Controls[I]).Lines.SaveToFile('C:\text.txt');
   PageControl1.ActivePage.Free;  //Удаляем вкладку.
end;


kuzmich92 писал(а):А так не получается ни самому сделать, не в инете найти, а из документации только справочник по Delphi7.

Ну все же я посоветовал бы, скачатьDRKB на сайте новая версия с Explorerom, ммм зачем? Вот 3 версия в формате chm, одним файлом мне больше нравится, а то >2000 мелких файлов этож беда.
И судя по вопросам, пускай не сразу, а постепенно, но все же прочесть вот эту книгу: Архангельский Delphi 2006, лично читал ее. По моему мнению очень достойная книга, ни в каждой найдешь столько по Delphi.
Просто со временем, Вы должны овладеть лишь знанием Языка и ООП, + Опыт. И все, не нужно заучивать, и помнить что у мемо есть то или иное свой-во или процедура, а вдруг возьмут все и переименуют, и что тогда.. а Вам будет все пофиг, вы откроете исходник посмотрите на класс и как минимум 80% должно стать ясным… ну а не тривиальные вопросы, ну для этого есть этот форум :)
Maxizar
постоялец
 
Сообщения: 385
Зарегистрирован: 20.03.2010 19:48:14

Re: Динамическое создание компонентов

Сообщение shraibikus » 30.08.2012 00:32:32

Теме внезапный ап.
Я так понимаю, что штатный TPageControl так и не научился рисовать кнопку закрытия вкладки на самом табе под Windows.
Пытался сделать это самостоятельно, и дошел до следующих изысканий:
Код: Выделить всё
TPageControl.TabRect(i);

возвращает нам нужные координаты самого таба, с отрицательными величинами, т.е. угадать куда нарисовать кнопку не сложно.
Однако, при динамическом создании кнопки, мы не можем ее рисовать за пределами области рисования.
Т.е. кнопка с отрицательными значениями Top и указанием Parent'а - текущего Page[i] рисуется, но не видима.
Если-же ее подвинуть в положительную сторону, то она на месте - т.е. видна. Но нам-то надо ее на самом табе.
Если выбрать Parent'ом кого-то другого, то кнопка вообще где угодно может оказаться, зачастую не видимая так-же (так как перекрывается другим контролом).
И как быть? Изменять AdjustClientRect не очень хочется.
Есть готовые варианты за это время, или все-же нужно пользоваться сторонними решениями?
Аватара пользователя
shraibikus
новенький
 
Сообщения: 36
Зарегистрирован: 22.09.2009 16:22:42
Откуда: Столица деревень

Re: Динамическое создание компонентов

Сообщение Alex2013 » 02.12.2021 15:33:36

Странный код сохранения и перезагрузки PageControl.
Не уверен что правильно но цикл с PageControl1.Pages[Индекс панели].Free; вылетает с ошибкой...
Код: Выделить всё
If F_Script_Ctrl Then begin
// Сохраняю  заголовки закладок
STab:=TCustomTabControl( MForm.PageControl1).Pages.Text;
TCustomTabControl( MForm.PageControl1).Pages.Clear; // Сброс
//Тут идет код создания закладок PageControl для  работы в другом режиме работы ...
//...
end else begin
// Восстанавливаю закладки.
With TStringList.Create do //Лень двигатель  прогресса :)
begin
TCustomTabControl( MForm.PageControl1).Pages.Text:= STab ;// Иначе не работает...
Text:=Stab; For I:=0 To Count-1 do
   begin
     with  TTabSheet.Create(MForm.PageControl1) do
        Caption:= Strings[I];
    end;
  Free;
end
end;

Вопрос: достаточно ли сделать TCustomTabControl( MForm.PageControl1).Pages.Clear;. или нужно очищать страницы дополнительно? (Вроде работает (контент для страниц PageControl создается отдельно и вставляется на страницу при переключение динамически ) но мучают меня смутные сомнения... )

Добавлено спустя 45 минут 59 секунд:
Помогла вот такая крякозябра...
Код: Выделить всё
While MForm.PageControl1.ActivePage<>Nil do MForm.PageControl1.ActivePage.Free;

Но всеравно не совсем понятно.
Без TCustomTabControl( MForm.PageControl1).Pages.Text:= STab ; не работает а с ним вроде на как больше ничего не нужно (хотя идее PageControl.Pages все равно пустой ).

Добавлено спустя 1 час 30 минут 30 секунд:
Вообщем так работает .
Код: Выделить всё
Const
STab:String='';
Var
S:String;
i :Integer;
ST:TStringList;
TS:TTabSheet;

procedure ClearTabList; // Очистка PageControl
Var
i:Integer;
Begin

// Убираю(точнее прячу) единственную "реальную вкладку" перед очисткой

// -------------- "в общем виде" делается иначе, но для понимания - пусть будет ----
For I:=0 to  MForm.PageControl1.PageCount-1 do
If  MForm.PageControl1.Pages[i].ControlCount>1 then
{ Именно ">1"  это не ошибка, а "фича" моей программы где первый раз добавляется заставка, но дальше она неважна   }
begin
   MForm.PageControl1.Pages[i].RemoveControl( MForm.Image2);
   MForm.PageControl1.Pages[i].RemoveControl( MForm.Panel4);
   MForm.PageControl1.Pages[i].RemoveControl( MForm.StatusBar1);
end;
//===================================================================

// Собственно очистка списка страниц.
For I:=MForm.PageControl1.PageCount-1  DownTo 0 do
if  MForm.PageControl1.ActivePageIndex<>I then MForm.PageControl1.Pages[I].Free;
MForm.PageControl1.ActivePage.Free;

end;

begin

...

If F_Script_Ctrl Then begin
Stab:=TCustomTabControl( MForm.PageControl1).Pages.Text;

ClearTabList; // Очистка списка страниц.

/Тут идет код создания закладок PageControl для  работы в другом режиме работы ...
//...

end else begin


ClearTabList; // Очистка списка страниц.

//Восстановление списка страниц.
ST:= TStringList.Create;
  ST.Text:=Stab;
For I:=0 To st.Count-1 do
  begin
    TS:= TTabSheet.Create(MForm.PageControl1);
    TS.PageControl:=MForm.PageControl1;
    TS.Caption:= ST[I];
    ts.Align:=alClient;
   end;
ST.Free;
end
end;


Не красиво но надеюсь надежно.
(Однако почему TCustomTabControl( MForm.PageControl1).Pages.Text:= STab ; работает даже БЕЗ создания TTabSheet все равно не понятно)
Последний раз редактировалось Alex2013 02.12.2021 21:54:21, всего редактировалось 14 раз(а).
Alex2013
долгожитель
 
Сообщения: 2948
Зарегистрирован: 03.04.2013 11:59:44

Re: Динамическое создание компонентов

Сообщение zub » 02.12.2021 17:54:14

>>Но всеравно не совсем понятно
>>Не красиво но надеюсь надежно
если непонятно - надежно не будет
zub
долгожитель
 
Сообщения: 2886
Зарегистрирован: 14.11.2005 23:51:26

Re: Динамическое создание компонентов

Сообщение Alex2013 » 02.12.2021 18:46:02

zub писал(а):>>Но всеравно не совсем понятно
>>Не красиво но надеюсь надежно
если непонятно - надежно не будет

Согласен! Но я уже не использую "таинственный хак" TCustomTabControl( MForm.PageControl1).Pages.Text:= STab ;
Который по идее работать не может "но почему-то работает". + отказался от "красивого финта" With TStringList.Create do и т. п.
Зы
Просто немного неприятно когда компонент работает "не так" как об этом думалось ранее . (Хотя одновременно есть шанс, что он чуть "умнее", благодаря этому иногда можно выкинуть "лишний код" )
Alex2013
долгожитель
 
Сообщения: 2948
Зарегистрирован: 03.04.2013 11:59:44

Re: Динамическое создание компонентов

Сообщение zub » 02.12.2021 19:34:14

Не понимаешь? разберись, начинать всегда надо с минимального примера с проблемой, а не с копипасты костылей в попытках чтоб все заработало
zub
долгожитель
 
Сообщения: 2886
Зарегистрирован: 14.11.2005 23:51:26

След.

Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 18

Рейтинг@Mail.ru