Arrow
Arrow30 января 2017 г. 14:22

Работа с динамическими компонентами

QPushButton

Помогите разобраться в чем проблема.

Идея такая:

Есть layout под именем btnLayout в который динамически добавляются кнопки при выполнении пользователем определенных действий.

При клике пользователем на кнопку должно отработаться определенное действие закрепленное за кнопкой и все последующие кнопки (созданные после данной кнопки) необходимо удалить. (текущая остается и созданные до нее)

В дизайнере создан btnLayout (горизонтальный) в который динамически добавляются кнопки (изначально он пустой). Этот btnLayout вместе с кнопкой (someButton) и спейсером между ними помещен в горизонтальный layout.

В низу фрагмент моего кода для реализации этого:


// В хедере
void addButton();

QPushButton *btn[20];

qint64 btnCount = 0;

// Реализация
void MainWindow::addButton()
{
    btnCount++;

    btn[btnCount] = new QPushButton(this);

    // Стиль кнопки
    btn[btnCount]->setStyleSheet(StyleHelper::getButtonStyleSheet());

    // Настройка свойств кнопки
    btn[btnCount]->setCheckable(true);
    btn[btnCount]->setChecked(true);
    btn[btnCount]->setText("Текст");

    ui->btnLayout->addWidget(btn[btnCount]);

    
    QString s = "...";
    qint64 num = btnCount;
    connect(btn[btnCount], &QPushButton::clicked, [this, s, num] {
        nTString(s);

        // Удалить все последующие кнопки
        for(int i = num+1; i <= btnCount; i++)
        {
            disconnect(btn[i]);
            delete btn[i];

            // Уменьшить счетчик
            btnCount--;
        }
    });

    // Снять выделение с предыдущей кнопки
    if(btnCount > 0) btn[btnCount-1]->setChecked(false);
}

Компиляция проходит успешно и работает.

Самое интересное начинается если для кнопки someButton добавить обработчик onClick. Тогда при запуске программа аварийно завершает работу.

При отладке выяснил, что вылет происходит на строке:

if(btnCount > 0) btn[btnCount-1]->setChecked(false);

При отладке появляется окно с текстом о том, что программа завершила свою работу получив соответствующее сообщение от ОС.

Если строку закоментировать, то все работает, только выделение с кнопок не снимается и все работает. Также работает если удалить обработчик onClick.

Но если количество динамически созданных кнопок более 5-ти, то удаляются все кнопки после текущей за исключением самой последней (ее тоже нужно удалять).

Не могу понять чем обработчик события onClick и строка

if(btnCount > 0) btn[btnCount-1]->setChecked(false);
не понравились друг другу :).
Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Вам это нравится? Поделитесь в социальных сетях!

37
Evgenii Legotckoi
  • 30 января 2017 г. 23:52

Здесь снимается выделение всё-таки не с предыдущей кнопки, а с текущей. Если количество кнопок btnCount = 20, то номер текущей кнопки будет равен 19. Счёт у программистов начинается с 0. ;-)

if(btnCount > 0) btn[btnCount-1]->setChecked(false);

Что это за nTString ? - это какой-то метод класса?

Насколько помню объекты объявленные в стеке лучше захватывать лямбдой через ссылку:

connect(btn[btnCount], &QPushButton::clicked, [this,  &s, &num] {

    });

Учитывая, что QPushButton - это объект, который наследован от QObject, то попробуйте удалять его соответствующим методом, а не через оператор delete . Это снимает некоторые проблемы, если объект где-то ещё используется.

btn[i]->deleteLater();

Но самой главной проблемой я считаю использование btnCount и num . Скорее всего, в какой-то момент происходит OutOfRange . Для num память выделяется в стеке локально в методе, и когда метод заканчивает работу, то память для num очищается, поэтому лямбда функция захватывает протухшую память. Если бы было выделение памяти в куче через указатель, то тогда всё работало бы правильно, а тут Undefined Behaviour получается.

Совет такой: Избавьтесь от использования btnCount и num . Замените строго заданный массив указателей на QPushButton на QVector. В нём всегда можно узнать количество элементов с помощью метода length() и номер текущего элемента с помощью метода indexOf() . А доводить до ума текущий код не стоит - это решение, которое в будущем создаст больше проблем.

    Arrow
    • 31 января 2017 г. 10:09

    Спасибо!

    Я как-то о проблемах с памятью не подумал.

    Про счет с нуля знаю :), но пишу btnCount++ - и начинаю счет с 1. :)

    С векторами в Qt никогда не работал - по документации получилось, что-то такое:

    
    // В хедере
    
    void addButton();
    
    QVector vector;
    
    // Реализация
    
    void MainWindow::addButton()
    {
        vector.append(new QPushButton());
        vector.last()->setStyleSheet(StyleHelper::getButtonStyleSheet());
        
    // Настройка свойств кнопки
        vector.last()->setMinimumHeight(35);
        vector.last()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
        vector.last()->setCheckable(true);
        vector.last()->setChecked(true);
        vector.last()->setText(QFileInfo(dir.path()).fileName());
        
    // Добавить в btnLayout
        ui->btnLayout->addWidget(vector.last());
    
        // Сигнал нажатия кнопки
        QString s = dir.path();
        QPushButton *b = vector.last();
        connect(vector.last(), &QPushButton::clicked, [this, s, b] {
            // mainList  - QListView, model - QFileSystemModel
            ui->mainList->setRootIndex(model->index(s));
    
            // Удалить все последующие кнопки
            for(int i = vector.indexOf(b)+1; i < vector.length(); i++)
            {
                disconnect(vector[i);
                vector[i]->deleteLater();
            }
        });
    
        if(vector.length() > 1) vector[vector.indexOf(b)-1]->setCheckable(false);
    }
    

    Если писать так:

    connect(vector.last(), &QPushButton::clicked, [this, &s, &b] 
    

    программа вылетает с аварийным сообщением.

    Все работает корректно, только после клика на кнопке выполнить действие для добавления еще одной - вылетает с надписью: Поток 0 остановлен по причине: Exception at 0x51f01fd4, read access violation.

    Вылет на строке:

    vector[i]->deleteLater();
    

    Если выполнить действие для удаления кнопки, то с тем же сообщением на строке:

    if(vector.length() > 1) vector[vector.indexOf(b)-1]->setCheckable(false);
    

    Так понимаю, что это ошибка доступа к памяти - ссылку на кнопку теряет?

    И вопрос попутно как в отладчике в Qt Creator посмотреть значения переменных, например s из кода выше (кроме qDebug << s;). Я имею ввиду, что-то похожее на то как в отладчике MSVS.

      Arrow
      • 31 января 2017 г. 10:11
      QVector<QPushButton*> vector;
      
        Evgenii Legotckoi
        • 31 января 2017 г. 10:32

        Про счет с нуля знаю :), но пишу btnCount++ - и начинаю счет с 1. :)
        Это неочевидный грязный хак. Такие вещи не должны проходить код-ревью, и должны отправляться на переписывание кода. К тому же... А вы не задумывались, что тогда в Вашем массиве QPushButton *btn[20]; на нулевом индексе будет находиться мусор, вместо указателя на первую добавленную кнопку? В р

        Теперь я догадываюсь, где ошибка.

        if(btnCount > 0) btn[btnCount-1]->setChecked(false);
        

        В этом коде скорее всего происходит обращение к элементу с нулевым индексом в массиве. В результате программа вылетает, поскольку там находится мусор.

        Подумайте сами. btnCount равен 1, когда Вы добавили одну кнопку. И тогда Вы пытаетесь снять выделение с предыдущей кнопки, btnCount - 1, и получается 0. обращаетесь к элементу с индексом 0, к мусору. Естественно, что программа вылетала. Вы сами себе накидали граблей.

        В ниже следующем куске кода явно ошибка, это вы так скопипастили из Креатора?

        disconnect(vector[i);
        vector[i]->deleteLater();
        
        Если писать так:
        connect(vector.last(), &QPushButton::clicked, [this, &s, &b] 
        
        программа вылетает с аварийным сообщением.

        Правильно, что ошибку выдаёт, у вас же b - это указатель, объект сохранён в куче. Для него память выделена через new. Его нельзя по ссылке захватить.

        connect(vector.last(), &QPushButton::clicked, [this, &s, b] 
        
        Вылет на строке:
        vector[i]->deleteLater();
        
        Если выполнить действие для удаления кнопки, то с тем же сообщением на строке:
        if(vector.length() > 1) vector[vector.indexOf(b)-1]->setCheckable(false);
        

        Может наоборот?

          Arrow
          • 31 января 2017 г. 13:25
          Про счет с нуля знаю :), но пишу btnCount++ - и начинаю счет с 1. :) Это неочевидный грязный хак. Такие вещи не должны проходить код-ревью, и должны отправляться на переписывание кода.

          Понятно, больше так писать. не буду.

          В ниже следующем куске кода явно ошибка, это вы так скопипастили из Креатора?
          disconnect(vector[i);
          vector[i]->deleteLater();
          

          Да и к сожалению что не так не знаю.

          Такая конструкция работать отказывается - ошибка:

          connect(vector.last(), &QPushButton::clicked, [this, &s, b] 
          

          Работает так:

          connect(vector.last(), &QPushButton::clicked, [this, s, b] 
          
          Может наоборот?

          Да.

            Evgenii Legotckoi
            • 31 января 2017 г. 13:31

            Я имел ввиду то, что написано неправильно:

            disconnect(vector[i);
            

            Правильное написание:

            disconnect(vector[i]);
            

            Qt Creator разве не подсвечивал этот кусок?

              Arrow
              • 31 января 2017 г. 13:33

              Туплю, скобки не заметил, в креаторе все правильно написано.

                Arrow
                • 31 января 2017 г. 13:35

                И все таки, что может быть не так в коде?

                И как просматривать значения переменных в отладчике в Qt Creator?

                  Evgenii Legotckoi
                  • 31 января 2017 г. 13:37

                  Так, а что с объявлением QVector? Это просто здесь так написали?

                  QVector vector;
                  

                  Или в коде это выглядит как положено?

                  QVector<QPushButton *> vector;
                  
                    Arrow
                    • 31 января 2017 г. 13:39

                    В коде второй вариант. В первом случае почему-то скопировал не все.

                      Evgenii Legotckoi
                      • 31 января 2017 г. 13:45

                      Кажется знаю. Объект удалить-то удалили через deleteLater, но в самом QVector указатель-то остался. Нужно добавить ещё и удаление элемента из QVector

                      disconnect(vector[i);
                      vector[i]->deleteLater();
                      

                      То есть будет так:

                      disconnect(vector[i);
                      vector[i]->deleteLater();
                      vector.remove(i);
                      
                        Arrow
                        • 31 января 2017 г. 13:46

                        Еще один вопрос немного не по теме - как обновить отображаемые в QListView данные? repain и update - не выолняют нужного действия.

                          Evgenii Legotckoi
                          • 31 января 2017 г. 13:51

                          repaint и update только перерисовывают сам QListView с содержимым. Чтобы их обновить, необходимо обновить сами данные. Если данные не обновлялись, то выше приведённые методы не окажут видимого эффекта.

                          И что касается значений переменных в отладчике, во время отладки обычно открывается справа панелька с таблицей, где можно посмотреть какие в данный момент переменные активны и их содержимое.

                            Arrow
                            • 31 января 2017 г. 13:51

                            Да работает, только все равно если кнопок больше двух последнюю кнопку не удаляет.

                            Цикл выглядит так:

                            for(int i = vector.indexOf(b)+1; i < vector.length(); i++)
                                    {
                                        disconnect(vector[i]);
                                        vector[i]->deleteLater();
                                        vector.remove(i);
                                    }
                            
                              Arrow
                              • 31 января 2017 г. 13:55

                              Спасибо с обновлением данных в QListView понятно куда копать.

                              В отладчике таблицы нет, панель есть, но пустая.

                                Evgenii Legotckoi
                                • 31 января 2017 г. 13:58

                                Это уже проблема самого цикла. Когда удаляется элемент из вектора, то уменьшается его длина. Поэтому нужно сам цикл реструктурировать.

                                while(vector.last() != b)
                                {
                                    disconnect(vector.last());
                                    vector.last()->deleteLater();
                                    vector.removeLast();
                                }
                                
                                  Evgenii Legotckoi
                                  • 31 января 2017 г. 14:00

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

                                    Arrow
                                    • 31 января 2017 г. 14:00

                                    Спасибо! Работает.

                                      Arrow
                                      • 31 января 2017 г. 14:04

                                      Спасибо, понял. Плюс нужно было поставить выбрать пункт - показывать локальные переменные.

                                        Arrow
                                        • 31 января 2017 г. 14:09

                                        Все равно, отладчик не всегда показывает переменные. Приходится постоянно добавлять вычисляемые значения

                                          Evgenii Legotckoi
                                          • 31 января 2017 г. 14:11

                                          Ну это уже издержки производства.

                                            Arrow
                                            • 31 января 2017 г. 14:15

                                            Согласен :)

                                              Arrow
                                              • 31 января 2017 г. 14:20

                                              Еще вопросик. Решил стилизировать меню через QSS и написал такое:

                                              QString StyleHelper::getMenuStyleSheet()
                                              {
                                                  return "QMenu {"
                                                              "background-color: #fff;"
                                                              "border: none;"
                                                              "border-radius: 0px;"
                                                              "margin: 2px;"
                                                              "}"
                                                              "QMenu::item:selected {"
                                                              "color: #111;"
                                                              "background-color: qlineargradient( x1: 0, y1: 0, x2: 0, y2: 1,"
                                                                      "stop: 0 #fff, stop: 1 #eef);"
                                                              "}"
                                                              "QMenu::separator {"
                                                              "height: .5px;"
                                                              "background: #eee;"
                                                              "margin-left: 10px;"
                                                              "margin-right: 5px;"
                                                              "}"
                                                              "QMenu::indicator:non-exclusive:checked {"
                                                              "image: url(:/pict/check.png);"
                                                              "}"
                                                              "QMenu::indicator:non-exclusive:unchecked {"
                                                              "image: url(:/pict/unchecked.png);"
                                                              "}";
                                              }
                                              

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

                                              Как можно убрать этот прямоугольник?

                                                Arrow
                                                • 31 января 2017 г. 14:30
                                                • Ответ был помечен как решение.

                                                Спасибо за помощь!

                                                Если читателям форума понадобится, вот отредактированный рабочий код:

                                                // Хедер *.h
                                                
                                                // Добавить кнопку директории на панель
                                                    void addButton();
                                                
                                                // Кнопки
                                                    QVector<QPushButton*> vector;
                                                
                                                // Реализация *.cpp
                                                
                                                void MainWindow::addButton()
                                                {
                                                    // Добавить кнопку
                                                
                                                    vector.append(new QPushButton());
                                                    vector.last()->setStyleSheet(StyleHelper::getButtonStyleSheet());
                                                
                                                    // Настройка свойств кнопки
                                                    vector.last()->setMinimumHeight(35);
                                                    vector.last()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
                                                    vector.last()->setCheckable(true);
                                                    vector.last()->setChecked(true);
                                                    vector.last()->setText(QFileInfo(dir.path()).fileName());
                                                    // Добавить в Layout
                                                    ui->btnLayout->addWidget(vector.last());
                                                
                                                    // Сигнал нажатия кнопки
                                                    QString s = dir.path();
                                                    QPushButton *b = vector.last();
                                                    connect(vector.last(), &QPushButton::clicked, [this, s, b] {        
                                                        ui->mainList->setRootIndex(model->index(s));
                                                        // Удалить все последующие кнопки
                                                        while(vector.last() != b)
                                                        {
                                                            disconnect(vector.last());
                                                            vector.last()->deleteLater();
                                                            vector.removeLast();
                                                        }
                                                    });
                                                
                                                    if(vector.length() > 1) vector[vector.indexOf(b)-1]->setChecked(false);
                                                }
                                                
                                                  Evgenii Legotckoi
                                                  • 31 января 2017 г. 14:37

                                                  Что-то не совсем представляю, как выглядит этот оттиск. Можете приложить скриншот в прикрепление к сообщению?

                                                  И ещё. Делайте лучше отдельные темы по отдельным вопросам.

                                                    Arrow
                                                    • 31 января 2017 г. 14:55

                                                    Буду в другой раз создавать тему.

                                                    Скриншот меню не вышел :), сделал похожий. Круг - это "моя картинка", а рамка - не мое творение.

                                                      Arrow
                                                      • 31 января 2017 г. 14:58

                                                      Скриншот приложить не выходит - форум выбивает ошибку.

                                                        Arrow
                                                        • 31 января 2017 г. 14:59

                                                        0

                                                          Arrow
                                                          • 31 января 2017 г. 14:59

                                                          Получилось :)

                                                            Evgenii Legotckoi
                                                            • 1 февраля 2017 г. 1:12

                                                            Не уверен, но возможно стоит применить настройку border для QMenu::indicator:non-exclusive:checked

                                                              Arrow
                                                              • 1 февраля 2017 г. 3:03

                                                              Не помогло. Сделал фото как это выглядит на экране.

                                                                Evgenii Legotckoi
                                                                • 1 февраля 2017 г. 8:38

                                                                Как вариант, наследовать QMenu и переопределить метод paintEvent и там уже отрисовывать, как хочется. Этот путь конечно посложнее, но гарантирует результат. А насчёт QSS у меня пока мыслей нет.

                                                                  Arrow
                                                                  • 1 февраля 2017 г. 8:50

                                                                  Немного помудрил с кодом и выяснил, что если убрать следующие строки, то и проблема исчезает, только вместе с картинками :)

                                                                                  "QMenu::indicator:non-exclusive:checked {"
                                                                                  "image: url(:/pict/check.png);"
                                                                                  "}"
                                                                                  "QMenu::indicator:non-exclusive:unchecked {"
                                                                                  "image: url(:/pict/unchecked.png);"
                                                                                  "}";
                                                                  
                                                                  Наследование это хорошо, но проблема насколько я понимаю в назначении картинки.
                                                                    Arrow
                                                                    • 1 февраля 2017 г. 8:53

                                                                    Вообще если в коде затрагивать indicator, например так:

                                                                                    "QMenu::indicator {"
                                                                                    "width: 12px;"
                                                                                    "height: 12px;"
                                                                                    "}"
                                                                    

                                                                    возникает эта проблема.

                                                                      Evgenii Legotckoi
                                                                      • 1 февраля 2017 г. 8:57

                                                                      Ну а если применить border к indicator?

                                                                      "QMenu::indicator {"
                                                                      "width: 12px;"
                                                                      "height: 12px;"
                                                                      "border: none;"
                                                                      "}"
                                                                      
                                                                        Arrow
                                                                        • 1 февраля 2017 г. 15:34

                                                                        "Не действует."

                                                                          Arrow
                                                                          • 1 февраля 2017 г. 15:41

                                                                          Рабочий вариант:

                                                                          QString StyleHelper::getMenuStyleSheet()
                                                                          {
                                                                              return "QMenu {"
                                                                                          "background-color: #fff;"
                                                                                          "border: none;"
                                                                                          "border-radius: 0px;"
                                                                                          "}"
                                                                                          "QMenu::item {"
                                                                                          "padding-left: 18px;"
                                                                                          "margin: 4px;"
                                                                                          "}"
                                                                                          "QMenu::item:selected {"
                                                                                          "color: #111;"
                                                                                          "background-color: qlineargradient( x1: 0, y1: 0, x2: 0, y2: 1,"
                                                                                                  "stop: 0 #fff, stop: 1 #eef);"
                                                                                          "}"
                                                                                          "QMenu::separator {"
                                                                                          "height: .5px;"
                                                                                          "background: #eee;"
                                                                                          "margin-left: 10px;"
                                                                                          "margin-right: 5px;"
                                                                                          "}"
                                                                                          "QMenu::indicator {"
                                                                                          "width: 14px;"
                                                                                          "height: 14px;"
                                                                                          "}"
                                                                                          "QMenu::indicator:non-exclusive:checked {"
                                                                                          "image: url(:/pict/check.png);"
                                                                                          "}"
                                                                                          "QMenu::indicator:non-exclusive:unchecked {"
                                                                                          "image: url(:/pict/unchecked.png);"
                                                                                          "}";
                                                                          }
                                                                          

                                                                          Добавил следующий код:

                                                                                         "QMenu::item {"
                                                                                          "padding-left: 18px;"
                                                                                          "margin: 4px;"
                                                                                          "}"
                                                                          

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

                                                                          Спасибо за помощь, Ваш код с "border: none;" натолкнул на правильную мысль :)

                                                                            Комментарии

                                                                            Только авторизованные пользователи могут публиковать комментарии.
                                                                            Пожалуйста, авторизуйтесь или зарегистрируйтесь
                                                                            AD

                                                                            C++ - Тест 004. Указатели, Массивы и Циклы

                                                                            • Результат:50баллов,
                                                                            • Очки рейтинга-4
                                                                            m
                                                                            • molni99
                                                                            • 26 октября 2024 г. 1:37

                                                                            C++ - Тест 004. Указатели, Массивы и Циклы

                                                                            • Результат:80баллов,
                                                                            • Очки рейтинга4
                                                                            m
                                                                            • molni99
                                                                            • 26 октября 2024 г. 1:29

                                                                            C++ - Тест 004. Указатели, Массивы и Циклы

                                                                            • Результат:20баллов,
                                                                            • Очки рейтинга-10
                                                                            Последние комментарии
                                                                            i
                                                                            innorwall11 ноября 2024 г. 22:12
                                                                            Django - Урок 055. Как написать функционал auto populate field Freckles because of several brand names retin a, atralin buy generic priligy
                                                                            i
                                                                            innorwall11 ноября 2024 г. 18:23
                                                                            QML - Урок 035. Использование перечислений в QML без C++ priligy cvs 24 Together with antibiotics such as amphotericin B 10, griseofulvin 11 and streptomycin 12, chloramphenicol 9 is in the World Health Organisation s List of Essential Medici…
                                                                            i
                                                                            innorwall11 ноября 2024 г. 15:50
                                                                            Qt/C++ - Урок 052. Кастомизация Qt Аудио плеера в стиле AIMP It decreases stress, supports hormone balance, and regulates and increases blood flow to the reproductive organs buy priligy online safe Promising data were reported in a PDX model re…
                                                                            i
                                                                            innorwall11 ноября 2024 г. 14:19
                                                                            Алгоритм сортировки кучей The role of raloxifene in preventing breast cancer priligy precio
                                                                            i
                                                                            innorwall11 ноября 2024 г. 13:55
                                                                            PyQt5 - Урок 006. Работа с QTableWidget buy priligy 60 mg 53 have been reported by Javanovic Santa et al
                                                                            Сейчас обсуждают на форуме
                                                                            i
                                                                            innorwall11 ноября 2024 г. 20:56
                                                                            добавить qlineseries в функции buy priligy senior brother Chu He, whom he had known for many years
                                                                            i
                                                                            innorwall11 ноября 2024 г. 10:55
                                                                            Всё ещё разбираюсь с кешем. priligy walgreens levitra dulcolax carbs The third ring was found to be made up of ultra relativistic electrons, which are also present in both the outer and inner rings
                                                                            9
                                                                            9Anonim25 октября 2024 г. 9:10
                                                                            Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…
                                                                            ИМ
                                                                            Игорь Максимов3 октября 2024 г. 4:05
                                                                            Реализация навигации по разделам Спасибо Евгений!

                                                                            Следите за нами в социальных сетях