Как стать автором
Обновить

Комментарии 23

Что-то не собирается
ошибка
ошибка: no matching function for call to ‘BaseComboModel::BaseComboModel(QString, const char [8], AddressBookMainWindow*)’
#define NEW_COUNTRY_MODEL new BaseComboModel( QString( "%1 || ' — ' || %2 " ).arg( AddressBookMainWindow::COUNTRY_NAME_COLUMN ).arg( AddressBookMainWindow::COUNTRY_CODE_COLUMN ), «country», this )

Починил, пробуйте.
Добавление пустых строчек-заменителей делается всё на стандартных моделях без зауми. QSqlQueryModel
SELECT -1 as id, '(Select country)' as countryname UNION SELECT id, countryname FROM country

В чём магия создания моделей именно для QComboBox — не раскрыто.
Как вставлять отличные от QListView отображения в QComboBox и пользовать их — не сказано.

Иными словами, много и ни о чём.
Добавление пустых строчек-заменителей делается всё на стандартных моделях без зауми. QSqlQueryModel

Пустые строчки можно делать и SQL запросом, согласен. Хотя если брать ваш UNION. То как быть в случае когда второй столбец будет иметь какой то другой тип данных? Переписать запрос?! В том то и дело что модели всё равно на конфликт типов данных с SQL запросом.
В чём магия создания моделей именно для QComboBox — не раскрыто.

В том то и дело что тут нет никакой магии. Всё проще простого. Смотрите код. Причём модель для таблицы можно подать в комбобокс и наоборот.
Как вставлять отличные от QListView отображения в QComboBox и пользовать их — не сказано.

А я обещал осветить именно эту тему?! Я обещал заполнять контентом комбобоксы одной строчкой кода. И показал.
Решается это так: QTableView c указанной QSqlQueryModel. Но там надо назначить делегаты которые будут отображать комбобоксы при редактировании. Эти делегаты обычно пишут наследуя от QStyledItemDelegate. Это большая тема. Я не смогу описать здесь все тонкости за раз.

То как быть в случае когда второй столбец будет иметь какой то другой тип данных?
Писать модель или кастовать их в общий тип на стороне БД. А у вас часто так бывает, что типы в столбце не совпадают?
В том то и дело что тут нет никакой магии.
Я с вами не согласен. Есть некоторые фишки, которые есть только у QComboBox, и которые можно интересно использовать: смена отображения, редактирование с валидатором, манипуляции над моделью аля мультивыбор. Но вы о них ни слова не сказали, как будто даже доки не открывали.
Я обещал заполнять контентом комбобоксы одной строчкой кода. И показал.
Вы целую модель под это написали. А всего-то надо было запихнуть union в запрос и modelColumn(1) в комбобокс, да IDшник брать по comboBox->model()->index(index.row(),0).data().toInt().
Решается это так
Что решается? Это уже решено, скрин по ссылке. Стоило хотя бы просто указать, что вместе с моделью можно менять и отображение.
Писать модель или кастовать их в общий тип на стороне БД. А у вас часто так бывает, что типы в столбце не совпадают?

У меня так может быть. У моих коллег такое может случиться. Поэтому мне надо писать поддерживаемый код. Который не свалиться на таких мелочах. К тому же это учебный пример. О том что так можно делать в принципе. Я набегался по этим граблям с UNION потому что мой запрос к базе обязан был работать аж в двух базах одновременно. Мне совсем не охота гадать кто и когда наступит на эти грабли снова.
Я с вами не согласен. Есть некоторые фишки, которые есть только у QComboBox, и которые можно интересно использовать: смена отображения, редактирование с валидатором, манипуляции над моделью аля мультивыбор. Но вы о них ни слова не сказали, как будто даже доки не открывали.

Погодите ка. А я разъве обязан тут обо всём этом писать? Я ж не писатель документации из команды Qt. Я просто поделился тем что знаю очень хорошо и что может пригодиться другим. Вам надо развить тему, пожалуйста пишите свою статью.
Вы целую модель под это написали.

Просто для меня это естественно. И я не считаю это чем то сложным и заумным.
А всего-то надо было запихнуть union в запрос и modelColumn(1) в комбобокс, да IDшник брать по comboBox->model()->index(index.row(),0).data().toInt().

Даже modelColumn(1) вам тут не нужен. Надо просто первым столбцом выводить не первичный ключ. А пользовательские данные. Например «SELECT countryname, id FROM countries».
Что решается? Это уже решено, скрин по ссылке.

Менять данные таблицы в полях слева от таблицы?! Ваш проект вы и решаете что тут считать решением.
Стоило хотя бы просто указать, что вместе с моделью можно менять и отображение.
Напишите свою статью о комбобоксах. Я за вас только порадуюсь.
Извините, что посмел лезть к Его Величеству со своими ничтожнейшими высирами! Конечно, Вы правы! Повелите мне самолюстрироваться-с?
существуют ли хорошие реализации QCOmboBox с checkboxes? Что бы можно было выбрать более одного пункта и удобно было выбрать все/ни одного?
По-моему для такого больше подойдет QListView и отдельные кнопки выбрать все/сбросить.
Но если очень надо, то берите любой вариант из гугла «qcombobox with checkboxes» и просто добавляйте контекстное меню с соответствующими командами.

Понятно, спасибо. Я уже использую одну из реализаций из гугла, но все что я пробовал не без недостатков.
Не работал ни разу с такими. Можно копнуть в сторону создания своего делегата для QComboBox. QComboBox::setItemDelegate(QAbstractItemDelegate * delegate). Если он похож как для QTableView. Там хоть преферанс с куртизанками впихнуть можно. Но это тема требующая времени.
Я в свое время написал для себя класс выпадающего списка для работы с данными пита [id,text]
   comboBox1 = new BaseKeyComboBox();
    comboBox1->setModel(sourceModel); //модель примерно такая "SELECT RecordID, DisplayText FROM MyCatalog;"
    comboBox1->setModelColumn(1); // Колонка для отображения текста
    comboBox1->setKeyColumn(0); // Колонка для ключа ID ТОЛЬКО int!!!!!!!
    comboBox1->setEditable(false);

    // mapper - это QDataWidgetMapper
    // Подключается как обычно
    mapper->addMapping(comboBox1, mainModel->fieldIndex("referemceid"));

Причем я не понял зачем делать модель специально для этого случая.
У меня можно подцепить любую.

И я не увидел, он у вас с QDataWidgetMapper умеет работать?
Причем я не понял зачем делать модель специально для этого случая.

Вот вы заполняете комбобокс пятью строчками кода плюс незнаю что там в модели. А я это делаю одной строкой. Когда комбобоксов от 10-и в программе. Есть над чем задуматься.
И я не увидел, он у вас с QDataWidgetMapper умеет работать?

Нет. Дело в том что мой комбобокс обязан устанавливать значения по вторичному ключу БД из Qt::UserRole. А QDataWidgetMapper работает со строкой из Qt::DisplayRole. К тому же этот маппер работает через раз.
Если у вас есть пара часов свободного времени. Можете ознакомиться как этот комбобокс сохраняет и заполняет значения из БД — https://habrahabr.ru/post/328670/
Грубо говоря я написал свою замену QDataWidgetMapper
Вот вы заполняете комбобокс пятью строчками кода плюс незнаю что там в модели. А я это делаю одной строкой. Когда комбобоксов от 10-и в программе. Есть над чем задуматься.

Я могу заполнять и одной строчкой кода, если все остальное используется по умолчанию. А модель самая обычная, самое главное чтобы там были столбцы типа int для ключа БД и QString для отображения.
А если таких виджетов много, то можно и фабрику под это дело использовать. Это уже вопрос уровня абстракций.

QDataWidgetMapper и должен работать с тем, что показывается пользователю. Никто же не делает несколько пунктов с одинаковым содержимым 'Пункт1, Пункт1', или я смысл не уловил…
И он у меня работает и глюков пока не замечал.
Я могу заполнять и одной строчкой кода, если все остальное используется по умолчанию. А модель самая обычная, самое главное чтобы там были столбцы типа int для ключа БД и QString для отображения.
А если таких виджетов много, то можно и фабрику под это дело использовать. Это уже вопрос уровня абстракций.

Насколько я понимаю вы сделали свою реализацию QComboBox — BaseKeyComboBox. Он использует простую модель типа QSqlQueryModel. Я использую простой QComboBox. Но сделал для него особенную модель из QSqlQueryModel.
В обоих случаях нам надо добавлять в проект два лишних файла. С примерно одинаковым количеством кода. И в обоих случаях это даёт заполнение QComboBox данными одной строчкой кода.
Теперь о том чего нельзя сделать в вашем варианте. Если посмотреть шире то модель она не только для данных. Через наследника модели можно определить всплывающую подсказку(tooltip), шрифт пункта и ещё много чего перечисленного в enum ItemDataRole (qnamespace.h).
В частности, хотел в своё время запрашивать из базы три столбца вместо двух. Что бы третий выводился как ToolTipRole первого столбца модели. Именно поэтому у меня название параметра columns в BaseComboModel.
QDataWidgetMapper и должен работать с тем, что показывается пользователю. Никто же не делает несколько пунктов с одинаковым содержимым 'Пункт1, Пункт1', или я смысл не уловил…

Может я не так объяснил. Попробую по другому.
Ладно, пусть это будет делом вкуса, так сказать.
Но…
То без изменения QSqlQueryModel, в качестве модели комбобокса, выдаст просто список «id». А человекочитаемое значение не будет видно

Это же явное заблуждение, которое и привело вас к созданию собственной модели.
Есть же void setModelColumn(int visibleColumn), которое и задает какой столбец выводить пользователю.
(продолжение)
Кода вы работаете с комбобоксоом. Вам важнее какой вторичный ключ записать в базу. А не какое название выбрал юзер на экране. Допустим комбобокс выбора стран. Пользователь может выбрать там «Russia», но вам ведь не название страны надо записывать в базу в результате. А «id» этой записи в cтолбец «countryid». И в моём случае я легко получаю этот «id» из UserRole. QDataWidgetMapper не может менять именно вторичные ключи в базе.
А вот для этого у меня есть
Q_PROPERTY(int currentKey READ currentKey WRITE setCurrentKey NOTIFY currentKeyChanged USER true)

которое и позволяет QDataWidgetMapper выполнять свою работу.

И, да… У меня записывается в базу именно id
В первом примере кода это видно
mapper->addMapping(comboBox1, mainModel->fieldIndex("referemceid"));

В принципе вы можете найти полную реализацию моего кода, она очень простая.
Полностью согласен. Cохранять данные через QDataWidgetMapper гораздо красивее. Ваш вариант с Q_PROPERTY конечно хорош.
Если ориентироваться на QDataWidgetMapper, можно ещё задать ему делегата. Который, внезапно, отвечает как раз за то чтобы взять значение у произвольного виджета и превратить его в QVariant().
Просто у меня QDataWidgetMapper не завёлся. И я подумал что очередные пляски с бубном как с QSqlTableModel мне не нужны.
И я подумал что очередные пляски с бубном как с QSqlTableModel мне не нужны.

Это вы про
QSqlTableModel может сохранять данные в базу. Странно что на форме раздельное сохранение данных для таблицы и простых полей. QSqlTableModel иногда работает с ошибками. Потом, у него немного ограниченный функционал. Поэтому вы можете плюнуть и воспользоваться QSqlQueryModel. Который уже ничего не сохраняет, но и ограничений у него нет. В этом случае таблица будет только для чтения.
?

Если да, то я опять же написал EditSqlQueryModel
С которой можно делать так…
contentModel = new EditSqlQueryModel(db, this);
        contentModel->setEditStrategy(EditSqlQueryModel::OnManualSubmit);
        contentModel->showDeleted(false);
        contentModel->setQuery("SELECT "
                               "a.recordid, "
                               "a.productionid, "
                               "a.productionclassid, "
                               "d.name as productionclassname, "
                               "FROM foamcontourproductioncontent a "
                               "left join productionclass as d on a.productionclassid = d.recordid "
                               "where a.foamcontourproductionid = " + QString::number(m_recordId) + " "
                               "order by a.recordid;");

        contentModel->setTable("foam.productioncontent");
        contentModel->init();

        EditSqlQueryModel::FieldMap fMap;
        QSqlRecord rec;
        rec = contentModel->record();
        fMap.insert("recordid", rec.field("recordid"));
        fMap.insert("productionid", rec.field("productionid"));

        contentModel->setUpdatableFields(fMap);

Такая реализация по мотивам Visual FoxPro.
Она правда имеет мало функционала по сравнению с CursorAdapter из VFP, но мне хватает.

Вы по-моему не с той стороны подошли к проблеме.
Нет, тут немного о другом. Я написал велосипед для обмена данными формы с базой. И для пущей наглядности усложнил его таблицей слева.
Поскольку решение это примерное. Демонстрация подхода. Который можно применить на любых формах. Т.е. и на формах без левой таблицы. А раз так, мне надо заполнять таблицу и форму отдельно.
Моя нота выше. Про то что программист может по своему желанию сменить модель для левой таблицы на другую. Вдруг заглючит, что бывало. Или функционал окажется недостаточным. Или она вообще будет не нужна.

Судя по вашему коду. Вы написали примерно такой же велосипед как у меня в той статье.
«Пляски с бубном» вокруг QSqlTableModel у меня были на другом проекте, в продакшене. Здесь просто констатация факта что иногда глючит. Поэтому надо оставлять место для запасных вариантов типа «QSqlQueryModel».
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации