I'm having a QTimeEdit displaying hh:mm.
This widget behavior is very annoying: When the value is 09:59, there is no easy way to move to 10:00. The spinbox controls on the right only applies to minutes or hours. When you put your cursor on minutes, incrementing is not allowed if value is 59.
To move from 09:59 to 10:00 you need to decrement 59 to 00 and then increment 09 to 10...that's really annoying.
Is there no option to make it possible to let user increment minutes by one using the spinbox control and then get value be changed from 09:59 to 10:00 with a single click??
Set wrapping to true.
As for the automatic change of the hours section, reimplement stepBy in a subclass of QTimeEdit like this:
MyTimeEdit.h
#ifndef MYTIMEEDIT_H
#define MYTIMEEDIT_H
#include <QTimeEdit>
class MyTimeEdit : public QTimeEdit
{
Q_OBJECT
public:
explicit MyTimeEdit(QWidget *parent = nullptr);
void stepBy(int steps) override;
};
#endif // MYTIMEEDIT_H
MyTimeEdit.cpp
#include "MyTimeEdit.h"
MyTimeEdit::MyTimeEdit(QWidget *parent) : QTimeEdit(parent)
{
setWrapping(true);
}
void MyTimeEdit::stepBy(int steps)
{
QTime cur = time();
QTimeEdit::stepBy(steps);
if (currentSection() == QDateTimeEdit::MinuteSection) {
int m = cur.minute();
if ((m == 0) && (steps < 0))
setTime(time().addSecs(-3600));
else if ((m == 59) && (steps > 0))
setTime(time().addSecs(3600));
}
else if (currentSection() == QDateTimeEdit::SecondSection) {
int s = cur.second();
if ((s == 0) && (steps < 0))
setTime(time().addSecs(-60));
else if ((s == 59) && (steps > 0))
setTime(time().addSecs(60));
}
}
I noticed that the above answer does not work when using the mouse wheel or Page Up/Page Down instead of the arrow keys, because then the step is larger than 1 (10 in my case).
Here is a version of MyTimeEdit.cpp that also works with the mouse wheel and Page Up/Down keys:
MyTimeEdit.cpp
#include "MyTimeEdit.h"
MyTimeEdit::MyTimeEdit(QWidget *parent) : QTimeEdit(parent)
{
setWrapping(true);
}
void MyTimeEdit::stepBy(int steps)
{
if (currentSection() == QDateTimeEdit::MinuteSection) {
setTime(time().addSecs(steps*60));
}
else if (currentSection() == QDateTimeEdit::SecondSection) {
setTime(time().addSecs(steps));
}
}
Related
What would be the best way of counting how many times a bool flag was changed from false to true inside a game update loop? For example if I have this simple example below, where if you hold button "A" pressed the Input class sets the enable bool of the Game class to true and if you release it sets it to false and a counter inside the Game class that counts how many times enable was changed from true to false. For example if you press "A" and release twice counter should update to 2. Having Game::Update() updating at 60fps the counter would be wrong with the current approach. To fix it I moved the check and the counter inside SetEnable instead of the Update loop.
// Input class
// Waits for input
void Input::HandleKeyDown()
{
// Checks if key A is pressed down
if (key == KEY_A)
game.SetEnable(true);
}
void Input::HandleKeyUp()
{
// Checks if key A is released
if (key == KEY_A)
game.SetEnable(false);
}
// Game class
void Game::SetEnable(bool enable)
{
if(enable == enable_)
return;
enable_ = enable;
//Will increment the counter as many times A was pressed
if(enable)
counter_ += 1;
}
void Game::Update()
{
// Updates with 60fps
// Will increment the counter as long as A is pressed
/*
if(enable_ == true)
counter_ += 1;
*/
}
void Game::Update()
{
if (key == KEY_A && ! enable_)
{
enable_ = true;
++counter_;
}
else if (key == KEY_B)
enable_ = false;
}
If I get you right, you want to count how many times enable_ changes. Your code has a small flaw, imagine this example:
enable_ = false
counter = 0
update gets called, key is A -> enable_ = true, counter = 1
update gets called, key is B -> enable_ = false, counter remains 1
Function that might fix this can look, for example, like this:
void Game::Update() {
if (key == KEY_A && !enable_) { // key is A and previous state is false
++counter;
enable_ = true;
}
if (key == KEY_B && enable_) { // key is B and previous state is true
++counter;
enable_ = false;
}
}
I am making a timer/stopwatch with Qt Creator. But, my reset function (reset button clicked) is not working as I want it to. I want it to stop the timer and set the display (QLCDNumber) to 0. Instead, the timer is stopped but the display numbers stay the same as if the pause button was clicked. Except that when the timer is started (start button clicked) again, it restarts from the original time (as I want it to do). Here is the code.
I only included the parts that are part of the problem.
void MainWindow::delay()
{
QTime dieTime = QTime::currentTime().addSecs(1);
while (QTime::currentTime() < dieTime && !spause && !sreset)
{
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
void MainWindow::on_tstart_clicked()
{
ttime = treset ? 0 : ttime;
tpause = treset = false;
ttime = ttime == 0 ? (ui->hr->value() * 3600 + ui->min->value() * 60 + ui->sec->value()) : ttime;
while (ttime >= 0 && !tpause && !treset)
{
const unsigned short sec = ttime % 3600 % 60, min = ttime % 3600 / 60, hr = ttime / 3600;
ui->tsec2->display(sec % 10);
ui->tsec1->display(sec / 10);
ui->tmin2->display(min % 10);
ui->tmin1->display(min / 10);
ui->thr2->display(hr % 10);
ui->thr1->display(hr / 10);
delay();
if (!tpause && !treset) --ttime;
}
}
void MainWindow::on_tpause_clicked()
{
tpause = true;
}
void MainWindow::on_treset_clicked()
{
treset = true;
ui->ssec2->display(0);
ui->ssec1->display(0);
ui->smin2->display(0);
ui->smin1->display(0);
ui->shr2->display(0);
ui->shr1->display(0);
}
Your click on a button is processed only in the function delay() containing processEvents(). When delay() is executed, it sends message to the application, but on_treset_clicked() is executed after the next loop of while() was started. Use QTimer to avoid this situation.
Here is how to use QTimer in your case.
(mainwindow.h)
#include <QTimer>
...
QTimer timer;
(mainwindow.cpp)
MainWindow::MainWindow()
{
...
connect(&timer,SIGNAL(timeout()),this,SLOT(on_timer()));
}
void MainWindow::on_tstart_clicked()
{
timer.start(1000);
}
void MainWindow::on_timer()
{
if(timer.isActive()) return;
ttime--;
(display LCD values)
if(ttime<=0)
{
ttime=0;
(emit signal for alarm or whatever you want)
timer.stop();
}
}
void MainWindow::on_tpause_clicked()
{
timer.stop();
(display LCD values)
}
void MainWindow::on_treset_clicked()
{
timer.stop();
ttime=0;
(display zeroes)
}
(Posted on behalf of the OP).
Everything is solved! I was resetting the stopwatch display by accident. Thanks #Michael!
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
// ttimer was declared in "MainWindow" -> QTimer ttimer;
connect(&ttimer, SIGNAL(timeout()), this, SLOT(on_timer()));
}
void MainWindow::on_tstart_clicked()
{
ttime = ttime == 0 ? (ui->hr->value() * 3600 + ui->min->value() * 60 + ui->sec->value()) : ttime;
ttimer.start(1000);
}
void MainWindow::on_timer()
{
if (!ttimer.isActive()) return;
--ttime;
const unsigned short hr = ttime / 3600, min = ttime % 3600 / 60, sec = ttime % 60;
ui->tsec2->display(sec % 10);
ui->tsec1->display(sec / 10);
ui->tmin2->display(min % 10);
ui->tmin1->display(min / 10);
ui->thr2->display(hr % 10);
ui->thr1->display(hr / 10);
if (ttime <= 0)
{
// (emit signal for alarm or whatever you want)
ttimer.stop();
}
}
void MainWindow::on_tpause_clicked()
{
ttimer.stop();
// (display LCD values)
}
void MainWindow::on_treset_clicked()
{
ttimer.stop();
ttime = 0;
ui->tsec2->display(0);
ui->tsec1->display(0);
ui->tmin2->display(0);
ui->tmin1->display(0);
ui->thr2->display(0);
ui->thr1->display(0);
}
I implemented QTimer in my code, but when I press the "Start" button (on_tstart_clicked) or any other buttons, nothing happens (no change in display).
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
// ttimer was declared in "MainWindow" -> QTimer ttimer;
connect(&ttimer, SIGNAL(timeout()), this, SLOT(on_timer()));
}
void MainWindow::on_tstart_clicked()
{
ttime = ttime == 0 ? (ui->hr->value() * 3600 + ui->min->value() * 60 + ui->sec->value()) : ttime;
ttimer.start(1000);
}
void MainWindow::on_timer()
{
if (!ttimer.isActive()) return;
--ttime;
const unsigned short hr = ttime / 3600, min = ttime % 3600 / 60, sec = ttime % 60;
ui->tsec2->display(sec % 10);
ui->tsec1->display(sec / 10);
ui->tmin2->display(min % 10);
ui->tmin1->display(min / 10);
ui->thr2->display(hr % 10);
ui->thr1->display(hr / 10);
if (ttime <= 0)
{
// (emit signal for alarm or whatever you want)
ttimer.stop();
}
}
void MainWindow::on_tpause_clicked()
{
ttimer.stop();
// (display LCD values)
}
void MainWindow::on_treset_clicked()
{
ttimer.stop();
ttime = 0;
ui->ssec2>tsec2->display(0);
ui->ssec1>tsec1->display(0);
ui->smin2>tmin2->display(0);
ui->smin1>tmin1->display(0);
ui->shr2>thr2->display(0);
ui->shr1>thr1->display(0);
}
The issue was simple, but I kept glossing over it. I had to change ttimer.isActive() to !ttimer.isActive().
But, now the the reset (on_treset_clicked) doesn't work. The QLCDNumber's aren't reset to 0.
I have a custom QTableView and QAbstractTableModel. My QTableView hides some of the columns from the QAbstractTableModel as they aren't needed.
When I hit Tab, I would like to select the next available (editable) column. My current implementation is to grab the next index from the QAbstractTableModel, but this index includes columns that are hidden. (So when hitting Tab it may be a couple presses before you see the "next" column selected.)
How can I tell Tab to jump to the next visible column?
The language is C++. Below is the code within my QTableView:
void keyPressEvent(QKeyEvent* event)
{
if((event->modifiers() == Qt::KeyboardModifier::NoModifier) && (event->key() == Qt::Key::Key_Tab))
{
this->moveToNextCell();
}
else
{
this->QTableView::keyPressEvent(event);
}
}
void moveToNextCell()
{
const QModelIndex index = this->currentIndex();
int nextColumn = index.column() + 1;
if(index.column() <= lastEditableCol)
{
this->setCurrentIndex(model->index(index.row(), nextColumn));
}
}
It's not elegant, but I've solved the problem by using isColumnHidden() from QTableView. I just iterate through the columns until I find one that isn't hidden.
for(int i = nextColumn; i <= numOfColumns && nextColumn <= numOfColumns; i++)
{
if(this->isColumnHidden(nextColumn) == true)
{
nextColumn += 1;
}
else
{
i = numOfCol + 1;
}
}
I know this is a beginner's problem but I cannot progress with my studies until I understand what is going on here. In an if block that I'm executing in Update(), I cannot understand why the two if statements are executed simultaneously. I thought that as soon as the initiative variable was initialized at the start of the block, the compiler would move down to the first if statement to see if the condition is true. If it is true, it would execute the code in that block until the timer reached zero. Then it would move down to the next. If the condition was true in the first, then it would be false in the second so it would move up to the beginning again. A new bool value would be calculated for initiative and the process would be repeated. What's blowing my mind is that no matter what I do, both blocks are executed and the timers are completely ignored. It is very confusing!
I've made some very basic code to highlight the problem I'm having. I'm using this C# code in Unity3d:
public float firstTimer = 2;
public float secondTimer = 10;
public bool initiative;
void Update()
{
initiative = InitiativeOrder();
if(initiative == true && firstTimer > 0);
{
firstTimer -= Time.deltaTime;
Debug.Log ("First timer has finished!");
}
if(initiative == false && secondTimer > 0);
{
secondTimer -= Time.deltaTime;
Debug.Log ("Second timer has finished!");
}
}
public bool InitiativeOrder()
{
float player = Random.Range(1,10);
float enemy = Random.Range(1,10);
if(player > enemy)
{
return true;
}
else if (player < enemy)
{
return false;
}
else
{
return true;
}
}
the reason the timer isnt working is because the ; after your if()
if(initiative == true && firstTimer > 0);<----remove these from your if
make your if() like these
if(initiative == true && firstTimer > 0)
{
firstTimer -= Time.deltaTime;
Debug.Log ("First timer has finished!");
}
if(initiative == false && secondTimer > 0)
{
secondTimer -= Time.deltaTime;
Debug.Log ("Second timer has finished!");
}
its not executing both if() blocks
this code
initiative = InitiativeOrder();
is being called every frame witch is im guessing 60 fps so for every frame per second it is executing that method and the chance however many times per second that it is true or false is why it seems that it is firing both if() blocks but really each frame that update fires its either true or false which is really fast
if you put that call in OnStart() it will only fire once like
void Start(){
initiative = InitiativeOrder();
}
EDIT YOUR CODE
public float firstTimer = 2;
public float secondTimer = 10;
public bool initiative;
void Start(){
initiative = InitiativeOrder();
}
void Update()
{
if(initiative == true && firstTimer > 0)
{
firstTimer -= Time.deltaTime;
Debug.Log ("First timer has finished!");
}
if(initiative == false && secondTimer > 0)
{
secondTimer -= Time.deltaTime;
Debug.Log ("Second timer has finished!");
}
}
public bool InitiativeOrder()
{
float player = Random.Range(1,10);
float enemy = Random.Range(1,10);
if(player > enemy)
{
return true;
}
else if (player < enemy)
{
return false;
}
else
{
return true;
}
}
I'm trying to do something basic : you have a QTreeView. 1st depth are folders only, 2nd depth are files only. I want to have a check box with the checked status next to each item. Files are either checked or unchecked, folders can also be partiallyChecked depending on their files; all in all quite natural I believe.
The way I though I should go was using a QStandardItemModel and populate it with a custom subclass of QStandardItem : DescriptionFileItem. Maybe that was a bad idea, if there's an easier way please enlight me.
I tried using signals and slots so that my signal CheckStateChanged on a file would be connected to a slot UpdateCheckedStateOnChildStateChanged on its containing folder. This required my DescriptionFileItem to inherit from QObject as well (BTW, I was surprised that QStandardItem did not inherit from QObject). I initially hoped this would work seamlessly with the provided base classes but it did not : emitDataChanged() didn't seem to trigger my model's dataChanged() signal...
Using the model's dataChanged signals directly didn't work either: it's call is protected so you can't use it without subclassing (I think that's my next move unless somebody can help me get it right).
At the moment I have a signal -> slot connection that won't work and I have no idea why; compile and link work ok. Here's the code; perhapps you'll spot my mistakes easily. I'm leaving some commented lines so you can maybe see what I did wrong in a previous attempt. Thanks for your input!
#ifndef DESCRIPTIONFILEITEM_H
#define DESCRIPTIONFILEITEM_H
#include <QStandardItem>
#include <Qt>
class DescriptionFileItem : public QObject, public QStandardItem
{
Q_OBJECT
public:
explicit DescriptionFileItem(const QString & text, bool isFileName=false, QObject* parent = 0);
void setData ( const QVariant & value, int role = Qt::UserRole + 1 );
QVariant data( int role = Qt::UserRole + 1 ) const;
QString text;
Qt::CheckState checkedState;
bool isFileName;
signals:
void CheckStateChanged();
public slots:
void UpdateCheckedStateOnChildStateChanged();
};
#endif // DESCRIPTIONFILEITEM_H
Corresponding .cpp :
#include "DescriptionFileItem.h"
DescriptionFileItem::DescriptionFileItem(const QString & text, bool isFileName, QObject* parent):
QObject(parent),QStandardItem(text)
{
this->isFileName = isFileName;
checkedState = Qt::Checked;
}
void DescriptionFileItem::setData ( const QVariant & value, int role){
if(role == Qt::CheckStateRole){
Qt::CheckState newCheckState = (Qt::CheckState)value.toInt();
checkedState = newCheckState;
if(isFileName){
if(newCheckState == Qt::Unchecked || newCheckState == Qt::Checked){
for(int i = 0; i<rowCount(); i++){
DescriptionFileItem* child = (DescriptionFileItem*)QStandardItem::child(i);
QModelIndex childIndex = child->index();
child->model()->setData(childIndex,newCheckState, Qt::CheckStateRole);
//child->setCheckState(newCheckState);
//child->setData(newCheckState,Qt::CheckStateRole);
}
/*if(rowCount()>1){
emit this->model()->dataChanged(this->child(0)->index(),this->child(rowCount()-1)->index());
}else{
emit this->model()->dataChanged(this->child(0)->index(),this->child(0)->index());
}*/
}
}else{
emit CheckStateChanged();
}
//emit this->model()->dataChanged(this->index(),this->index());
}else{
QStandardItem::setData(value,role);
}
}
QVariant DescriptionFileItem::data( int role ) const{
if (role == Qt::CheckStateRole){
return checkedState;
}
return QStandardItem::data(role);
}
void DescriptionFileItem::UpdateCheckedStateOnChildStateChanged()
{
Qt::CheckState min = Qt::Checked;
Qt::CheckState max = Qt::Unchecked;
Qt::CheckState childState;
for(int i = 0; i<rowCount(); i++){
DescriptionFileItem* child = (DescriptionFileItem*)QStandardItem::child(i);
childState = (Qt::CheckState) child->data(Qt::CheckStateRole).toInt();
min = min>childState ? childState: min;
max = max<childState ? childState: max;
}
if(min >= max)
setData(min, Qt::CheckStateRole);
else
setData(Qt::PartiallyChecked, Qt::CheckStateRole);
}
And the construction of the connection / tree:
DescriptionFileItem* descFileStdItem = new DescriptionFileItem(descriptionFileName, true);
descFileStdItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled|Qt::ItemIsTristate);
descriptionFileSIModel.appendRow(descFileStdItem);
typedef pair<string,int> indexType;
foreach(indexType index,dataFile->indexes){
DescriptionFileItem* key_xItem = new DescriptionFileItem(index.first.c_str());
descFileStdItem->appendRow(key_xItem);
key_xItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled);
QObject::connect(key_xItem,SIGNAL(CheckStateChanged()),descFileStdItem,SLOT(UpdateCheckedStateOnModelDataChanged()));
}
EDIT: final answer, thanks to stu (see below)
void DataLoadWidget::ModelItemChanged(QStandardItem *item)
{
QStandardItem* parent = item->parent();
if(parent == 0){
//folder state changed--> update children if not partially selected
Qt::CheckState newState = item->checkState();
if(newState != Qt::PartiallyChecked){
for (int i = 0; i < item->rowCount(); i++)
{
item->child(i)->setCheckState(newState);
}
}
}
else{//child item changed--> count parent's children that are checked
int checkCount = 0;
for (int i = 0; i < parent->rowCount(); i++)
{
if (parent->child(i)->checkState() == Qt::Checked)
checkCount++;
}
if(checkCount == 0)
parent->setCheckState(Qt::Unchecked);
else if (checkCount == parent->rowCount())
parent->setCheckState(Qt::Checked);
else
parent->setCheckState(Qt::PartiallyChecked);
}
}
Unless I've misunderstood your question it seems that your solution is massively over-complicated. You should be able to do this trivially with the default QStandardItemModel implementation.
How about something like this (error handling omitted)?
QObject::connect(model, SIGNAL(itemChanged(QStandardItem*)), someObject, SLOT(modelItemChanged(QStandardItem*)));
And then in the signal handler:
void modelItemChanged(QStandardItem* item)
{
QStandardItem* parent = item->parent();
int checkCount = 0;
int rowCount = parent->rowCount();
for (int i = 0; i < rowCount; i++)
{
if (parent->child(i)->checkState() == Qt::Checked)
checkCount++;
}
switch (checkCount)
{
case 0:
parent->setCheckState(Qt::Unchecked);
break;
case rowCount:
parent->setCheckState(Qt::Checked);
break;
default:
parent->setCheckState(Qt::PartiallyChecked);
}
}
This is by no means optimal but it may be good enough for your purposes.