Property values are lost (after each loop) when nesting libraries - arduino

I created 2 libraries to use in my Arduino code. One is a HwSwitch library, the other is a HwServo library which uses the HwSwitch library.
HwSwitch Library:
HwSwitch::HwSwitch(String switchName, int switchPort, int inputType, int pressedState)
{
Name = switchName;
SwitchPort = switchPort;
_pressedState = pressedState;
_lastCheckMillis = 0;
pinMode(switchPort, inputType);
_lastPinState = digitalRead(SwitchPort);
}
bool HwSwitch::IsPressed()
{
int currentPinState = GetPinState();
return currentPinState == _pressedState;
}
bool HwSwitch::SwitchStateChanged()
{
int currentPinState = GetPinState();
if (_lastPinState != currentPinState)
{
Serial.println("---");
Serial.println("1. Now: " + String(currentPinState) + " - Prev: " + String(_lastPinState));
_lastPinState = currentPinState;
Serial.println("2. Now: " + String(currentPinState) + " - Prev: " + String(_lastPinState));
return true;
}
return false;
}
int HwSwitch::GetPinState()
{
unsigned long ms = millis();
if ((ms - _lastCheckMillis) < 50)
{
return _lastPinState;
}
_lastCheckMillis = ms;
return digitalRead(SwitchPort);
}
HwServo Library:
HwServo::HwServo(int servoPort, int zeroPoint, HwSwitch limitSwitch)
{
_servo.attach(servoPort);
_servo.write(zeroPoint);
ServoPort = servoPort;
ZeroPoint = zeroPoint;
LimitSwitch = limitSwitch;
}
void HwServo::RotateUp()
{
_servo.write(ZeroPoint + UP);
}
void HwServo::RotateDown()
{
if (!LimitSwitch.IsPressed())
{
_servo.write(ZeroPoint + DOWN);
}
}
void HwServo::Stop()
{
_servo.write(ZeroPoint);
}
And this is how I initialized it in the Arduino code:
HwServo HwServos[] = {
HwServo(9, 94, HwSwitch("S1", 14, INPUT_PULLUP, HIGH)),
HwServo(5, 90, HwSwitch("S2", 8, INPUT_PULLUP, HIGH)),
};
void setup() { }
void loop() {
for(int i = 0; i < 2; i++)
{
HwServo hwServo = HwServos[i];
if (hwServo.LimitSwitch.SwitchStateChanged())
{
SendSwitchStateUpdate(hwServo.LimitSwitch);
if (hwServo.LimitSwitch.IsPressed())
{
hwServo.Stop();
}
}
}
}
Now finally to the problem! As you can see in the HwSwitch library I output some data using Serial.println. Here I can see that _lastPinState is successfully updated, but gets reset after every loop. However, when I create a HwSwitch directly and use it, _lastPinState is not reset. In other words, the resetting of the value only seems to occur when the HwSwitch library is used inside the HwServo library.
Appearently this has something to do with the pointers? I am probably initializing my classes incorrectly, but I have no idea how to fix it. Anyone that can help with (and preferably explain) this issue?

I don't have my Arduino on me right now, but I took look and re-wrote your code, added the omitted constructors at my best guess, and got it to compile. There were some things which needed corrected. I'm sure there are other ways, but this is what I did.
For complete code, go here.
First, I created some pointers to objects I'd like to stick around, like so:
HwServo *HwServos[2];
HwSwitch *s1;
HwSwitch *s2;
HwServo *sv1;
HwServo *sv2;
Now each is reserved in memory on the Arduino.
Now, construct the objects in setup():
void setup() {
s1 = new HwSwitch("S1", 14, INPUT_PULLUP, HIGH);
s2 = new HwSwitch("S2", 8, INPUT_PULLUP, HIGH);
sv1 = new HwServo(9, 94, *s1);
sv2 = new HwServo(5, 90, *s2);
//Now, since you're going through an array:
HwServos[0] = sv1;
HwServos[1] = sv2;
}
Use that setup function!!! Maybe not always necessary, or in some cases even recommended, but it's nice to collect things which only need created once there, especially is this case.
Note that new was not used inside the scope of either object, but rather in the scope of the program... So no fancy destructors in your objects are required. Normally, you'd worry about deleting them all before program termination (or whenever best suited), but in Arduino's case, it'll just lose power and kill everything anyway.
You should change your class definitions to this:
class HwSwitch {
public:
String Name;
int SwitchPort;
int _pressedState;
int _lastCheckMillis;
int _lastPinState;
HwSwitch(String, int, int, int);
bool IsPressed();
bool SwitchStateChanged();
int GetPinState();
};
class HwServo {
public:
HwServo();
HwServo(int, int, HwSwitch &);
int ServoPort;
int ZeroPoint;
HwSwitch & LimitSwitch;
void RotateUp();
void RotateDown();
void Stop();
Servo _servo;
};
Note: I made everything public, feel free to move private stuff back to private if you wish.
I changed the constructors to:
HwSwitch::HwSwitch(String switchName, int switchPort, int inputType, int pressedState)
{
Name = switchName;
SwitchPort = switchPort;
_pressedState = pressedState;
_lastCheckMillis = 0;
pinMode(switchPort, inputType);
_lastPinState = digitalRead(SwitchPort);
}
HwServo::HwServo(int servoPort, int zeroPoint, HwSwitch &limitSwitch)
{
_servo.attach(servoPort);
_servo.write(zeroPoint);
ServoPort = servoPort;
ZeroPoint = zeroPoint;
LimitSwitch = limitSwitch;
}
And I modified loop() like so:
void loop() {
// put your main code here, to run repeatedly:
for(int i = 0; i < 2; i++)
{
if (HwServos[i]->LimitSwitch.SwitchStateChanged())
{
SendSwitchStateUpdate(HwServos[i]->LimitSwitch);
if (HwServos[i]->LimitSwitch.IsPressed())
{
HwServos[i]->Stop();
}
}
}
}

Related

What is rosidl_runtime_c__double__Sequence type?

I'm trying to use a teensy 4.1 as an interface between an encoder and ROS thanks to micro-ros (arduino version).
I would like to publish position of a wheel to the /jointState topic with the teensy but there is no example on the micro-ros arduino Github repo.
I've tried to inspect the sensormsgs/msg/jointState message struct but everything is a bit fuzzy and I don't understand how to make it works. I can't understand what is rosidl_runtime_c__double__Sequence type.
I've tried several things but I always get an error about operand types
no match for 'operator=' (operand types are 'rosidl_runtime_c__String' and 'const char [18]')
msg.name.data[0] = "drivewhl_1g_joint";
Here is my arduino code
#include <micro_ros_arduino.h>
#include <stdio.h>
#include <rcl/rcl.h>
#include <rcl/error_handling.h>
#include <rclc/rclc.h>
#include <rclc/executor.h>
#include <sensor_msgs/msg/joint_state.h>
rcl_publisher_t publisher;
sensor_msgs__msg__JointState msg;
rclc_executor_t executor;
rclc_support_t support;
rcl_allocator_t allocator;
rcl_node_t node;
rcl_timer_t timer;
#define LED_PIN 13
#define RCCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){error_loop();}}
#define RCSOFTCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){}}
void error_loop(){
while(1){
digitalWrite(LED_PIN, !digitalRead(LED_PIN));
delay(100);
}
}
void timer_callback(rcl_timer_t * timer, int64_t last_call_time)
{
RCLC_UNUSED(last_call_time);
if (timer != NULL) {
RCSOFTCHECK(rcl_publish(&publisher, &msg, NULL));
//
//Do not work
//msg.name=["drivewhl_1g_joint","drivewhl_1d_joint","drivewhl_2g_joint","drivewhl_2d_joint"];
//msg.position=["1.3","0.2", "0","0"];
msg.name.size = 1;
msg.name.data[0] = "drivewhl_1g_joint";
msg.position.size = 1;
msg.position.data[0] = 1.85;
}
}
void setup() {
set_microros_transports();
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
delay(2000);
allocator = rcl_get_default_allocator();
//create init_options
RCCHECK(rclc_support_init(&support, 0, NULL, &allocator));
// create node
RCCHECK(rclc_node_init_default(&node, "micro_ros_arduino_node", "", &support));
// create publisher
RCCHECK(rclc_publisher_init_default(
&publisher,
&node,
ROSIDL_GET_MSG_TYPE_SUPPORT(sensor_msgs, msg, JointState),
"JointState"));
// create timer,
const unsigned int timer_timeout = 1000;
RCCHECK(rclc_timer_init_default(
&timer,
&support,
RCL_MS_TO_NS(timer_timeout),
timer_callback));
// create executor
RCCHECK(rclc_executor_init(&executor, &support.context, 1, &allocator));
RCCHECK(rclc_executor_add_timer(&executor, &timer));
}
void loop() {
delay(100);
RCSOFTCHECK(rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100)));
}
I'm a beginner with Ros and C, it may be a very dumb question but I don't know how to solve it. Thanks for your help !
rosidl_runtime_c__String__Sequence is a structure used to old string data that is to be transmitted. Specifically it is a sequence of rosidl_runtime_c__String data. You're running into an error because rosidl_runtime_c__String is also a struct itself with no custom operators defined. Thus, your assignment fails since the types are not directly convertible. What you need to do instead is use the rosidl_runtime_c__String.data field. You can see slightly more info here
void timer_callback(rcl_timer_t * timer, int64_t last_call_time)
{
RCLC_UNUSED(last_call_time);
if (timer != NULL) {
//msg.name=["drivewhl_1g_joint","drivewhl_1d_joint","drivewhl_2g_joint","drivewhl_2d_joint"];
//msg.position=["1.3","0.2", "0","0"];
msg.name.size = 1;
msg.name.data[0].data = "drivewhl_1g_joint";
msg.name.data[0].size = 17; //Size in bytes excluding null terminator
msg.position.size = 1;
msg.position.data[0] = 1.85;
RCSOFTCHECK(rcl_publish(&publisher, &msg, NULL));
}
}
I also spent quite some time trying to get publishing JointState message from my esp32 running microros, and also couldn't find working example. Finally, i was successful, maybe it will help someone.
In simple words:
.capacity contains max number of elements
.size contains actual number of elements (strlen in case of string)
.data should be allocated as using malloc as .capacity * sizeof()
each string within sequence should be allocated separately
This is my code that allocates memory for 12 joints, named j0-j11. Good luck!
...
// Declarations
rcl_publisher_t pub_joint;
sensor_msgs__msg__JointState joint_state_msg;
...
// Create publisher
RCCHECK(rclc_publisher_init_default(&pub_joint, &node,
ROSIDL_GET_MSG_TYPE_SUPPORT(sensor_msgs, msg, JointState),
"/hexapod/joint_state"));
//Allocate memory
joint_state_msg.name.capacity = 12;
joint_state_msg.name.size = 12;
joint_state_msg.name.data = (std_msgs__msg__String*) malloc(joint_state_msg.name.capacity*sizeof(std_msgs__msg__String));
for(int i=0;i<12;i++) {
joint_state_msg.name.data[i].data = malloc(5);
joint_state_msg.name.data[i].capacity = 5;
sprintf(joint_state_msg.name.data[i].data,"j%d",i);
joint_state_msg.name.data[i].size = strlen(joint_state_msg.name.data[i].data);
}
joint_state_msg.position.size=12;
joint_state_msg.position.capacity=12;
joint_state_msg.position.data = malloc(joint_state_msg.position.capacity*sizeof(double));
joint_state_msg.velocity.size=12;
joint_state_msg.velocity.capacity=12;
joint_state_msg.velocity.data = malloc(joint_state_msg.velocity.capacity*sizeof(double));
joint_state_msg.effort.size=12;
joint_state_msg.effort.capacity=12;
joint_state_msg.effort.data = malloc(joint_state_msg.effort.capacity*sizeof(double));
for(int i=0;i<12;i++) {
joint_state_msg.position.data[i]=0.0;
joint_state_msg.velocity.data[i]=0.0;
joint_state_msg.effort.data[i]=0.0;
}
....
//Publish
RCSOFTCHECK(rcl_publish(&pub_joint, &joint_state_msg, NULL));

Usage of std::unique_ptr when moving object to worker thread in Qt

I am trying to update old code by removing class destructors. Now I got to the following code situation with a Qt master and a Qt slave (the slave is created, and moved to a second thread afterwards):
My slave was formerly written as:
serial_controller_worker::serial_controller_worker(const QString &portname, int waitTimeout, int BaudRate, int numStopBits, bool parity, bool useParity, bool useHex)
{
this->portName = portname;
this->waitTimeout = waitTimeout;
this->baudrate = BaudRate;
this->numStopBits = numStopBits;
this->useParity = useParity;
this->parity = parity;
this->useHex = useHex;
this->serial = new QSerialPort(this);
this->storage = "";
this->delay_write = 0;
}
serial_controller_worker::~serial_controller_worker()
{
if(this->serial->isOpen())
this->serial->close();
if(this->serial != NULL)
delete this->serial;
}
and was called in the master as
serial_controller_worker *newWorker = new serial_controller_worker(portName, waitTimeout, BaudRate, numStopBits, parity, useParity, useHex);
newWorker->moveToThread(&workerThread);
this->Hex = Hex;
connect(&workerThread, &QThread::finished, newWorker, &QObject::deleteLater);
connect(this, &Serial_port_library::newTransaction, newWorker, &serial_controller_worker::transaction);
connect(this, &Serial_port_library::connect_now, newWorker, &serial_controller_worker::connectToSerial);
connect(newWorker, &serial_controller_worker::response, this, &Serial_port_library::response_slot);
connect(newWorker, &serial_controller_worker::error_Val, this, &Serial_port_library::connectError);
workerThread.start();
emit this->connect_now();
Now I would like to transfer the slave to a constructor-only class, thus removing the destructor in the slave class. Nevertheless I still have to keep the destruction functions. Thus I created a new function for that:
void serial_controller_worker::delete_serial_controller_worker()
{
if(this->serial->isOpen())
this->serial->close();
if(this->serial != NULL)
delete this->serial;
}
and created a std::unique_ptr with a custom destructor function:
struct WorkerFree
{
void operator() (serial_controller_worker *p) const { p->delete_serial_controller_worker(); }
};
class serial_controller_master{
private:
std::unique_ptr<serial_controller_worker, WorkerFree> serial_worker;
public:
serial_controller_master();
}
serial_controller_master::serial_controller_master()
{
serial_worker.reset(serial_controller_worker(portName, waitTimeout, BaudRate, numStopBits, parity, useParity, useHex));
serial_worker->moveToThread(&workerThread);
this->Hex = Hex;
connect(&workerThread, &QThread::finished, serial_worker, &QObject::deleteLater);
}
How can I tell Qt to use my "destructor" when calling QObject::deleteLater() instead of trying to find another destructor, and how do I use the connect-calls later correctly? Currently I get errors like
error: no matching function for call to ‘Serial_port_library::connect(QThread*, void (QThread::*)(QThread::QPrivateSignal), std::unique_ptr<serial_controller_worker, WorkerFree>&, void (QObject::*)())’
connect(&workerThread, &QThread::finished, serial_worker, &QObject::deleteLater);

Reading string from struct in Arduino PROGMEM

I want to read a string from a struct stored in Arduino PROGMEM:
struct commandCode {
int code;
const char *name;
};
const PROGMEM commandCode commands[LAST_COMMAND] = {
{ CMD_DEMO, "DEMO" } ,
{ CMD_STOP, "STOP"} ,
{ CMD_FORWARD, "FORWARD"},
{ CMD_BACKWARD, "BACKWARD"},
{ CMD_TURN_LEFT, "TURN LEFT"},
{ CMD_TURN_RIGHT, "TURN RIGHT"},
{ CMD_WAIT, "WAIT"},
{ CMD_WAIT_DONE, "WAIT DONE"},
};
This code prints the string just fine:
void CommandCodes::show() {
Serial.print(LAST_COMMAND);
Serial.println(" Comands Defined:");
for (int i = FIRST_COMMAND; i < LAST_COMMAND; i++) {
CommandCodes::commandCode cmd = commands[i];
showCommand(cmd);
}
}
void CommandCodes::showCommand(commandCode cmd) {
if (cmd.code > FIRST_COMMAND) {
Serial.print(F("["));
Serial.print(cmd.code);
Serial.print(F("] "));
Serial.println(cmd.name);
}
}
This code bombs and restarts the program:
const char* CommandCodes::name(int code) {
for (int i = FIRST_COMMAND; i < LAST_COMMAND; i++) {
CommandCodes::commandCode cmd = commands[i];
if (cmd.code == code) {
return cmd.name;
}
}
return NULL;
}
What is the code to return a pointer to cmd.name?
As the structure only contains a pointer, not the string data, the strings are still stored in RAM.
Also you aren't reading from PROGMEM when you access the data, the fact it's working in certain situations is just luck, but it's still incorrect.
To place all the data in PROGMEM, you'll need to allocate space inside the struct for it. As the largest string is 11 chars + null you can make the length 12.
struct commandCode {
int code;
const char name[12];
};
const commandCode commands[] PROGMEM = {
{ CMD_DEMO, "DEMO" } ,
{ CMD_STOP, "STOP"} ,
{ CMD_FORWARD, "FORWARD"},
{ CMD_BACKWARD, "BACKWARD"},
{ CMD_TURN_LEFT, "TURN LEFT"},
{ CMD_TURN_RIGHT, "TURN RIGHT"},
{ CMD_WAIT, "WAIT"},
{ CMD_WAIT_DONE, "WAIT DONE"}
}
As the internals of each struct are in PROGMEM you need to read them using special functions. You cannot read them directly.
This also means you cannot copy an item like you have done:
CommandCodes::commandCode cmd = commands[i];
But you can use a reference.
const commandCode &cmd = commands[i];
However, like I mentioned above, the elements of the referenced struct still need to be accessed properly.
For an integer, you need to use pgm_read_word. For the strings, you can trick the Serial class into printing it for you as it handles flash strings (like where you use the F() macro). This can be done by casting the pointer to a const __FlashStringHelper*.
Here is a working sketch showing how to access each part properly. Give it a test and try and work out what I've done. I'm sure you'll have some questions, so just add them to the comments of this answer and I'll update my answer for you.
struct commandCode {
int code;
const char name[12];
};
enum COMMANDS{
CMD_DEMO,
CMD_STOP,
CMD_FORWARD,
CMD_BACKWARD,
CMD_TURN_LEFT,
CMD_TURN_RIGHT,
CMD_WAIT,
CMD_WAIT_DONE,
};
const commandCode commands[] PROGMEM = {
{ CMD_DEMO, "DEMO" } ,
{ CMD_STOP, "STOP"} ,
{ CMD_FORWARD, "FORWARD"},
{ CMD_BACKWARD, "BACKWARD"},
{ CMD_TURN_LEFT, "TURN LEFT"},
{ CMD_TURN_RIGHT, "TURN RIGHT"},
{ CMD_WAIT, "WAIT"},
{ CMD_WAIT_DONE, "WAIT DONE"}
};
#define FIRST_COMMAND 0
#define LAST_COMMAND sizeof(commands) / sizeof(*commands)
#define FSH (const __FlashStringHelper*) //A helper to allow printing the PROGMEM strings.
void show() {
for (int i = FIRST_COMMAND; i < LAST_COMMAND; i++) {
const commandCode &cmd = commands[i];
showCommand(cmd);
}
}
void showCommand(const commandCode &cmd) {
if ( pgm_read_word( &cmd.code ) > FIRST_COMMAND) {
Serial.print(F("["));
Serial.print( pgm_read_word( &cmd.code ) );
Serial.print(F("] "));
Serial.println( FSH( cmd.name ) );
}
}
const char* name(int code) {
for (int i = FIRST_COMMAND; i < LAST_COMMAND; i++) {
const commandCode &cmd = commands[i];
if (pgm_read_word(&cmd.code) == code) {
return cmd.name; //As cmd.name resolves to a pointer it can be passed back as is.
//However to use the 'pointed to data' it will have to be accessed properly.
}
}
return NULL;
}
void setup() {
Serial.begin(9600);
Serial.println("Show test");
show();
Serial.println("Name test");
for (int i = FIRST_COMMAND; i < LAST_COMMAND; i++) {
Serial.println( FSH( name(i) ) );
}
Serial.println("Done");
}
void loop() {}

Qt Unique connection

I was wondering either there is a possibility in Qt, co create a signal-slot connection, that will automatically break all other connections to this particular slot/ signal? I would appreciate all help.
Qt doesn't provide such functionality directly. Moreover, it's impossible to iterate signal-slot connections, so you can't even implement it yourself in general.
What you should be doing is keeping track of the connections that you initiate yourself, and removing them as appropriate.
For example:
enum class ConnectionDisposal { Dispose, Keep };
class UniqueConnector {
Q_DISABLE_COPY(UniqueConnector)
QMetaObject::Connection m_conn;
ConnectionDisposal m_cd;
public:
explicit UniqueConnector(ConnectionDisposal cd = ConnectionDisposal::Dispose) :
m_cd(cd) {}
~UniqueConnector() { if (m_cd == ConnectionDisposal::Dispose) disconnect(); }
template <typename T, typename R>
QMetaObject::Connection connect(const QObject * tx, T txf,
const QObject * rx, R rxf,
Qt::ConnectionType type = Qt::AutoConnection) {
QObject::disconnect(m_conn);
return m_conn = QObject::connect(tx, txf, rx, rxf, type);
}
template <typename T, typename R>
QMetaObject::Connection connect(const QObject * tx, T txf, R rxf) {
QObject::disconnect(m_conn);
return m_conn = QObject::connect(tx, txf, rxf);
}
bool disconnect() { return QObject::disconnect(m_conn); }
};
The UniqueConnector allows only one connection to exist on its instance. So, for each unique connection, you need one UniqueConnector instance. The connection is removed upon destruction of the connector, unless you specify otherwise.
So, you can use following scenario:
if (!connect(senderObject, SIGNAL(signalName()), receiverObject, SLOT(slotName()), Qt::UniqueConnection))
{
QMetaObject::disconnect(senderObject, senderObject->metaObject()->indexOfSignal(SIGNAL(signalName())),
NULL, receiverObject->metaObject()->indexOfSlot(SLOT(slotName())));
connect(senderObject, SIGNAL(signalName()), receiverObject, SLOT(slotName()));
}
I wrote this function very quickly and tested it, it seems that it really works! Yes, algorithm is not perfect, it probably can be improved, but it requires more time. Try this solution and tell result:
QMetaObject::Connection uniqueConnect(const QObject *sender, const char *signal, const QObject *receiver , const char *slot, Qt::ConnectionType type = Qt::AutoConnection)
{
const QMetaObject * metaSender = sender->metaObject();
const QMetaObject * metaReceiver = receiver->metaObject();
int signalIndex = metaSender->indexOfSignal(signal);
int slotIndex = metaReceiver->indexOfSlot(slot);
//iterate throw all methods! and discover only signals and slots
for (int i = 0; i < metaSender->methodCount(); ++i)
{
for (int j = 0; j < metaReceiver->methodCount(); ++j)
{
if(metaSender->method(i).methodType() == QMetaMethod::Signal)
{
if(metaReceiver->method(j).methodType() == QMetaMethod::Slot)
{
//immitate SIGNAL SLOT macro, see more in the end of the answer.
QByteArray finalSignal = "2" + metaSender->method(i).methodSignature();
QByteArray finalSlot = "1" + metaReceiver->method(j).methodSignature();
QObject::disconnect(sender,finalSignal.data(),receiver,finalSlot.data());
}
}
}
}
return QObject::connect(sender,signal,receiver,slot,type);
}
Test:
QObject *obj = new QObject;
connect(obj,SIGNAL(objectNameChanged(QString)),this,SLOT(testFunc()));
connect(obj,SIGNAL(destroyed()),this,SLOT(testFunc()));
obj->setObjectName("newNAme");
uniqueConnect(obj,SIGNAL(objectNameChanged(QString)),this,SLOT(showMaximized()));
obj->setObjectName("more");
Output:
testFunc called once!!!
...maximized window...
How to immitate SIGNAL SLOT macro.

QT C++ wait till specific time to execute function

I am trying to create an app that holds a list of tasks and for each time a deadline, now i want to execute a function (show a popup) once a deadline is met.
i have this:
#ifndef TIMER_H
#define TIMER_H
#include <QWidget>
#include <QTimer>
#include <QtGui>
#include <QObject>
class Timer : public QWidget
{
Q_OBJECT
public:
Timer(QWidget * parent = 0);
void setTimer(QString title, QString description, QDate date, QTime reminderTime);
public slots:
void showWarning() {QString show = tit;
QPushButton * thanks = new QPushButton(QObject::tr("Thank you for reminding me!"));
show.append("\n");
show.append(des);
QMessageBox popup;
popup.setText(show);
popup.setWindowTitle("Calendar : Reminder");
popup.setDefaultButton(thanks);
popup.exec();
}
private:
QString tit;
QString des;
QDateTime now;
QDateTime timeoftheaction;
QTimer *timer;
};
cpp file:
#endif // TIMER_H
#include "timer.h"
#include <iostream>
using namespace std;
Timer::Timer(QWidget * parent)
: QWidget(parent)
{
}
void Timer::setTimer(QString title, QString description, QDate date, QTime reminderTime)
{
now.currentDateTime();
timer = new QTimer;
tit = title;
des = description;
timeoftheaction.setDate(date);
timeoftheaction.setTime(reminderTime);
connect(timer, SIGNAL(timeout()),this,SLOT(showWarning()));
timer->start(now.secsTo(timeoftheaction)*1000);
}
Yet function showWarning is never being called...
no compilation errors, function showWarning works perfectly (tested)
I think the error is in the connect but i am not sure...
Short answer:
Change:
now.currentDateTime();
to
now = QDateTime::currentDateTime();
Longish answer:
currentDateTime() is a static function which instead of changing your existing object, actually returns a new QDataTime object. Although you are calling it as a member function, it's still called as a static one and leaves your object intact, which is still invalid.
Your later call to secsTo() on an invalid data time probably gets you an negative or really large number that either has passed (never going to trigger) or really late in the future.
Here is something that might be a more generic solution.
#include <QThread>
#include <QTimer>
#include <QObject>
#include <map>
/**
* Singleton to implement simple 'relative' timer.
* Implements busy wait and also timeout-notifications (useful to monitor operations that could hang, etc).
*
* If the whole application is stalled (e.g. when a new device is connected), and we only want to
* wait for a period during which application was 'really' working (not just hanging waiting for OS)
* - then ticks will be missed too. This way - it's should be possible to avoid unnecessary timeouts
* that could happen if global time was measured (especially annoying on WINdows platforms)
*/
class RelativeTimer : public QObject
{
Q_OBJECT
typedef std::multimap <unsigned int, std::pair <QObject*, QString> > Notifications;
public:
/**
* Call to busy-wait for number of ticks.
*/
static void wait_num_of_ticks(unsigned int num_of_ticks_to_wait)
{
if(self.timer_id == 0)
{
qDebug("timer not initialised, call 'RelativeTimer::Init()'");
return;
}
if(num_of_ticks_to_wait > 0)
{
unsigned long until = self.tick_counter + num_of_ticks_to_wait; // it's ok if it wraps around..
while(self.tick_counter != until)
{
QCoreApplication::processEvents(); // let others to their job..
// or comment above out and just busy wait..
}
}
}
/**
* Call to busy-wait until ms_to_wait have elapsed.
* If ms_to_wait is < tick period
* Interval will define 'tick' frequency (and accuracy).
*/
static void wait_ms(unsigned int ms_to_wait)
{
wait_num_of_ticks(num_of_ticks_to_wait(ms_to_wait));
}
/**
* Call to schedule a notification after a given timeout.
* returns notification_id that can be used to cancel this notification.
*/
static unsigned long notify_timeout_ms(unsigned int ms_to_wait,
QObject *receiver,
const char* method_name)
{
unsigned long ticks_to_wait = 0;
if(receiver && method_name)
{
ticks_to_wait = num_of_ticks_to_wait(ms_to_wait);
if(ticks_to_wait > 1)
{
ticks_to_wait += self.tick_counter;
if(ticks_to_wait == 0) // avoid 0 - make it one tick more (to alow to see if successfully added this notif)
{
ticks_to_wait = 1;
}
self.notifications.insert(std::make_pair(ticks_to_wait,
std::make_pair(receiver, method_name)));
qDebug("added delayed call..");
}
else
{
QMetaObject::invokeMethod(receiver, method_name, Qt::QueuedConnection);
ticks_to_wait = 0;
}
}
return ticks_to_wait;
}
/**
* Call to cancel a notification with a given id.
* Specify name if there were more notification with the same id (scheduled for the same tick).
* returns true on successfull cancellation, false otherwise.
*/
static bool cancel_timeout_notification(unsigned long notification_id, QString notification_name="")
{
bool cancelled = false;
if(self.notifications.size())
{
std::pair<Notifications::iterator, Notifications::iterator> to_cancel = self.notifications.equal_range(notification_id);
Notifications::iterator n = to_cancel.first;
for( ;n != to_cancel.second; ++n)
{
if(notification_name.size()== 0 || n->second.second == notification_name)
{
self.notifications.erase(n);
cancelled = true;
break;
}
}
}
return cancelled;
}
static const unsigned int default_tick_period_ms = 100;
/**
* Call this method after event loop is created- to initiate (re-start) timer.
* tick period defines 'tick' frequency (and accuracy of the timer)
* (note on Windows that there's no point to go down below 100ms).
*/
static void Init(unsigned int tick_period_ms = default_tick_period_ms)
{
self.moveToThread(&self.thread);
self.thread.start();
while(!self.thread.isRunning());
self.current_interval = tick_period_ms;
// InitMe() should execute in the thread context..
QMetaObject::invokeMethod(&self, "InitMe", Qt::QueuedConnection);
}
private:
/**
* Internal method to convert ms to number of ticks.
*/
static unsigned int num_of_ticks_to_wait(unsigned int ms_to_wait)
{
if(ms_to_wait > self.current_interval)
{
if(ms_to_wait % self.current_interval)
{
// average it..
ms_to_wait = ms_to_wait + self.current_interval / 2;
}
ms_to_wait /= self.current_interval;
}
else
{
ms_to_wait = 0;
}
return ms_to_wait;
}
/**
* Internal method to handle tick. Increments counter and invokes notifications.
*/
void timerEvent ( QTimerEvent* /*event*/ )
{
tick_counter++;
if(notifications.size())
{
std::pair<Notifications::iterator, Notifications::iterator> to_notify = notifications.equal_range(tick_counter);
Notifications::iterator n = to_notify.first;
for( ;n != to_notify.second; ++n)
{
QMetaObject::invokeMethod(n->second.first,
n->second.second.toStdString().c_str(),
Qt::QueuedConnection);
}
notifications.erase(to_notify.first, to_notify.second);
}
}
private slots:
/**
* Internal slot to initialize the timer. Should be called in this->timer context.
*/
void InitMe()
{
if(timer_id != 0)
{
killTimer(timer_id);
timer_id = 0;
}
tick_counter = 0;
timer_id = self.startTimer(self.current_interval);
}
private:
RelativeTimer()
{
}
~RelativeTimer()
{
thread.quit();
thread.wait();
}
QThread thread;
Notifications notifications;
int timer_id;
unsigned int current_interval;
unsigned long tick_counter;
static RelativeTimer self; // implement it as a signleton.. Define it in your C file, e.g.:
// RelativeTimer RelativeTimer::self;
};
Can be used like:
CurrQObjectClass::OnTimeout()
{
// ...
}
CurrQObjectClass::SomeMethod()
{
RelativeTimer::notify_timeout_ms(5000, this, "OnTimeout");
}
but also for busy-waiting:
RelativeTimer::wait_ms(2000);
Enjoy.

Resources