C++ Builder динамичное создание элементов

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by besfamilnyi, 7 Jun 2019.

  1. besfamilnyi

    besfamilnyi Member

    Joined:
    5 Jun 2012
    Messages:
    45
    Likes Received:
    10
    Reputations:
    0
    Всем привет. В общем капитально запутался, и не могу правильно организовать структуру программы (или как это правильно называется?). Собственно задача: создать интерфейс как на картинке. С динамическим созданием совладал, интерфейс создается, но я не знаю как создать событие он клик, точнее знаю но есть одно но, есть небольшой такой парадокс непонятный для меня.
    max_el=accs_list->Count; можно узнать только после нажатия кнопки "загрузить акки", и поэтому получается что нужно создавать массивы указателей по кол-ву акков после того как StringList accs_list заполнится, но, парадокс для меня вот в чем, что бы создать кол-во элементов равное кол-ву акков, приходится объявлять массивы указателей в обработчике кнопки, и там же создаются сами элементы, но функция он клик далее в коде void __fastcall TForm1::stop_click(TObject * Sender) находится не в функции кнопки TForm1::Button1Click и если я в обработчике он клик кнопки stop вызову label_stop_command[((TButton*)Sender)->Tag]->Caption="true"; то это ошибка т.к. элемент label_stop_command[] еще не создан и соответственно нельзя к нему обратиться, компилятор ругается.
    можно сделать например TGroupBox **accs_thread=new TGroupBox*[100]; но если допустим будет 101 акк в текстовом файле то вывалится ошибка, да и вообще этот вариант не нравится, нужно создание элементов четко по кол-ву акков, не больше и не меньше.
    Сам кодер-любитель самоучка если что и в профессиональных терминах не особо разбираюсь, поэтому если можно объясните пожалуйста понятным языком и желательно на пальцах.
    В общем цель: динамичное создание элементов по кол-ву акков, с полноценными кнопками и обработчиками, с зараннее неизвестным кол-вом акков. Спасибо, с уважением.
    Code:
    TStringList*accs_list=new TStringList;//тут акки
    
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
           OpenTextFileAccs->Execute();
           accs_list->LoadFromFile(OpenTextFileAccs->FileName);
           Label2->Caption="["+IntToStr(accs_list->Count)+"]";
            max_el=accs_list->Count;
    // создаю массивы указателей по кол-ву акков, в .h файле они все прописаны extern TGroupBox **accs_thread; итд
            TGroupBox **accs_thread=new TGroupBox*[max_el];
            TLabel **label_acc=new TLabel*[max_el];
            TLabel **label_status=new TLabel*[max_el];
            TLabel **work_status=new TLabel*[max_el];
            TLabel **label_stop_command=new TLabel*[max_el];
            TButton **stop_thread=new TButton*[max_el];
    
            int i_el =0;
    //создаю визуальные элементы
            while (i_el<accs_list->Count)
           {
            accs_thread[i_el]=new TGroupBox(this);
    
            accs_thread[i_el]->Parent=ScrollBox1;
    
              accs_thread[i_el]->Align = alTop;
              accs_thread[i_el]->AlignWithMargins=true;
              accs_thread[i_el]->Margins->Top=3;
            accs_thread[i_el]->Left=20;
            accs_thread[i_el]->Height=65;
            accs_thread[i_el]->Width=660;
            accs_thread[i_el]->ParentColor=false;
            accs_thread[i_el]->ParentBackground=false;
            accs_thread[i_el]->Color=clSkyBlue;
            accs_thread[i_el]->Caption="Поток#"+IntToStr(i_el)+" "+accs_list->Strings[i_el];
            ///
             label_acc[i_el] = new TLabel(this);
            label_acc[i_el]->Parent=accs_thread[i_el];
            label_acc[i_el]->Caption="acc"+IntToStr(i_el);
            label_acc[i_el]->Left=15;
            label_acc[i_el]->Top=15;//top_grboxacc;
           
          
             label_stop_command[i_el] = new TLabel(this);
            label_stop_command[i_el]->Parent=accs_thread[i_el];
            label_stop_command[i_el]->Caption="false";
            label_stop_command[i_el]->Left=100;
            label_stop_command[i_el]->Top=15;//top_grboxacc;
            //label_stop_command[i]->Visible=false;
    
             work_status[i_el] = new TLabel(this);
            work_status[i_el]->Parent=accs_thread[i_el];
            work_status[i_el]->Caption="ожидание";
            work_status[i_el]->Left=200;
            work_status[i_el]->Top=15;
    
            label_status[i_el] = new TLabel(this);
            label_status[i_el]->Parent=accs_thread[i_el];
            label_status[i_el]->Caption="Status: ";
            label_status[i_el]->Left=150;
            label_status[i_el]->Top=15;
    
            stop_thread[i_el] = new TButton(this);
            stop_thread[i_el]->Parent=accs_thread[i_el];
            stop_thread[i_el]->Caption="stop";
            stop_thread[i_el]->Left=310;
            stop_thread[i_el]->Height=23;
            stop_thread[i_el]->Width=54;
            stop_thread[i_el]->Top=15;
            stop_thread[i_el]->Tag=i_el;
            stop_thread[i_el]->OnClick = stop_click; //функция он клик кнопки stop
    
            i_el ++;
           }
    //правильно ли я удаляю созданные массивы указателей?
        delete[] accs_thread;
       delete[] label_acc;
       delete[] label_status;
       delete[] work_status;
       delete[] label_stop_command;
       delete[] stop_thread;
    
    }
    
    Code:
     void __fastcall TForm1::stop_click(TObject * Sender)
    {
        Form1->MemoLog->Lines->Add("qweqwe^_^");
        Form1->MemoLog->Lines->Add( ((TButton*)Sender)->Tag);
        //Form1->MemoLog->Lines->Add( BoolToStr(mass_stop_thr[((TButton*)Sender)->Tag]) );
        //label_stop_command[((TButton*)Sender)->Tag]->Caption="true";
    }
    
    [​IMG]
     
  2. DartPhoenix

    DartPhoenix Elder - Старейшина

    Joined:
    15 Sep 2013
    Messages:
    1,106
    Likes Received:
    8,474
    Reputations:
    25
    Ох. Я конечно слегка перебрал и потому нихрена не понял.
    Но делается все так:
    1. Ты загружаешь акки в StringList
    2. Каждому акку создаешь в соответствие компонент и где-то в служебном поле прописываешь например ID этого акка (порядковый номер в string_list). UPD: но надо учесть что при удалении номера эти id сместятся поэтому желательно-бы как-то хранить их иначе чем в обычном списке. Ну или найти красивое решение можно, но это придется поморщить мозг. В тупую если - то лучше всего просто по id-шникам.
    3. Размещаешь кнопочки как тебе пожелается и связываешь все их on_click с одним единственным обработчиком которому либо передаешь в качестве параметра присвоенный id - либо из этого обработчика определяешь Sender'а а у него читаешь этот id/
    4. Внутри обработчика, согласно указанному id делаешь то что требуется.

    UPD: уничтожение компонентов можно привязать к соответствующему свойству StringGrid'а (ты же в нем создаешь их ? ) или поднять собственное свойство... тут уже зависит от реализации. И все вроде...
     
    #2 DartPhoenix, 7 Jun 2019
    Last edited: 7 Jun 2019
  3. besfamilnyi

    besfamilnyi Member

    Joined:
    5 Jun 2012
    Messages:
    45
    Likes Received:
    10
    Reputations:
    0
    Спасибо что откликнулись.

    Вот примерно так и делаю. Вместо id и служебного поля юзаю Tag, вот этой строчкой создаю его для каждой кнопки stop_thread[i_el]->Tag=i_el;

    А вот она и та самая одна функция он-клик, и вы наверное видите закомментированную строчку где я и смотрел Sender, вот только есть одна проблема которую я и назвал парадоксом, при такой архитектуре нельзя изменить в он-клике лейбл label_stop_command[((TButton*)Sender)->Tag]->Caption="true"; (тут как раз использую Tag для определения какой именно элемент нужно поменять по счёту) так как он ещё не создан, а раньше создавать массивы указателей я не могу, потому что тогда будет неизвестно кол-во акков по которому и создается нужное кол-во элементов.
    Code:
    void __fastcall TForm1::stop_click(TObject * Sender)
    {
       Form1->MemoLog->Lines->Add("qweqwe^_^");
       Form1->MemoLog->Lines->Add( ((TButton*)Sender)->Tag); //просто так в Лог выводится ID нажатой кнопки
       //а вот эта строчка закомментирована т.к. ошибка ведь label_stop_command еще не создан.
       //label_stop_command[((TButton*)Sender)->Tag]->Caption="true";
    }
    
    Зачем я меняю этот лейбл, что бы в потоке, можно было определить для какого потока нажата кнопка "stop", ну типа такого:

    Code:
           while (true) //допустим это какой то рабочий цикл
           {
               if (label_stop_command[thread_id]->Caption=="true")
               {
               work_status[thread_id]->Caption="остановлен";
               break;
               }
               ::Sleep(1000);
           }
    
    Изначально для этой цели пробовал сделать глобальный массив bool, но какие то траблы с ним были именно при глобальном использовании, не зашло в общем и пришлось выдумывать вот с лейблом.
    Грубо говоря я и сделал как вы сказали и всё работает, интерфейс создается динамически (на скрине видно), у каждой кнопки свой Tag - номер компонента согласно номеру акка, и функция он-клик тоже создается, одна, в ней смотрю тег того кто вызвал он-клик ((TButton*)Sender)->Tag , с этим проблем нет. Проблема в том что из он-клика нельзя поменять Caption лейбла т.к. этот лейбл еще не создан, а зараннее создать их нельзя т.к. еще будет неизвестно кол-во акков.

    кстати прочитал в гугле что в моем случае
    delete[] accs_thread;
    будет некорректным удалением. Ну это ладно, в цикле удалю оператором delete.

    нет, для списка акков использую StringList, а сами визуальные компоненты это просто GroupBox'ы же просто в ScrollBox'е.

    как то так...
     
    #3 besfamilnyi, 8 Jun 2019
    Last edited: 8 Jun 2019
  4. DartPhoenix

    DartPhoenix Elder - Старейшина

    Joined:
    15 Sep 2013
    Messages:
    1,106
    Likes Received:
    8,474
    Reputations:
    25
    Чот я туплю. Нехрена не понять как это не создан если ты создаешь лейбл вместе с кнопкой stop. Если создана кнопка - значит есть и лейбл с таким же id. Можно проверять указатель на null еще на всякий случай для хорошего тона.

    Вообще с тредами так не очень правильно работать. Обычно для этого юзаются мьютексы/семафоры, но в принципе обычный булев массив как ты делал - это норм, но тоже с мьютексом чтобы разные треды одновременно не могли писать и читать оттуда ибо будет жопа. Тут есть вроде ман.
     
  5. besfamilnyi

    besfamilnyi Member

    Joined:
    5 Jun 2012
    Messages:
    45
    Likes Received:
    10
    Reputations:
    0
    Дело в том что все элементы создаются в функции это кнопка "LoadAccs"
    void __fastcall TForm1::Button1Click(TObject *Sender)
    { }
    И получается что пока эта функция не была вызвана, элементов нету, и нету того лейбла. А заранее создать массив указателей я не могу, т.к. еще неизвестно кол-во акков.
    Палка о двух концах короче. Не знаю с какой стороны подойти. Изначально я делал отдельную функцию CreateElements на юните формы, и при старте потоков вызывал ее из потока, но так не подходит, потому что по замыслу нужно сначала загрузить акки и создать для каждого акка еще интерфейсы управления, для каждого акка будут задаваться разные настройки, еще до их запуска.

    Для синхронизации, в потоках юзаю CriticalSection, это я для примера привел максимально простой код, что бы понятно было зачем в данном случае мне менять Label, но суть моей проблемы не в этом.
    Спасибо.

    __________________________________________________________________________

    UPD: гуглением на тему динамических массивов наткнулся на векторы. Но пока не пойму как их можно использовать.
     
    #5 besfamilnyi, 9 Jun 2019
    Last edited: 9 Jun 2019