Название: QSqlRelationTableModel для одного столбца вывести несколько связанных значений
Отправлено: break от Май 09, 2009, 17:16
хочу для одного столбца вывести несколько связанных значений, то есть
таблица ПОЕЗДКИ (TRAVELS) имеет вторичный ключ на таблицу ВОДИТЕЛИ (DRIVERS)
в таблице ВОДИТЕЛИ есть поля ИМЯ, ФАМИЛИЯ, ОТЧЕСТВО
Хочется выводить окошко с таблицей поездок так чтобы выводились и ИМЯ, ФАМИЛИЯ, ОТЧЕСТВО в соответствие со столбцом DRIVER_ID из TRAVELS
QSqlRelationTableModel позволяет установить соответствие только одному столбцу, заводить в БД втаблице TRAVELS еще поля дублирующие DRIVER_ID кажется глупым - т.к. перестраивать БД из-за нужд интерфейса что-то не то. Слепливать все поля в одно при выводе ИМЯ + ФАМИЛИЯ + ОТЧЕСТВО - тоже не хочется т.к. надо сортировать и фильтровать отдельно по имени и фамилии.
Если бы было можно создать псевдостолбцы на основе QSqlRelationTableModel с соответствие DRIVER_ID но выбором конкретного поля из DRIVERS.
какие могут быть варианты?
раньше в DELPHI мог сделать простой запрос:
select t.*, d.* from TRAVELS t, DRIVERS d where (t.DRIVER_ID = d.ROW_ID)
и получить желаемое
Название: Re: QSqlRelationTableModel для одного столбца вывести несколько связанных значений
Отправлено: ритт от Май 09, 2009, 17:29
QSqlRelationTableModel не подойдёт.
select t.*, d.* from TRAVELS t, DRIVERS d where (t.DRIVER_ID = d.ROW_ID) столбцов сколько?)
Название: Re: QSqlRelationTableModel для одного столбца вывести несколько связанных значений
Отправлено: break от Май 10, 2009, 00:06
Цель не бесконечное количество столбцов - например я мог бы указать так
select t.START_DATE_TIME, t.DESCRIPT, d.NAME, d.SURNAME, d.PATRONIMYC, d.B_DATE from TRAVELS t, DRIVERS d where (t.DRIVER_ID = d.ROW_ID)
Название: Re: QSqlRelationTableModel для одного столбца вывести несколько связанных значений
Отправлено: ритт от Май 10, 2009, 03:57
видимо, я чего-то не понял... чем плох последний запрос? и почему нужна именно QSqlRelationTableModel?
Название: Re: QSqlRelationTableModel для одного столбца вывести несколько связанных значений
Отправлено: break от Май 11, 2009, 13:10
QSqlRelationModel нужен т.к. я написал небольшой класс "справочник" - представляющий из себя форму с табличкой (TableView) сделал это для того чтобы во многих местах программы использовать один интерфейс для пльзователя и собственно не переписывать один и тот же код. Справочник может работать в режимер редактирования и в режиме выбора записей, сразу встроен фильтр по столбцам - во общем н важно... (http://pic.ipicture.ru/uploads/090511/uIPYUGgYYJ.png) Когдато то аналогичное делал и в делфи, но тамошние компоненты позволяли задать не имя таблицы а запросы SelectSQL, UpdateSQL и т.д. - тоесть полная свобода в плане псевдостолбцов. Пока вижу единственным решением писать свою модель с поддержкой аналогичных возможностей (это и планировал), но сейчас времени нет на это, поэтому решил узнать как другие с такой ситуацией борятся - может есть более простой путь или кто-то уже писал модель похожую (в плане задания SQL запросов) на всякие датасеты из делфи - типа FIBSQLDataset и т.д. h файл: #ifndef __DRIVERS_DICT_H__ #define __DRIVERS_DICT_H__
#include <QtGui> #include <QtSql> #include "ui_TableDict.h"
#define ROW_ID_FIELD_NAME "ROW_ID"
struct CColumnProps { QString sDisplayName; bool bVisible; bool bReadOnly; QVariant vDefaultValue;
CColumnProps() {} CColumnProps( QString displayName, bool visible, bool readOnly, QVariant defaultValue ) { sDisplayName = displayName; bVisible = visible; bReadOnly = readOnly; vDefaultValue = defaultValue; } };
class CColumnFilter : public QSortFilterProxyModel { QVector<QString> m_filterValues; public: void setExpCount( int nVal ) { m_filterValues.resize( nVal ); } void setColumnExp( int column, QString exp ) { m_filterValues[ column ] = exp; } QVariant data( const QModelIndex &index, int role ) const;
protected: virtual bool filterAcceptsRow ( int source_row, const QModelIndex & source_parent ) const; };
class CTableDict : public QDialog { Q_OBJECT; Ui::TableDict m_ui; // User interface CColumnFilter m_mdlColFilter; // Модель содержащая условия фильтра по колонкам QSqlRelationalTableModel m_mdlData; // Главная модель данных QList<int> m_readOnlyRows; QString m_sDictName;
bool m_bCanAddRow; bool m_bCanDelRow; bool m_bEditMode_Allowed; bool m_bSelectMode_Allowed;
int m_nFirstVisibleColumn;
QHash<QString, CColumnProps> m_columnProps;
QAbstractItemView::EditTriggers m_enabledEditTriggers; QAbstractItemView::EditTriggers m_disabledEditTriggers;
public: enum DictWorkMode { dwmSelect, dwmEdit,
dwmCount };
// Construction/destruction CTableDict( QWidget * parent = 0 ); virtual ~CTableDict(); void refreshData(); inline QHash<QString, CColumnProps>& columnProps() { return m_columnProps; } inline QList<int>& readOnlyRows() { return m_readOnlyRows; }
inline void setTableName( QString sVal ) { m_mdlData.setTable( sVal ); } void setDictName( QString sVal ); void setWorkMode( DictWorkMode mode );
inline void setCanAddRow ( bool bVal ) { m_bCanAddRow = bVal; } inline void setCanDelRow ( bool bVal ) { m_bCanDelRow = bVal; } inline void setEditMode_Allowed ( bool bVal ) { m_bEditMode_Allowed = bVal; } inline void setSelectMode_Allowed ( bool bVal ) { m_bSelectMode_Allowed = bVal; }
inline QSqlRelationalTableModel& dataModel() { return m_mdlData; } private slots: void colFilter_currentColumnChanged ( const QModelIndex & current, const QModelIndex & previous ); void data_currentChanged ( const QModelIndex & current, const QModelIndex & previous ); void data_columnResized( int logicalIndex, int oldSize, int newSize );
void on_tvColumnFilter_cellChanged ( int row, int column ); void on_tvData_doubleClicked ( const QModelIndex & index ); void on_btnRefresh_pressed(); void on_btnAddRow_pressed(); void on_btnDelRow_pressed(); void on_btnSelect_pressed(); void on_btnCancel_pressed(); void on_btnCommit_pressed(); void on_btnRollback_pressed();
void on_btnChangeWorkMode_pressed();
protected: virtual void keyPressEvent(QKeyEvent * event); virtual void showEvent ( QShowEvent * event );
private: DictWorkMode m_workMode; QString dwmToDispString( DictWorkMode mode ); void updateColumnFilter_LeftColumn(); void returnCurrent_RowID(); };
#endif // __DRIVERS_DICT_H__
cpp файл: #include "TableDict.h" #include <QSqlError>
// TODO: Написать делегат для столбцов с датами // TODO: Написать делегат для столбцов с timestamp // TODO: Вывести значет восклицательного знака когда данные изменилсь - спросить при выходе
QVariant CColumnFilter::data( const QModelIndex &index, int role ) const { if ( role == Qt::BackgroundRole ) { // подсвечиваем фон удаленных записей красным цветом if ( headerData( index.row(), Qt::Vertical ) == QLatin1String("!") ) return QColor( Qt::red ); };
return QSortFilterProxyModel::data( index, role ); }
bool CColumnFilter::filterAcceptsRow( int source_row, const QModelIndex & source_parent ) const { QString exp; QModelIndex index; for ( int i = 0; i < m_filterValues.count(); ++i ) { exp = m_filterValues[i]; index = sourceModel()->index( source_row, i, source_parent ); if ( !sourceModel()->data( index ).toString().contains( exp, Qt::CaseInsensitive ) ) return false; }; return true; }
CTableDict::CTableDict( QWidget * parent ) : QDialog( parent ), m_mdlData( 0, QSqlDatabase::database( QLatin1String(QSqlDatabase::defaultConnection), false ) ) { //Q_INIT_RESOURCE( SD_Images );
m_ui.setupUi( this );
m_bCanAddRow = false; m_bCanDelRow = false; m_bEditMode_Allowed = true; m_bSelectMode_Allowed = true;
m_nFirstVisibleColumn = -1;
m_ui.tvData->setModel( &m_mdlColFilter ); QObject::connect( m_ui.tvData->selectionModel(), SIGNAL(currentChanged ( const QModelIndex &, const QModelIndex & )), this, SLOT(data_currentChanged ( const QModelIndex &, const QModelIndex & )) );
QObject::connect( m_ui.tvData->horizontalHeader(), SIGNAL(sectionResized ( int, int, int )), this, SLOT(data_columnResized ( int, int, int )) );
QObject::connect( m_ui.tvColumnFilter->selectionModel(), SIGNAL(currentColumnChanged ( const QModelIndex &, const QModelIndex & )), this, SLOT(colFilter_currentColumnChanged ( const QModelIndex &, const QModelIndex & )) );
m_mdlData.setEditStrategy( QSqlTableModel::OnManualSubmit ); m_mdlColFilter.setDynamicSortFilter( true ); m_mdlColFilter.setSourceModel( &m_mdlData ); m_ui.tvColumnFilter->horizontalHeader()->setVisible( false ); m_ui.tvColumnFilter->verticalHeader()->setVisible( false );
m_enabledEditTriggers = QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed | QAbstractItemView::AnyKeyPressed | QAbstractItemView::SelectedClicked;
m_disabledEditTriggers = QAbstractItemView::NoEditTriggers;
setWorkMode( dwmEdit ); }
CTableDict::~CTableDict() { //Q_CLEANUP_RESOURCE(SD_Images); }
void CTableDict::refreshData() { m_mdlData.select();
// для таблицы с фильром устанавливаем на 1 столбец больше, т.к. 1 столбец используется // для выравнивания относительно вертикального хидера таблицы данных m_ui.tvColumnFilter->setColumnCount( m_mdlData.columnCount()+1 ); // установка числа столбцов-выражений по которым будет фильтрация m_mdlColFilter.setExpCount( m_mdlData.columnCount() );
QSqlRecord rec = m_mdlData.record();
m_nFirstVisibleColumn = rec.count()-1; for ( int i = 0; i < rec.count(); ++i ) { QString sFieldName = rec.field( i ).name();
// если для этой колонки были заданы параметры if ( m_columnProps.contains( sFieldName ) ) { CColumnProps colProps = m_columnProps.value( sFieldName );
m_ui.tvData -> setColumnHidden( i, !colProps.bVisible ); m_ui.tvColumnFilter -> setColumnHidden( i+1, !colProps.bVisible );
// setDefaultValue пока использовать не получилось, вставляю значения по умолчанию в addRow rec.field( i ).setDefaultValue( colProps.vDefaultValue );
// в Qt::DisplayRole названия колонок для вывода на экран m_mdlData.setHeaderData( i, Qt::Horizontal, QVariant( colProps.sDisplayName ), Qt::DisplayRole );
// определение индекса первого видимого столбца if ( colProps.bVisible ) if ( m_nFirstVisibleColumn > i ) m_nFirstVisibleColumn = i; }; };
m_ui.tvData->resizeColumnsToContents(); m_ui.tvData->horizontalHeader()->setStretchLastSection( true );
m_ui.tvData->setFocus(); if ( m_workMode == dwmSelect ) m_ui.tvData->selectRow( 0 ); else m_ui.tvData->setCurrentIndex( m_mdlColFilter.index(0, m_nFirstVisibleColumn) );
m_mdlColFilter.invalidate(); //// странный глюк раньше работало без этого!!! ( до SDK 2009.02 ) data_currentChanged ( QModelIndex(), QModelIndex() ); }
void CTableDict::setDictName( QString sVal ) { m_sDictName = sVal; }
void CTableDict::setWorkMode( DictWorkMode mode ) { m_workMode = mode;
QString sTitle = QString::fromUtf8("Справочник: ") + m_sDictName; QString sMode = dwmToDispString( m_workMode );
sTitle = sTitle + " (" + sMode + ")"; setWindowTitle( sTitle );
m_ui.btnSelect -> setVisible( m_workMode == dwmSelect ); m_ui.btnCancel -> setVisible( m_workMode == dwmSelect );
m_ui.btnCommit -> setVisible( m_workMode == dwmEdit ); m_ui.btnRollback -> setVisible( m_workMode == dwmEdit );
QString styleSheet; if ( m_workMode == dwmSelect ) { styleSheet = QString("QPushButton { background-image: url(:/images/acSelect_Mode.png) }"); m_ui.btnChangeWorkMode->setStyleSheet( styleSheet ); m_ui.tvData->setSelectionBehavior( QAbstractItemView::SelectRows ); } else { styleSheet = QString("QPushButton { background-image: url(:/images/acEdit_Mode.png) }"); m_ui.btnChangeWorkMode->setStyleSheet( styleSheet ); m_ui.tvData->setSelectionBehavior( QAbstractItemView::SelectItems ); }; }
void CTableDict::colFilter_currentColumnChanged ( const QModelIndex & current, const QModelIndex & previous ) { Q_UNUSED( previous );
// не даем редактировать нулевую колонку фильтра предназначенную для выравнивания относительно таблицы данных if ( current.column() == 0 ) m_ui.tvColumnFilter->setEditTriggers( m_disabledEditTriggers ); else m_ui.tvColumnFilter->setEditTriggers( m_enabledEditTriggers ); }
void CTableDict::data_currentChanged ( const QModelIndex & current, const QModelIndex & previous ) { Q_UNUSED( previous );
bool bSelectMode = ( m_workMode == dwmSelect );
if ( bSelectMode ) { m_ui.tvData->setEditTriggers( m_disabledEditTriggers ); m_ui.btnAddRow->setEnabled( false ); m_ui.btnDelRow->setEnabled( false ); return; };
QModelIndex _current = m_mdlColFilter.mapToSource( current );
QSqlRecord rec = m_mdlData.record( _current.row() ); int row_id = rec.field( ROW_ID_FIELD_NAME ).value().toInt();
bool bReadOnlyRow = ( m_readOnlyRows.indexOf( row_id ) != -1 );
m_ui.btnAddRow->setEnabled( m_bCanAddRow ); m_ui.btnDelRow->setEnabled( m_bCanDelRow && (bReadOnlyRow==false) );
if ( bReadOnlyRow ) { m_ui.tvData->setEditTriggers( m_disabledEditTriggers ); return; };
bool bReadOnlyColumn = false;
QString sFieldName = rec.field( _current.column() ).name(); if ( m_columnProps.contains( sFieldName ) ) { CColumnProps colProps = m_columnProps[ sFieldName ]; bReadOnlyColumn = colProps.bReadOnly; };
if ( bReadOnlyColumn ) m_ui.tvData->setEditTriggers( m_disabledEditTriggers ); else m_ui.tvData->setEditTriggers( m_enabledEditTriggers ); }
void CTableDict::data_columnResized( int logicalIndex, int oldSize, int newSize ) { Q_UNUSED( oldSize );
updateColumnFilter_LeftColumn();
// при изменении ширины колонок таблицы - меняем также ширину колонок фильтра // logicalIndex + 1 -- т.к. всегда присутствует колонка 0 для выравнивания относительно таблицы данных m_ui.tvColumnFilter->setColumnWidth( logicalIndex + 1, newSize ); }
void CTableDict::on_tvColumnFilter_cellChanged ( int row, int column ) { QString exp = m_ui.tvColumnFilter->item( row, column )->data( Qt::DisplayRole ).toString(); m_mdlColFilter.setColumnExp( column-1, exp ); m_mdlColFilter.invalidate(); }
void CTableDict::on_tvData_doubleClicked ( const QModelIndex & index ) { returnCurrent_RowID(); }
void CTableDict::on_btnRefresh_pressed() { refreshData(); }
void CTableDict::on_btnAddRow_pressed() { // перед вставкой записи необходимо отминить фильтрацию записей, иначе ее может быть не видно for ( int i = 0; i < m_ui.tvColumnFilter->columnCount(); ++i ) { QTableWidgetItem * pItem = m_ui.tvColumnFilter->item( 0, i ); if ( pItem ) pItem->setData( Qt::DisplayRole, QVariant() ); }; m_mdlColFilter.invalidate();
int row = m_mdlColFilter.rowCount(); m_mdlColFilter.insertRows(row, 1);
// вставка значений по умолчанию в нововставленную запись QSqlRecord rec = m_mdlData.record(); for ( int i = 0; i < rec.count(); ++i ) { QString sFieldName = rec.field( i ).name(); // если для этой колонки были заданы параметры if ( m_columnProps.contains( sFieldName ) ) { CColumnProps colProps = m_columnProps.value( sFieldName ); m_mdlColFilter.setData( m_mdlColFilter.index(row, i), colProps.vDefaultValue ); }; };
m_ui.tvData->setFocus(); m_ui.tvData->setCurrentIndex( m_mdlColFilter.index(row, m_nFirstVisibleColumn) ); }
void CTableDict::on_btnDelRow_pressed() { int row = m_ui.tvData->selectionModel()->currentIndex().row(); m_mdlColFilter.removeRow( row ); m_mdlColFilter.invalidate();
m_ui.tvData->setFocus(); m_ui.tvData->setCurrentIndex( m_mdlColFilter.index(row-1, m_nFirstVisibleColumn) ); }
void CTableDict::on_btnSelect_pressed() { returnCurrent_RowID(); }
void CTableDict::on_btnCancel_pressed() { done( 0); }
void CTableDict::on_btnCommit_pressed() { m_mdlData.submitAll(); refreshData(); }
void CTableDict::on_btnRollback_pressed() { m_mdlData.revertAll(); refreshData(); }
void CTableDict::on_btnChangeWorkMode_pressed() { if ( ( m_workMode == dwmSelect ) && ( m_bEditMode_Allowed ) ) setWorkMode( dwmEdit ); else if ( ( m_workMode == dwmEdit ) && ( m_bSelectMode_Allowed ) ) setWorkMode( dwmSelect );
refreshData(); }
void CTableDict::keyPressEvent(QKeyEvent * event) { // для убирания закрытия по клавишам Enter/Esc if ( ( event->key() == Qt::Key_Escape ) || ( event->key() == Qt::Key_Return ) ) return;
QDialog::keyPressEvent( event ); }
void CTableDict::showEvent ( QShowEvent * event ) { QDialog::showEvent( event );
setFocusProxy( m_ui.tvData ); m_ui.tvData->setFocus();
updateColumnFilter_LeftColumn(); }
QString CTableDict::dwmToDispString( DictWorkMode mode ) { static const QString dwmDispStrings[ dwmCount ] = { QString::fromUtf8( "Режим Выбора" ), QString::fromUtf8( "Режим Редактирования" ) }; return dwmDispStrings[ mode ]; }
void CTableDict::updateColumnFilter_LeftColumn() { // если для таблицы данных виден вертикальный заголовок (с номерами строк), то делаем в таблице фильтра // нулевую колонку соответствующую по ширине этому заголовку if ( m_ui.tvData->verticalHeader()->isVisible() ) m_ui.tvColumnFilter->setColumnWidth( 0, m_ui.tvData->verticalHeader()->width() ); else m_ui.tvColumnFilter->setColumnWidth( 0, 0 ); }
void CTableDict::returnCurrent_RowID() { if ( m_workMode != dwmSelect ) return;
if ( !m_mdlData.rowCount() ) { done( 0 ); return; };
QSqlRecord rec = m_mdlData.record( m_ui.tvData->currentIndex().row() ); QSqlField field = rec.field( ROW_ID_FIELD_NAME );
int row_id = field.value().toInt(); done( row_id ); }
Название: Re: QSqlRelationTableModel для одного столбца вывести несколько связанных значений
Отправлено: mishabard от Июль 07, 2011, 15:37
Здравствуйте break! У меня примерно такая же задача появилась. Вы решили это проблему? Если да то как?
Название: Re: QSqlRelationTableModel для одного столбца вывести несколько связанных значений
Отправлено: RVZ от Июль 13, 2011, 11:00
Вопроса я не понял но как ответ может подойдет. Предлагаю добавить еще одну таблицу с рейсам и процедуру создания рейса Допустим
Водители Маршруты Рейсы -------------- ------------- ------------------------- | v_id | Name | | m_id | Name| | r_id | r_No| v_id | m_id | -------------- ------------- -------------------------
Тогда при отработке конструктора формы оформления рейса (Где r_No уникальный регистрационный номер рейса - который в свою очередь может быть не уникальным в таблице рейсов то можно отправлять хоть по десеть водителей по одному маршруту одним приказом и на нескольких :) или на одной машине) создается запись в таблице рейсов(по средствам хранимой процедуры) тогда можно держать информацию о том какие водители по каким маршрутам ездили при добавлении таблицы Транспортные средства(и столбца с их id к рейсам) еще и на каких машинах и так далее развивая реляционную структуру БД
Название: Re: QSqlRelationTableModel для одного столбца вывести несколько связанных значений
Отправлено: break от Июль 16, 2011, 21:18
mishabard - проблему пока не решил, надобность в ее быстром решении отпала. Думаю самый верный вариант написания правильных классов для работы с БД. То есть написание такого Query (модели), которому можно задать selectSQL, updateSQL, deleteSQL и т.д. Чтобы была возможность через этот Query работать с несколькими таблицами, как будто это один набор данных (так увидит это пользователь). Как например это делается в аналогичных классах для Delphi - FIBc.
RVZ - вы задачу не поняли совсем, вы предлагаете связь многие ко многим - в моем случае она не нужна. Есть водители и есть поездки. В моей задаче с каждой поездкой может быть связан один и только один водитель. Но у этого водителя куча дополнительных параметров в своей таблице. Вот и стоит вопрос как вытягивать их все, а не одно какое-то поле для представления в TableView. При этом не модифицируя метанные лишними дублирующимися полями.
Название: Re: QSqlRelationTableModel для одного столбца вывести несколько связанных значений
Отправлено: Странник от Июль 17, 2011, 14:42
можно унаследоваться от QSqlQueryModel. в ней реализовано read-only представление результатов произвольного sql-запроса, остается дописать поддержку редактирования. есть правда мнение, что в случае с несколькими связанными таблицами все равно получится нечто неудобоваримое и непрозрачное в использовании.
Название: Re: QSqlRelationTableModel для одного столбца вывести несколько связанных значений
Отправлено: break от Июль 18, 2011, 00:27
есть правда мнение, что в случае с несколькими связанными таблицами все равно получится нечто неудобоваримое и непрозрачное в использовании Практика использования FIBDataset в свое время для меня доказала обратное. Простой пример - хочу вывести ту же самую таблицу поездок. В поездке имею ID водителя, ID траспорта, вывожу таблицу дата поездки | фамилия водителя | имя водителя | возраст водителя | номер транспортного средства | фото транспортного средства | то есть есть примерно такой запрос select t.*, d.*, tr.* from TRAVELS t, DRIVERS d, TRANSPORT tr where ( t.DRIVER = d.ROW_ID ) and ( t.TRANSPORT_ID = tr.ROW_ID ) по сути простой JOIN но для удаления поездки, я хочу иметь запрос delete t.* from TRAVELS t where t.ROW_ID = ?ROW_ID т.е. хочу удалять только поездку а не транспорт и водителей (что вполне логично) а для изменения поездок, хочу иметь такой запрос update TRAVELS(DATE_TIME, DRIVER_ID, TRANSPORT_ID) set DATE_TIME = ?NEW_DATE_TIME, DRIVER_ID = ?NEW_DRIVER_ID, TRANSPORT_ID = ?NEW_TRANSPORT_ID Там так и работало.
|