#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__
#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 );}
select t.*, d.*, tr.*from TRAVELS t, DRIVERS d, TRANSPORT trwhere ( t.DRIVER = d.ROW_ID ) and ( t.TRANSPORT_ID = tr.ROW_ID )
delete t.*from TRAVELS twhere t.ROW_ID = ?ROW_ID
update TRAVELS(DATE_TIME, DRIVER_ID, TRANSPORT_ID)setDATE_TIME = ?NEW_DATE_TIME,DRIVER_ID = ?NEW_DRIVER_ID,TRANSPORT_ID = ?NEW_TRANSPORT_ID