Как выровнять qcombobox
Перейти к содержимому

Как выровнять qcombobox

Как выровнять qcombobox

Просмотр профиля

10.11.2011, 20:14

Группа: Новичок
Сообщений: 4
Регистрация: 31.7.2011
Пользователь №: 2775

Репутация: 0

Подскажите, пожалуйста, как выравнить текущий текст, отображаемый в комбобоксе, чтобы он отображался не по центру слева, а снизу и по центру?

опции профиля:

Просмотр профиля

11.11.2011, 0:16

Группа: Участник
Сообщений: 297
Регистрация: 23.6.2011
Пользователь №: 2765

Репутация: 3

Переопределить paintEvent или поставить стиль типа такого:

QComboBox padding: Y 0 0 X; 
>

X — это сколько вы хотите отступить от левого края (примерно равно comboBox->width()/2 — длина надписи в пикселях/2)
y — это сколько вы хотите отступить сверху (примерное равно comboBox->height() — сколько вы хотите отступить снизу)

Создание моделей данных для QComboBox

Всем привет! Хочу поделиться с вами двумя способами, как можно и нужно создавать модели данных для виджетов типа QComboBox в Qt. В конце статьи будет показано решение, для заполнения комбобокса из БД, одной строкой кода.

Способ №1. Полностью ручное создание модели

Все модели данных в Qt должны быть наследниками от QAbstractItemModel. Лично в моей практике комбобоксы всегда отображали перечисление из SQL базы данных. Это были пол, страна, национальность и некоторые другие списки, из которых пользователю нужно было выбрать один пункт. Поэтому, при создании модели, у меня всегда было две параллельных задачи:

  1. Как сформировать человекочитаемые названия пунктов пользователю?
  2. Как связать читаемые пункты с ключами, которые надо писать в БД?

Для реализации замысла. Первым делом надо посмотреть какие из методов QAbstractItemModel, вы обязаны определить у себя:

  • QModelIndex QAbstractItemModel::index(int row, int column, const QModelIndex & parent = QModelIndex()) const
  • QModelIndex QAbstractItemModel::parent(const QModelIndex & index) const
  • int QAbstractItemModel::columnCount(const QModelIndex & parent = QModelIndex()) const
  • int QAbstractItemModel::rowCount(const QModelIndex & parent = QModelIndex()) const
  • QVariant QAbstractItemModel::data(const QModelIndex & index, int role = Qt::DisplayRole) const

Таким образом, для комбобокса выбора национальности. Получается следующая реализация модели:

// nationalitymodel.h // #pragma once #include class NationalityModel : public QAbstractListModel < Q_OBJECT typedef QPairDataPair; QList < DataPair >m_content; public: explicit NationalityModel( QObject *parent = 0 ); virtual QVariant data( const QModelIndex & index, int role = Qt::DisplayRole ) const; virtual int rowCount( const QModelIndex & parent = QModelIndex() ) const; >; // nationalitymodel.cpp #include "nationalitymodel.h" NationalityModel::NationalityModel(QObject *parent) : QAbstractListModel(parent) < m_content QVariant NationalityModel::data( const QModelIndex &index, int role ) const < const DataPair& data = m_content.at( index.row() ); QVariant value; switch ( role ) < case Qt::DisplayRole: < value = data.first; >break; case Qt::UserRole: < value = data.second; >break; default: break; > return value; > int NationalityModel::rowCount(const QModelIndex &/*parent*/) const < return m_content.count(); >// addressbookmainwindow.cpp. В конструкторе формы, где будет использоваться модель ( AddressBookMainWindow::AddressBookMainWindow() ) ui->nationalityCombo->setModel( new NationalityModel( this ) ); 

Все значения пунктов комбобокса, просто записываються в QList< DataPair &gt m_content;. И потом выдаются при обращении комбобокса к функции NationalityModel::data(). Начинающим важно понимать. Не программист явно вызывает эту функцию в своём коде. А комбобокс обращается к данной функции когда ему надо! Ваша задача, что бы функция отдавала эти актуальные данные по запросу.

NationalityModel::data() вызывается с двумя параметрами. Как того требует простотип QAbstractItemModel::data():

  • const QModelIndex &index. Объект содержащий номер строки, колонки и ссылку на родительский QModelIndex. Т.е. QComboBox сообщает место(позицию) пункта, для которого запрашиваются данные. В нашем случае актуален только номер строки. Остальные параметры внутри &index только для совместимости с другими моделями, типа QTreeView и QTableView. Поэтому наша функция запрашивает пару, «читаемое» и «служебное» значения (DataPair) только для данной строки. Хранящиеся в списке возможных значений (m_content).
  • int role. В этом параметре QComboBox сообщает, какого рода данные нужны(какая роль). В нашем случае «читаемое» значение это Qt::DisplayRole, а «служебное» Qt::UserRole.

За один вызов NationalityModel::data() возвращаются данные одной роли для одной, конкретной строки в списке.

Если обратится к enum ItemDataRole, где определены Qt::DisplayRole, Qt::UserRole. Станет понятно для чего ещё можно реализовать такую модель. Например, поменять шрифт некоторых пунктов (Qt::FontRole). Выровнять текст пункта меню, как то по особенному. Или задать текст всплывающей подсказки. Смотрите в упомянутый enum. Возможно вы найдёте там то что искали уже давно.

Исходный код примера

Возможно вам будет интересно изучить этот код в работе. Для этих целей был создана реализация небольшой адресной книги.

Как скачать код с github.com

  1. Скачайте проект «git clone https://github.com/stanislav888/AddressBook.git»
  2. Меняете текущий каталог «cd AddressBook»
  3. Инициализируйте подмодуль «git submodule init»
  4. Подгружаете код подмодуля в проект «git submodule update»
  5. Открываете и собираете проект
  6. Запускаете программу
  7. Если всё хорошо, появиться окно выбора\создания файла базы данных. Можете посмотреть что за программа. Для заполнения тестовыми данными есть кнопочка «Fill test data»

Способ №2. Быстрое создание модели из перечисления в SQL БД

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

Для реализации нам потребуется QSqlQueryModel. Это похожая модель. Она тоже наследник QAbstractItemModel, но используется для отображения результатов SQL запроса QSqlQuery в таблице QTableView. В данном случае, наша задача приспособить данный класс. Что бы он давал данные так же как в первом примере.

Вы удивитесь, но код получился небольшим.

// addressdialog.h /// #pragma once #include class BaseComboModel : public QSqlQueryModel < Q_OBJECT QVariant dataFromParent(QModelIndex index, int column) const; public: explicit BaseComboModel( const QString &columns, const QString &queryTail, QObject *parent = 0 ); virtual QVariant data(const QModelIndex &item, int role = Qt::DisplayRole) const; virtual int rowCount(const QModelIndex &parent) const; >; // basecombomodel.cpp #include "basecombomodel.h" #include namespace < enum Columns // Depends with 'query.prepare( QString( "SELECT . ' < Id, Data, >; > BaseComboModel::BaseComboModel( const QString& visualColumn, const QString& queryTail, QObject *parent ) : QSqlQueryModel( parent ) < QSqlQuery query; query.prepare( QString( "SELECT %1.id, %2 FROM %3" ).arg( queryTail.split( ' ' ).first() ).arg( visualColumn ).arg( queryTail ) ); // I.e. query.prepare( "SELECT country.id, countryname || ' - ' || countrycode FROM country" ); query.exec(); QSqlQueryModel::setQuery( query ); >QVariant BaseComboModel::dataFromParent( QModelIndex index, int column ) const < return QSqlQueryModel::data( QSqlQueryModel::index( index.row() - 1 // "- 1" because make first row empty , column ) ); >int BaseComboModel::rowCount(const QModelIndex &parent) const < return QSqlQueryModel::rowCount( parent ) + 1; // Add info about first empty row >QVariant BaseComboModel::data(const QModelIndex & item, int role /* = Qt::DisplayRole */) const < QVariant result; if( item.row() == 0 ) // Make first row empty < switch( role ) < case Qt::UserRole: result = 0; break; case Qt::DisplayRole: result = "(please select)"; break; default: break; >> else < switch( role ) < case Qt::UserRole: result = dataFromParent( item, Id ); break; case Qt::DisplayRole: result = dataFromParent( item, Data ); break; default: break; >> return result; > // Использование модели в форме(addressdialog.ui) выглядит примерно так ui->countryCombo->setModel( new BaseComboModel( "countryname || ' - ' || countrycode", "country", this ) ); 

В данной реализации, всю работу делает QSqlQueryModel. Надо только немного переопределить логику QSqlQueryModel::data(). Для начала представьте, что в модель записывается SQL запрос «SELECT country.id, countryname || ‘ — ‘ || countrycode FROM country».

Конечно в коде проекта это немного более замысловато. Но если отладить там будет сформирована именно такая строка. Запрос выводит два столбца. Первичный ключ(«id»). И человекочитаемое значние, видимое на скриншоте. Поскольку все результаты SQL запроса оказываются в Qt::DisplayRole у QSqlQueryModel. То без изменения QSqlQueryModel, в качестве модели комбобокса, выдаст просто список «id». А человекочитаемое значение не будет видно. Т.к. комбобокс никак не использует второй столбец модели(запроса). Вы это увидите, если закомментируете объявление и реализацию BaseComboModel::data().

Для того что бы увидеть список стран, как на скриншоте, BaseComboModel::data():

  • возвращает данные первого запрошенного столбца(«id») как Qt::UserRole первого столбца
  • возвращает данные второго столбца(«countryname || ‘ — ‘ || countrycode») как Qt::DisplayRole первого столбца
  • добавляет строку «(please select)» в самом начале. За счёт смещения номеров при запросе данных от QSqlQueryModel. Т.е. к результатам SQL запроса, модель сама добавляет ещё одну строку

Таким образом вы можете быстро и легко делать модели для QComboBox-а с помощью BaseComboModel. Например, у вас есть SQL таблица месяцев в году(«months»). Где два столбца, «id» и «monthname». Вам можно заполнить комбобокс выбора месяца следующим образом:

ui->monthsCombo->setModel( new BaseComboModel( «monthname», «months», this ) );
Получить значение «id» выбранного месяца ui->monthsCombo->itemData( ui->monthsCombo->currentIndex(), Qt::UserRole );. Получть значение видимое пользователю ui->monthsCombo->currentText();. Этот код гораздо компактнее всех остальных случаев. Большинство разработчиков в данной ситуации пишут, отдельно запрос к базе (QSqlQuery). А потом, в цикле, добавляют полученные записи в комбобокс, через QComboBox::addItem(). Это конечно рабочее, но не самое красивое решение.

Практика

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

Два варианта как это сделать:

    Эксперименты на основе моего приложения — адресной книги, упомянутой выше. Заголовок и реализация BaseComboModel уже присутствуют в проекте. Примеры ниже, будут на её основе.

    const QString &columns. Формирование человекочитаемого названия пункта для пользователя. В примере выше «countryname || ‘ — ‘ || countrycode» применяется конкатенация двух столбцов через дефис. Оператор конкатенации «||» специфичный для SQLite. Можно указать несколько столбцов через запятую. Но показываться будет только первый.

Далее, надо добавить на форму QComboBox с которым вы будете экспериментировать. В моём случае это будет addressbookmainwindow.ui. Имя нового виджета ui->comboBox

Теперь будем заполнять этот комбобокс разными способами

ui->comboBox->setModel( new BaseComboModel( «countryname», «country», this ) );
«SELECT country.id, countryname FROM country».
Просто список стран
ui->comboBox->setModel( new BaseComboModel( «countryname», «country WHERE countrycode IN ( ‘US’, ‘RU’, ‘CN’ )», this ) );
«SELECT country.id, countryname FROM country WHERE countrycode IN ( „US“, „RU“, „CN“ )».
Некоторые страны выбранные по коду.
ui->comboBox->setModel( new BaseComboModel( «lastname», «persons», this ) );
«SELECT persons.id,, lastname FROM persons».
Список фамилий записанных в базе. Что бы они были, надо кликнуть кнопку «Fill test data»
ui->comboBox->setModel( new BaseComboModel( «lastname || ‘ — ‘ || email», «persons LEFT JOIN address AS a ON a.id = persons.addressid», this ) );
«SELECT persons.id, lastname || ‘ — ‘ || email FROM persons LEFT JOIN address AS a ON a.id = persons.addressid».
Список фамилий с email aдресами. Не забывайте что «||» оператор конкатенации строк только в SQLite. Для других баз понадобится переделать конкатенацию
ui->comboBox->setModel( new BaseComboModel( «lastname || ‘ — ‘ || countryname», «persons INNER JOIN address AS a ON a.id = persons.addressid INNER JOIN country AS c ON a.countryid = c.id», this ) );
«SELECT persons.id, lastname || ‘ — ‘ || countryname FROM persons INNER JOIN address AS a ON a.id = persons.addressid INNER JOIN country AS c ON a.countryid = c.id».
Список фамилий с соответствующими странами

Конечно, все эти фокусы с «JOIN» и «WHERE» выглядят интересно. Но в большинстве случаев не нужны. Поэтому и было решено использовать два параметра в конструкторе. Вместо того чтобы подавать туда SQL запрос целиком. Если вы храните все перечисления в одной таблице. И разделяете эти перечисления по какому-то дополнительному ключу. Лучше сделать третий параметр, со значением этого ключа. Вместо того чтобы использовать каждый раз «WHERE».

Повторюсь, как получить «id» выбранной записи
ui->comboBox->itemData( ui->comboBox->currentIndex(), Qt::UserRole );

Заключение

Надеюсь, несмотря на сложность кода. Вы извлекли что то полезное для себя. Если хотите узнать больше о приложении «AddressBook» приведённом здесь ради примера. Смотрите статью «Автоматизация обмена данными Qt форм с SQL базой данных».

QCombobox text aligment

Привет! Подскажите, как текст в QCombobox поставить по центру. В гугле основной вариант предлагают следующий: QComboBox comboBox; comboBox.setEditable(true); comboBox.lineEdit()->setReadOnly(true); comboBox.lineEdit()->setAlignment(Qt::AlignCenter); comboBox.addItem(«Nemesis»); comboBox.addItem(«Erebus»); comboBox.addItem(«Nix»); for (int i = 0 ; i < comboBox.count() ; ++i) < comboBox.setItemData(i, Qt::AlignCenter, Qt::TextAlignmentRole); но comboBox.lineEdit()->setReadOnly(true); и comboBox.lineEdit()->setAlignment(Qt::AlignCenter); не могут выполнится т.к. у lineEdit() таких методов нет. Подскажите, как быть? P.S Qt 5.6

Отслеживать
задан 13 окт 2016 в 12:08
473 2 2 серебряных знака 15 15 бронзовых знаков

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

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

Отслеживать
11.7k 9 9 золотых знаков 28 28 серебряных знаков 40 40 бронзовых знаков
ответ дан 13 окт 2016 в 12:21
Vladimir Pavluk Vladimir Pavluk
460 2 2 серебряных знака 6 6 бронзовых знаков
спасибо, помогло!
13 окт 2016 в 12:35

  • c++
  • qt
  • qtcreator
    Важное на Мете
Похожие

Подписаться на ленту

Лента вопроса

Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.

Дизайн сайта / логотип © 2023 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2023.11.29.1725

Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.

Как выровнять qcombobox

Argument ‘Topic id’ is null or empty

Сейчас на форуме

© Николай Павлов, Planetaexcel, 2006-2023
info@planetaexcel.ru

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

ООО «Планета Эксел»
ИНН 7735603520
ОГРН 1147746834949
ИП Павлов Николай Владимирович
ИНН 633015842586
ОГРНИП 310633031600071

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *