PDA

View Full Version : How to use keyboard with QDateEdit and QTimeEdit?



Debra
2nd July 2019, 18:36
I have starting date and time and ending date and time. They work well with the mouse. When I go to use the keyboard, nothing happens.


ui_duration_start_dateEdit->setDate(QDate::currentDate().addMonths(-1));
ui_duration_start_timeEdit->setTime(0,0,0));
ui_duration_end_dateEdit->setDate(QDate::currentDate());
ui_duration_end_timeEdit->setTime(23,59,59));
connect(ui_duration_start_dateEdit, SIGNAL(dateChanged(QDate)), this, SLOT(SlotSetFromDateTime()));
connect(ui_duration_start_timeEdit, SIGNAL(timeChanged(QTime)), this, SLOT(SlotSetFromDateTime()));
connect(ui_duration_end_dateEdit, SIGNAL(dateChanged(QDate)), this, SLOT(SlotSetToDateTime()));
connect(ui_duration_end_timeEdit, SIGNAL(timeChanged(QTime)), this, SLOT(SlotSetToDateTime()));
connect(ui_duration_start_dateEdit->calendarWidget(), SIGNAL(selectionChanged()), this, SLOT(SlotUpdateDates()));
connect(ui_duration_end_dateEdit->calendarWidget(), SIGNAL(selectionChanged()), this, SLOT(SlotUpdateDates()));

Is there something I need to do to allow for keyboard input? Is there something that I did that prevents keyboard entry? I am looking forward to your help.

d_stranz
3rd July 2019, 00:57
You haven't really posted any code that illustrates whatever problem you are having. Showing a bunch of signal-slot connections doesn't tell us anything.

It isn't clear why you are connecting to dateChanged() and timeChanged() signals with slots that ignore the new date or time sent as part of those signals. If you don't care what the user has chosen as the new date or time, why bother connecting to those signals at all?

Posting a minimum example (a QDialog containing the two widgets, plus the code you have posted above and the code implementing the three slots) might help in diagnosing the problem.

Debra
3rd July 2019, 14:22
Thank you for responding.
The ui_ prefixed variable are widgets made in Creator. The end of the name indicates what type of widget it is.
I am trying to set the start date and time as well as the end date and time of a range. I then use that range to restrict the query to values in the range. These values are sent to a QTableView.
There are checks on the dates to make sure that the start date and time is before the end date and time.
I can change the QDateEdits and the QTimeEdits with the mouse. However, I am unable to change them with the keyboard.
I believe this is the code you were asking for. Please let me know if you need more.


void FindBagDuration::Init(void)
{
...

// Start and end dates and times
ui_duration_start_dateEdit->setDate(QDate::currentDate().addMonths(-1));
ui_duration_start_timeEdit->setTime(QTime(0,0,0));
ui_duration_end_dateEdit->setDate(QDate::currentDate());
ui_duration_end_timeEdit->setTime(QTime(23,59,59));
connect(ui_duration_start_dateEdit, SIGNAL(dateChanged(QDate)), this, SLOT(SlotSetFromDateTime()));
connect(ui_duration_start_timeEdit, SIGNAL(timeChanged(QTime)), this, SLOT(SlotSetFromDateTime()));
connect(ui_duration_end_dateEdit, SIGNAL(dateChanged(QDate)), this, SLOT(SlotSetToDateTime()));
connect(ui_duration_end_timeEdit, SIGNAL(timeChanged(QTime)), this, SLOT(SlotSetToDateTime()));
connect(ui_duration_start_dateEdit->calendarWidget(), SIGNAL(selectionChanged()), this, SLOT(SlotUpdateDates()));
connect(ui_duration_end_dateEdit->calendarWidget(), SIGNAL(selectionChanged()), this, SLOT(SlotUpdateDates()));
connect(this, SIGNAL(SignalSynchDateChanged(const QDate, bool)), this, SLOT(SlotSynchDateChanged(const QDate, bool)));
connect(this, SIGNAL(SignalSynchTimeChanged(const QTime, bool)), this, SLOT(SlotSynchTimeChanged(const QTime, bool)));

...
}

void FindBagDuration::SlotSetFromDateTime(void)
{
QDate start_date = ui_duration_start_dateEdit->date();// Used to make comparisons easier to read.
QTime start_time = ui_duration_start_timeEdit->time();
QDate end_date = ui_duration_end_dateEdit->date();
QTime end_time = ui_duration_end_timeEdit->time();

ui_duration_stackedWidget->setCurrentWidget(ui_duration_blank_page);
// Clear \todo do we need to clear the widget first?

if(end_date < start_date)
{
ui_duration_end_dateEdit->setDate(start_date);
emit SignalSynchDateChanged(start_date, false);
}

emit SignalSynchDateChanged(start_date, true);

if(end_date == start_date)
{
if(end_time < start_time)
{
ui_duration_end_timeEdit->setTime(start_time);
emit SignalSynchTimeChanged(start_time, false);
}
}
m_duration_model->SlotSetFromDateTime(QDateTime(start_date, start_time));
emit SignalSynchTimeChanged(start_time, true);
emit SignalSynchDateChanged(start_date, true);
}

void FindBagDuration::SlotSetToDateTime(void)
{
QDate start_date = ui_duration_start_dateEdit->date(); // Used to make comparisons easier to read.
QTime start_time = ui_duration_start_timeEdit->time();
QDate end_date = ui_duration_end_dateEdit->date();
QTime end_time = ui_duration_end_timeEdit->time();

ui_duration_stackedWidget->setCurrentWidget(ui_duration_blank_page);
// Clear \todo do we need to clear the widget?

// don't allow the start and end date to be the same
if(start_date > end_date)
{
ui_duration_start_dateEdit->setDate(end_date);
emit SignalSynchDateChanged(end_date, true);
}

if(end_date == start_date)
{
// don't allow the start and end time to be the same
if(start_time >= end_time)
{
if(start_time.hour() >= end_time.hour())
{
ui_duration_start_timeEdit->setTime(end_time.addSecs(-360));
emit SignalSynchTimeChanged(end_time.addSecs(-360), true);
}
else if(start_time.minute() >= end_time.minute())
{
ui_duration_start_timeEdit->setTime(end_time.addSecs(-60));
emit SignalSynchTimeChanged(end_time.addSecs(-60), true);
}
else
{
ui_duration_start_timeEdit->setTime(end_time.addSecs(-1));
emit SignalSynchTimeChanged(end_time.addSecs(-1), true);
}
}
}
emit SignalSynchTimeChanged(end_time, false);
emit SignalSynchDateChanged(end_date, false);
m_duration_model->SlotSetToDateTime(QDateTime(end_date, end_time));
}

void FindBagDuration::SlotSynchDateChanged(const QDate& date, bool start_date )
{
// Clear(); \todo ?

if(start_date == true)
{
if(ui_duration_end_dateEdit->date() < date)
{
ui_duration_end_dateEdit->setDate(date);
}

ui_duration_start_dateEdit->setDate(date);
}
else
{
// don't allow the start and end date to be the same
if(ui_duration_start_dateEdit->date() >= date)
{
ui_duration_start_dateEdit->setDate(date);
}

ui_duration_end_dateEdit->setDate(date);
}

if(ui_duration_start_dateEdit->date() == date)
{
if(ui_duration_start_timeEdit->time() > ui_duration_end_timeEdit->time())
{
ui_duration_start_timeEdit->setTime(QTime(0, 0, 0));
}
}
}

void FindBagDuration::SlotSynchTimeChanged(const QTime& time, bool start_time )
{
// Clear(); \todo ?

if(start_time == true)
{
if(ui_duration_end_dateEdit->date() == ui_duration_start_dateEdit->date())
{
if(ui_duration_end_timeEdit->time() < time)
{
ui_duration_end_timeEdit->setTime(time);
}
}

ui_duration_start_timeEdit->setTime(time);
}
else
{
if(ui_duration_end_dateEdit->date() == ui_duration_start_dateEdit->date())
{
// don't allow the start and end time to be the same
if(ui_duration_start_timeEdit->time() >= time)
{
ui_duration_start_timeEdit->setTime(QTime(0, 0, 0));
}
}

ui_duration_end_timeEdit->setTime(time);
}
}

void FindBagDuration::SlotUpdateDates(void)
{
if(ui_duration_start_dateEdit->date() == ui_duration_end_dateEdit->date())
{
// don't allow the start and end time to be the same
if(ui_duration_start_timeEdit->time() >= ui_duration_end_timeEdit->time())
{
ui_duration_start_timeEdit->setTime(QTime(0, 0, 0));
}
}
}

Lesiok
3rd July 2019, 14:37
I think you have to check whether the start / end date / time is valid in both slots and do something further only when all four elements are valid.

Debra
3rd July 2019, 16:11
Validating the dateEdits and timeEdits is a good recommendation. I'll try that. I don't think that fixes the problem though, since the problem occurs with the initialized values also. It is very frustrating that it will work with the mouse, but not the keyboard. Thank you for your recommendation.

d_stranz
3rd July 2019, 18:20
Possibly your problem is due to the way you have implemented your slots to handle date / time changes. From my reading of the QDateTime docs, a call to setTime() or setDate() on one of these widgets will result in a dateChanged() / timeChanged() signal, so you have possibly set up some recursion by calling these from within your slots.

Also, when you emit a signal from within a slot, any slots connected to that signal will get executed immediately, with control returning to the original slot only after all of the connected slots have finished - thus more chance of unintended recursion.

In addition, it is possible that every keystroke will result in one of the ...Changed() signals being emitted, and if the partially-edited value is incorrect, your slot might simply reset it back to the original making it appear as though keyboard editing isn't doing anything. The definitive signal to watch for keystroke editing is QAbstractSpinBox::editingFinished(), which is emitted only after a return keypress or focus has left the widget.

You can verify this with the debugger, setting a breakpoint in one of your slots and checking the call stack to see where it is being invoked. You could also sprinkle some qDebug() calls around to help better track how and when your signals / slot connections are being executed.

Monitoring interactions between two or more widgets is tricky, especially when a change in one widget's value can affect another widget's value - you may inadvertently set up a ping-pong effect. Sometimes the only way around this is to disable signals on the linked widget, set the new value, then re-enable signals, thereby preventing the change in value from triggering the associated signal.

Debra
3rd July 2019, 19:25
Tried a lot of things, but the problem wasn't in the code. Thank you for your recommendations; they are good ideas regardless of my problem.
The problem was a result of a Linux environment variable QT_XKB_CONFIG_ROOT not being set.
Thank you both, Lesiok and d_stranz!

d_stranz
4th July 2019, 05:15
Hah, so much for my big ideas :eek: