PDA

View Full Version : Simple QT SQL Viewer/Updater... Revisited



AlphaWolfXV
22nd July 2013, 02:28
Hello all,
First I am on Windows 7 Pro, 64bit, using QT creator 2.4.1 and QT version 4.8.1.
I am working on a simple SQL Viewer/Editor in a Table view. I have made connection to a SQL Server DB via ODBC and have no issues updating records/deleting records or adding records. I do have two minor issues that I would like to resolve and they are as follows:
1. Requires 2 Esc Key presses to get to signal/slot on Nav bar.
- I have a nav widget I made which handles goto first, prev, next, last, new, delete and x of y records. Also in this bar I have an icon which I want to change to something when the record is "dirty". Then once either cancelled or submitted the icon goes back to "locked" or "clean". This is similar to MS Access which had the "..(pencil)" icon which would show up on editing a record either in table view or in form view.
- the issue I have is 1 escape key will revert the table view back to original state, but I do not seem to be able to intercept this to trigger my signal to change the icon back.
- Is there something I can do to either intercept the first table-view revert signal (must be internal) and act on it?

2. After "submitting" a record to the db when using "Enter" key the table model resets causing the highlighted row to go back to not visible and the user then needs to arrow or click to regain entry mode to the table.
- I of course can work around this, but I would like it to either stay in the same field and just submit or else redirect it to the next row down if available. I have been trying to do this using the "RowChanged" event but no matter which settings I change (like reloading the model or incrementing a line counter to set the current record to) it will not take it until it has been either arrow key or mouse click to the desired row.

Code will follow, if you have any suggestions, please feel free to respond! (This is only the questionable frmStatus code) if the rest of the application code is needed please feel free to let me know and I will post it!

Thanks to all,
AlphaWolfXV

frmStatus.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>frmStatus</class>
<widget class="QDialog" name="frmStatus">
<property name="windowModality">
<enum>Qt::WindowModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>485</width>
<height>300</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>9</x>
<y>9</y>
<width>471</width>
<height>281</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<family>Calibri</family>
<pointsize>15</pointsize>
<weight>75</weight>
<bold>true</bold>
<underline>true</underline>
</font>
</property>
<property name="text">
<string>Add / Edit Status Definitions in the Database</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="tableView"/>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>


Remaining frmStatus Code:
frmStatus.cpp

#include "frmstatus.h"
#include "ui_frmstatus.h"

frmStatus::frmStatus(QWidget *parent) :
QDialog(parent),
ui(new Ui::frmStatus)
{
ui->setupUi(this);
tableModel = new QSqlTableModel();
tableModel->setTable("tStatus");
tableModel->setEditStrategy(QSqlTableModel::OnRowChange);
tableModel->select();
tableModel->setHeaderData(0, Qt::Horizontal, tr("Status ID"),Qt::DisplayRole);
tableModel->setHeaderData(1,Qt::Horizontal,tr("Status Description"), Qt::DisplayRole);

qDebug()<<"row count:"<<tableModel->rowCount();

nav = new dbrecordnav(this);
QHBoxLayout *hb1 = new QHBoxLayout();
hb1->addWidget(new QLabel(""),1);
hb1->addWidget(nav);
hb1->addWidget(new QLabel(""),1);
ui->verticalLayout->addLayout(hb1);
nav->setCurrentRec( 1);
nav->setTotalRec(tableModel->rowCount());
ui->tableView->setModel(tableModel);
ui->tableView->horizontalHeader()->setClickable(true);
QModelIndex mi = ui->tableView->model()->index(0,1);
ui->tableView->setCurrentIndex(mi);
setModal(true);
#ifdef RELEASE
ui->tableView->hideColumn(0);
#endif
ui->tableView->resizeColumnToContents(1);
itemSelModel = new QItemSelectionModel(tableModel,this);
itemSelModel = ui->tableView->selectionModel();
ui->tableView->installEventFilter(this);
//ui->tableView->setItemDelegateForColumn(1,statusDescEdit);
connect(nav,SIGNAL(first()),this,SLOT(go2First())) ;
connect(nav,SIGNAL(prev()),this,SLOT(go2Prev()));
connect(nav,SIGNAL(next()),this,SLOT(go2Next()));
connect(nav,SIGNAL(last()),this,SLOT(go2Last()));
connect(nav,SIGNAL(newrec()),this,SLOT(addStatus() ));
connect(nav,SIGNAL(deleterec()),this,SLOT(deleteSt atus()));
connect(nav,SIGNAL(changeRecord(int)),this,SLOT(go 2Rec(int)));
connect(this,SIGNAL(dirty()),nav,SLOT(handleDirtyR ecord()));
connect(this,SIGNAL(revert()),nav,SLOT(handleClean Record()));
// connect(nav,SIGNAL(storeRecChanges()),this,SLOT(sa veCurRec()));
connect(itemSelModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)) , this, SLOT(rowChanged(QModelIndex, QModelIndex)));

}

frmStatus::~frmStatus()
{
delete ui;
}
bool frmStatus::eventFilter(QObject *object, QEvent *e)
{
if(object == ui->tableView)
{
if (e->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(e);
#ifdef DEBUG
qDebug() << "EVENT FILTER key press" << keyEvent->key();
#endif
if(keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return)
{
#ifdef DEBUG
qDebug()<<"Pressed Enter key before model reset!";
#endif
//goahead and try to save
if(!tableModel->submit()){qDebug()<<"Submit failed... try again!"<<tableModel->lastError().text();}

//e->ignore();
return true;
}
else if(keyEvent->key() == Qt::Key_Down)
{
this->go2Next();
return true;
}
else if(keyEvent->key() == Qt::Key_Up)
{
this->go2Prev();
return true;
}
else if(keyEvent->key() == Qt::Key_Escape)
{
qDebug()<<"EVENT FILTER: Escape pressed";
blnDirty = false;
revertForm();
return true;
}
else
{
//dirty
qDebug()<<"form dirty...";
blnDirty = true;
emit dirty();
return false;
}
}
else
{
return false;
}
}
else
{
// pass the event on to the parent class
return QDialog::eventFilter(object, e);
}
}

void frmStatus::rowChanged(QModelIndex current, QModelIndex previous)
{
disconnect(itemSelModel,SIGNAL(currentRowChanged(Q ModelIndex,QModelIndex)),this,SLOT(rowChanged(QMod elIndex, QModelIndex)));
if(current.row()==-1)
{
nav->setCurrentRec(previous.row()+1);
tableModel->select();
ui->tableView->setModel(tableModel);
itemSelModel = ui->tableView->selectionModel();

QModelIndex mi = ui->tableView->model()->index(previous.row(),1);
ui->tableView->setCurrentIndex(mi);
ui->tableView->setRootIndex(mi);
itemSelModel->setCurrentIndex(mi,QItemSelectionModel::Current);
#ifdef DEBUG
qDebug()<<"Model Reset!";
qDebug()<<"Current index of table view:"<<ui->tableView->currentIndex().row();
#endif

}
else
{
qDebug()<<"Normal Cell Change..." <<current.row()<<previous.row();
nav->setCurrentRec(current.row()+1);
if(!(tableModel->submit())){qDebug()<<"Error with submit on row change:"<<tableModel->lastError().text();}
qDebug()<<"Normal Cell Change...After Submit!" <<current.row()<<previous.row();
//ui->tableView->setModel(tableModel);
// itemSelModel = ui->tableView->selectionModel();
//QModelIndex mi = ui->tableView->model()->index(0,1);
//ui->tableView->setCurrentIndex(mi);
//ui->tableView->setRootIndex(mi);
//itemSelModel->setCurrentIndex(mi,QItemSelectionModel::Current);
}
connect(itemSelModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)) , this, SLOT(rowChanged(QModelIndex, QModelIndex)));
qDebug()<<"atend_Current index of table view:"<<ui->tableView->currentIndex().row();
}


void frmStatus::addStatus()
{
tableModel->insertRows(0,1);
tableModel->setData(tableModel->index(0,1),QString(" "));
if(!tableModel->submit()){qDebug()<<"Submit failed... try again!"<<tableModel->lastError().text();}
nav->setTotalRec(tableModel->rowCount());
QModelIndex mi = ui->tableView->model()->index(tableModel->rowCount()-1,1);
ui->tableView->setCurrentIndex(mi);
ui->tableView->resizeRowsToContents();
nav->setCurrentRec(ui->tableView->currentIndex().row()+1);
}
void frmStatus::deleteStatus()
{
qDebug()<<"Current index of table view:"<<ui->tableView->currentIndex().row();
tableModel->removeRow(nav->getCurrentRec()-1);
if(!tableModel->submit()){qDebug()<<"Submit failed... try again!"<<tableModel->lastError().text();}
nav->setTotalRec(tableModel->rowCount());
QModelIndex mi = ui->tableView->model()->index(tableModel->rowCount()-1,1);
ui->tableView->setCurrentIndex(mi);
nav->setCurrentRec(ui->tableView->currentIndex().row()+1);
qDebug()<<"Current index of table view:"<<ui->tableView->currentIndex().row();
ui->tableView->resizeRowsToContents();
}

void frmStatus::go2First()
{
QModelIndex mi = ui->tableView->model()->index(0,1);
ui->tableView->setCurrentIndex(mi);
nav->setCurrentRec(ui->tableView->currentIndex().row()+1);
}
void frmStatus::go2Prev()
{
//check if already at beginning...
if(!(ui->tableView->currentIndex().row() == 0))
{
QModelIndex mi = ui->tableView->model()->index(ui->tableView->currentIndex().row()-1,1);
ui->tableView->setCurrentIndex(mi);
nav->setCurrentRec(ui->tableView->currentIndex().row()+1);
}
}
void frmStatus::go2Next()
{
//check if already at end...
if(!(ui->tableView->currentIndex().row() == tableModel->rowCount()-1))
{
QModelIndex mi = ui->tableView->model()->index(ui->tableView->currentIndex().row()+1,1);
ui->tableView->setCurrentIndex(mi);
nav->setCurrentRec(ui->tableView->currentIndex().row()+1);
}
}
void frmStatus::go2Last()
{
QModelIndex mi = ui->tableView->model()->index(tableModel->rowCount()-1,1);
ui->tableView->setCurrentIndex(mi);
nav->setCurrentRec(ui->tableView->currentIndex().row()+1);
}
void frmStatus::go2Rec(int a)
{
QModelIndex mi = ui->tableView->model()->index(a-1,1);
ui->tableView->setCurrentIndex(mi);
nav->setCurrentRec(ui->tableView->currentIndex().row()+1);
}

/*void frmStatus::keyPressEvent(QKeyEvent *event)
{
if(event->key()==Qt::Key_Escape)
{
qDebug()<<"STATUS FORM - Esc pressed";
blnDirty = false;
revertForm();
}
else if(event->key() == Qt::Key_Return||event->key() == Qt::Key_Enter)
{
qDebug()<<"Enter Key Pressed - STATUS FORM";
}
else
{
qDebug()<<"form dirty...";
blnDirty = true;
emit dirty();
}
}*/
void frmStatus::revertForm()
{
emit revert(); //resets nav icon...
}

/*
The following are only used in Forms with mappign widgets not controlled by a table view.
here the view handles all the submitting etc... we don't need to do any additional...
just connect up the signals/slots to make the form function as we want...*/
/*
void frmStatus::disconnectDirty(){}
void frmStatus::connectDirty(){}
void frmStatus::setDirty() { blnDirty = true; emit dirty();qDebug()<<"we got a dirty() signal!";} //set whenever something changes on the form
void frmStatus::setDirty(int){blnDirty = true;emit dirty();} //set whenever something changes on the form
void frmStatus::setDirty(QString){blnDirty = true;emit dirty();} //set whenever something changes on the form
void frmStatus::setDirty(QDate){blnDirty = true;emit dirty();} //set whenever something changes on the form
void frmStatus::revertForm()
{

}

bool frmStatus::getCurRecData()
{
bool res;
return res;
}
bool frmStatus::getUpdateRecData()
{
bool res;
return res;
}
bool frmStatus::updateSQLData()
{
bool res;
return res;
}
bool frmStatus::handleNewRec()
{
bool res;
return res;
}

void frmStatus::saveCurRec()
{

}
*/


frmStatus.h

#ifndef FRMSTATUS_H
#define FRMSTATUS_H

#include <QDialog>
#include <QtGui>
#include <QtSql>
#include "globals.h"
#include "../qtWidgets/recordNav/dbrecordnav.h"
namespace Ui {
class frmStatus;
}
//tStatus Enum
enum{
tStatus_Id = 0,
tStatus_Desc = 1 };
struct statusForm{
int id;
QString statusDesc;
};

class frmStatus : public QDialog
{
Q_OBJECT

public:
explicit frmStatus(QWidget *parent = 0);
~frmStatus();

public slots:
signals:
void dirty();
void revert();
protected:
//void keyPressEvent(QKeyEvent *event);
bool eventFilter(QObject *w, QEvent *e);
//bool event(QEvent *e);
protected slots:

private slots:
void rowChanged(QModelIndex, QModelIndex);
void addStatus();
void deleteStatus();
void go2Prev();
void go2First();
void go2Last();
void go2Next();
void go2Rec(int a);
// void setDirty();
// void setDirty(int);
// void setDirty(QString);
// void setDirty(QDate);
// void saveCurRec(void);
private:
void revertForm();
Ui::frmStatus *ui;
QItemSelectionModel *itemSelModel;
dbrecordnav *nav;
QSqlTableModel *tableModel;
bool blnDirty;
};

#endif // FRMSTATUS_H

AlphaWolfXV
24th July 2013, 02:27
Hello All,
Please advise if anything needs further clarification!
Thanks,
AlphaWolfXV

AlphaWolfXV
26th July 2013, 04:26
Hello all,
Ok, I think I have a more basic question:
I believe that the reason I am not able to get the ESC key captured is that the QTableView is handling it (by default) and returning true. So, if this is true, then I believe I need to re-implement the keyevent that QTableView is getting and return "FALSE" and then let the main eventFilter() handle the actual keypress... Will this work? How and what do I reimplement (or which class should I subclass to obtain this functionality?) I have tried to look through QTableView but eventFilter() does not seem to be a valid item to change... Any thoughts? I think both answers to the above questions are tied to this concept, any help is much appreciated.
Thanks,
AlphaWolfXV

AlphaWolfXV
29th July 2013, 15:30
Hi All,
Ok, so I have made some progress as to what not to do...!

I have made a QLineEdit Delegate and applied to the QTableView.
Then I subclassed QLineEdit to make the delegate and reimplemented the keyPressEvent(). I can catch almost all keys except for escape and tab...
Then I tried to make an eventFilter on the lineedit subclass to see if I could capture it there... still nothing...

Is there a "normal" way to get a hold of the escape key from a qtableview???
Thanks,
AlphaWolfXV

Oh, also I have been attempting to catch with a QShortcut but that is not proving effective with the Escape key. It is seemingly being captured and dealt with long before any event is called... But where to catch it!
Thanks again,
AlphaWolfXV

AlphaWolfXV
31st July 2013, 16:56
Hello again,
Quick update:
I have created a line edit delegate applied to the qtableview column of choice. This works good. Then I implemented the eventFilter(obj,event) in the lineEditDelegate which now ignores any special key press events and passes all back to the parent. This is good, I think, I am now trying to handle reset of the model etc via the parent widget and will keep you all posted!
Thanks,
AlphaWolfXV

AlphaWolfXV
1st August 2013, 04:17
Ok, so I finally have part of this working, I can now intercept the escape key. I copied over the bool eventFilter(obj,event) from qItemDelegate.cpp for my implementation, then I emit a signal when esc is pressed so that it triggers my app to handle a revert.
An additional alternative could have been to just return false from the esc key and let the app decide, I think it is a matter of preference?! Either way that portion is working now, I attached the lineEditDelegate relevant code if anyone else is interested. I am still working on getting the "Enter" key to not reset the model after it submits the data... I think it is tied to the
QMetaObject::invokeMethod(this, "_q_commitDataAndCloseEditor",
Qt::QueuedConnection, Q_ARG(QWidget*, editor)); which is called on either a Qt::Key_Return || Qt::Key_Enter... I am still digging into this one...

If anyone has other thoughts on how to stop the tableModel from resetting when enter is pressed after an edit when the down arrow key works correctly, please let me know,
Thanks,
AlphaWolfXV

lineEditDelegate(cpp for eventFilter...)

bool lineEditDelegate::eventFilter(QObject *object, QEvent *event)
{
{
QWidget *editor = qobject_cast<QWidget*>(object);
if (!editor)
return false;
if (event->type() == QEvent::KeyPress) {
switch (static_cast<QKeyEvent *>(event)->key()) {
case Qt::Key_Tab:
emit commitData(editor);
emit closeEditor(editor, QAbstractItemDelegate::EditNextItem);
return true;
case Qt::Key_Backtab:
emit commitData(editor);
emit closeEditor(editor, QAbstractItemDelegate::EditPreviousItem);
return true;
case Qt::Key_Enter:
case Qt::Key_Return:
if (QLineEdit *e = qobject_cast<QLineEdit*>(editor))
if (!e->hasAcceptableInput())
return false;
QMetaObject::invokeMethod(this, "_q_commitDataAndCloseEditor",
Qt::QueuedConnection, Q_ARG(QWidget*, editor));
return false;
case Qt::Key_Escape:
// don't commit data
qDebug()<<"ESCAPE?";
emit sig_ESC_Pressed(); //MY NEW SIGNAL!!!!!
emit closeEditor(editor, QAbstractItemDelegate::RevertModelCache);
break;
default:
return false;
}
if (editor->parentWidget())
editor->parentWidget()->setFocus();
return true;
} else if (event->type() == QEvent::FocusOut || (event->type() == QEvent::Hide && editor->isWindow())) {
//the Hide event will take care of he editors that are in fact complete dialogs
if (!editor->isActiveWindow() || (QApplication::focusWidget() != editor)) {
QWidget *w = QApplication::focusWidget();
while (w) { // don't worry about focus changes internally in the editor
if (w == editor)
return false;
w = w->parentWidget();
}

emit commitData(editor);
emit closeEditor(editor, NoHint);
}
} else if (event->type() == QEvent::ShortcutOverride) {
if (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Escape) {
event->accept();
return true;
}
}
return false;
}
}