Qt library design with signals - qt

I am trying to design a Qt library which gives the output back to the client code using signals, but I can't quite get my head around it, I think something is wrong.
Say the library exposes a single class A as follows:
class A {
public:
void request(int data);
signals:
void response(int res);
}
So the client code instantiates an A, connects its signal to a slot, and calls request(). I initially chose to use a signal to return the output because A takes some time to elaborate the response, so I want that call to be non-blocking.
My problem is: what if I need to call request() in many different places in my code, and do different things after I receive my response? I think the question is fundamentally on the correct use of signal/slot design of Qt.
To give a concrete example, and hopefully explain myself further, I temporarily solved the issue setting a boolean before the request() to "remind" me what path of execution to take later:
void doingThis() {
doingThis = true;
request(data);
}
...
void doingThat() {
doingThis = false;
request(data);
}
...
public mySlot(int res) {
if (dointThis) {
...
} else {
...
}
}
This is hideous. What am I doing wrong?

I agree with Ludo who commented on your question.
If you pass some random number (identifier) into the request, then A can emit that same random number back with the response signal. Even if you have a bunch of slots connected to that signal, you would make them only handle the signal if the identifier was familiar to them.
class A {
public:
void request(int data, int id);
signals:
void response(int res, int id);
}
void doingThis() {
request(data, 0xaaaa);
}
...
void doingThat() {
request(data, 0xbbbb);
}
...
public mySlotA(int res, int id) {
if (id == 0xaaaa) {
...
}
}
public mySlotB(int res, int id) {
if (id == 0xbbbb) {
...
}
}
In the case above, the id is hard-coded to represent where the call came from. However, you could also randomly generate the ID. If you did that, then you'd need to save the randomly generated ID. The advantage is that you could send several different requests from doingThis() and be able to understand which response belongs to each request when they arrive back in your slot.

Related

RxJava one observable, multiple subscribers, one execution

I create an Observable from a long running operation + callback like this:
public Observable<API> login(){
return Observable.create(new Observable.OnSubscribe<API>() {
#Override
public void call(final Subscriber<? super API> subscriber) {
API.login(new SimpleLoginListener() {
#Override
public void onLoginSuccess(String token) {
subscriber.onNext(API.from(token));
subscriber.onCompleted();
}
#Override
public void onLoginFailed(String reason) {
subscriber.onNext(API.error());
subscriber.onCompleted();
}
});
}
})
}
A successfully logged-in api is the pre-condition for multiple other operations like api.getX(), api.getY() so I thought I could chain these operation with RxJava and flatMap like this (simplified): login().getX() or login().getY().
My biggest problem is now, that I don't have control over when login(callback) is executed. However I want to be able to reuse the login result for all calls.
This means: the wrapped login(callback) call should be executed only once. The result should then be used for all following calls.
It seems the result would be similar to a queue that aggregates subscribers and then shares the result of the first execution.
What is the best way to achieve this? Am I missing a simpler alternative?
I tried code from this question and experiemented with cache(), share(), publish(), refCount() etc. but the wrapped function is called 3x when I do this for all of the mentioned operators:
apiWrapper.getX();
apiWrapper.getX();
apiWrapper.getY();
Is there something like autoConnect(time window) that aggregates multiple successive subscribers?
Applying cache() should make sure login is only called once.
public Observable<API> login() {
return Observable.create(s -> {
API.login(new SimpleLoginListener() {
#Override
public void onLoginSuccess(String token) {
s.setProducer(new SingleProducer<>(s, API.from(token)));
}
#Override
public void onLoginFailed(String reason) {
s.setProducer(new SingleProducer<>(s, API.error()));
}
});
}).cache();
}
If, for some reason you want to "clear" the cache, you can do the following trick:
AtomicReference<Observable<API>> loginCache = new AtomicReference<>(login());
public Observable<API> cachedLogin() {
return Observable.defer(() -> loginCache.get());
}
public void clearLoginCache() {
loginCache.set(login());
}
Ok I think I found one major problem in my approach:
Observable.create() is a factory method so even if every single observable was working as intented, I created many of them. One way to avoid this mistake is to create a single instance:
if(instance==null){ instance = Observable.create(...) }
return instance

Parameter passing to the TinyOS Timer.

I am completely new to the tinyos and related API. I have defined a timer and starting it as below.
uses interface Timer<TMilli> as DelayTimer;
call DelayTimer.startOneShot(TIMER_PERIOD_MILLI);
Also defined a timer expiry handler as below,
event void DelayTimer.fired() {
//...
}
My requirement is that to pass an argument to this timer so that same can be used in the the timer handler function.
Can some one provide how it can be done?
There is no way to pass any parameter to the Timer directly. You need to save it in your component's state before calling startOneShot:
implementation {
uint16_t parameter;
// ...
void function(uint16_t value) {
parameter = value;
call DelayTimer.startOneShot(TIMER_PERIOD_MILLI);
}
event void DelayTimer.fired() {
// use variable parameter
}
}
However, if your case is simple and you need only to distinct between various "reasons" of calling Timer, you may use different Timer instances for different purposes:
uses interface Timer<TMilli> as LogTimer;
uses interface Timer<TMilli> as SendTimer;
And then, in an implementation:
void someFunction() {
call LogTimer.startPeriodic(5000);
// ...
}
void anotherFunction() {
call SendTimer.startOneShot(SEND_DELAY);
// ...
}
event void LogTimer.fired() {
// perform logging
}
event void SendTimer.fired() {
// send a packet
}

Is there a way to exclude a Client from a Clients.method call in SignalR?

I am evaluating SignalR (which happens to be used with Knockoutjs) to see if we can use it to notify clients of concurrency issues. Basically user "a" saves a record and users "b,c,d,e,f,g" are notified. I basically have an example working that notifies all clients. So I think I am almost there.
I came across this link and it lead me on the current path that I am on. I have also been looking at the documentation on Github.
Basically I want to exclude the a single client from the Clients.method() call. I dont see a way to loop through the clients and check the ClientId. The only other I can see to accomplish this is to maybe look at using the groups to keep track of it, but that seemed a little cumbersome, but I was having issues with that as well.
public class TicketHub : Hub
{
static int TotalTickets = 10;
public void GetTicketCount()
{
AddToGroup("ticketClients");
Clients.setTicketCount(TotalTickets);
}
public void BuyTicket()
{
if (TotalTickets > 0)
TotalTickets -= 1;
RemoveFromGroup("ticketClients");
// This will call the method ONLY on the calling client
// Caller.updateTicketCountWithNotification(TotalTickets);
// This will call the method on ALL clients in the group
Clients["ticketClients"].updateTicketCountNotify(TotalTickets);
AddToGroup("ticketClients");
Caller.updateTicketCountDontNotify(TotalTickets);
}
}
javascript code:
<script type="text/javascript">
$(document).ready(function () {
var test = $.connection.test;
$("#btnTest").click(function () {
test.testMethod();
});
test.show = function (text, guid) {
if (guid != test.guid) //notify all clients except the caller
alert(text);
};
$.connection.hub.start(function () { test.start(); });
});
</script>
Class :
public class Test : Hub
{
public void Start()
{
Caller.guid = Guid.NewGuid();
}
public void TestMethod()
{
Clients.show("test", Caller.guid);
}
}
If you want to exclude the caller from the call to the client side method you can use:
Clients.Others.clientSideMethod();
There is also Clients.AllExcept(...) that allows excluding certain people.

synchronizing slots in QThread -?

I have 2 slot handlers in QThread- derived class: one is timer handler and another is just asynchronous callback handler. Both have to modify the same data.
struct somedata {
int max;
int min;
double avg;
}
...
class MyThread: QThread {
private:
somedata m_data;
private Q_SLOTS:
void asyncCallback(int a, int b) {
m_data.max += a;
m_data.min += b;
}
void timer() {
m_data.avg =(m_data.a + m_data.b)/2;
}
}
Should the access to m_data be serialized in some fashion, although both method are in the same thread?
Thanks,
As long as you can guarantee that your data is only ever being accessed or modified by a single thread at any time, then you don't need to work about synchronizing access to that data via thread-safety constructs.
One way to verify this is to check the return value of QThread's static currentThread() function when your functions are called.
If both functions are called by same thread then you dont need to worry about the data getting changes when the other call is in the second slot.If you are not sure then the best option is to use a mutex in both the slots so that only one process or changes the value of m_data.

Blocking a Qt application during downloading a short file

I'm writing an application using Qt4.
I need to download a very short text file from a given http address.
The file is short and is needed for my app to be able to continue, so I would like to make sure the download is blocking (or will timeout after a few seconds if the file in not found/not available).
I wanted to use QHttp::get(), but this is a non-blocking method.
I thought I could use a thread : my app would start it, and wait for it to finish. The thread would handle the download and quit when the file is downloaded or after a timeout.
But I cannot make it work :
class JSHttpGetterThread : public QThread
{
Q_OBJECT
public:
JSHttpGetterThread(QObject* pParent = NULL);
~JSHttpGetterThread();
virtual void run()
{
m_pHttp = new QHttp(this);
connect(m_pHttp, SIGNAL(requestFinished(int, bool)), this, SLOT(onRequestFinished(int, bool)));
m_pHttp->setHost("127.0.0.1");
m_pHttp->get("Foo.txt", &m_GetBuffer);
exec();
}
const QString& getDownloadedFileContent() const
{
return m_DownloadedFileContent;
}
private:
QHttp* m_pHttp;
QBuffer m_GetBuffer;
QString m_DownloadedFileContent;
private slots:
void onRequestFinished(int Id, bool Error)
{
m_DownloadedFileContent = "";
m_DownloadedFileContent.append(m_GetBuffer.buffer());
}
};
In the method creating the thread to initiate the download, here is what I'm doing :
JSHttpGetterThread* pGetter = new JSHttpGetterThread(this);
pGetter->start();
pGetter->wait();
But that doesn't work and my app keeps waiting. It looks lit the slot 'onRequestFinished' is never called.
Any idea ?
Is there a better way to do what I'm trying to do ?
Instead of using a thread you can just go into a loop which calls processEvents:
while (notFinished) {
qApp->processEvents(QEventLoop::WaitForMore | QEventLoop::ExcludeUserInput);
}
Where notFinished is a flag which can be set from the onRequestFinished slot.
The ExcludeUserInput will ensure that GUI related events are ignored while waiting.
A little late but:
Do not use these wait loops, the correct way is to use the done() signal from QHttp.
The requestFinished signal from what I have seen is just for when your application has finished the request, the data may still be on its way down.
You do not need a new thread, just setup the qhttp:
httpGetFile= new QHttp();
connect(httpGetFile, SIGNAL(done(bool)), this, SLOT(processHttpGetFile(bool)));
Also do not forget to flush the file in processHttpGetFile as it might not all be on the disk.
you have to call QThread::quit() or exit() if you are done - otherwise your thread will run forever...
I chose to implement David's solution, which seemed to be the easiest.
However, I had handle a few more things :
I had to adapt the QEventLoop enum values for Qt4.3.3 (the version I'm using);
I had to track the request Id, to make sure to exit the while loop when the download request is finished, and not when another request is finished;
I added a timeout, to make sure to exit the while loop if there is any problem.
Here is the result as (more or less) pseudo-code :
class BlockingDownloader : public QObject
{
Q_OBJECT
public:
BlockingDownloaderBlockingDownloader()
{
m_pHttp = new QHttp(this);
connect(m_pHttp, SIGNAL(requestFinished(int, bool)), this, SLOT(onRequestFinished(int, bool)));
}
~BlockingDownloader()
{
delete m_pHttp;
}
QString getFileContent()
{
m_pHttp->setHost("www.xxx.com");
m_DownloadId = m_pHttp->get("/myfile.txt", &m_GetBuffer);
QTimer::singleShot(m_TimeOutTime, this, SLOT(onTimeOut()));
while (!m_FileIsDownloaded)
{
qApp->processEvents(QEventLoop::WaitForMoreEvents | QEventLoop::ExcludeUserInputEvents);
}
return m_DownloadedFileContent;
}
private slots:
void BlockingDownloader::onRequestFinished(int Id, bool Error)
{
if (Id == m_DownloadId)
{
m_DownloadedFileContent = "";
m_DownloadedFileContent.append(m_GetBuffer.buffer());
m_FileIsDownloaded = true;
}
}
void BlockingDownloader::onTimeOut()
{
m_FileIsDownloaded = true;
}
private:
QHttp* m_pHttp;
bool m_FileIsDownloaded;
QBuffer m_GetBuffer;
QString m_DownloadedFileContent;
int m_DownloadId;
};
I used QNetworkAccsessManager for same necessity. Because this class managing connections RFC base (6 proccess same time) and non-blocking.
http://qt-project.org/doc/qt-4.8/qnetworkaccessmanager.html
How about giving the GUI some amount of time to wait on the thread and then give up.
Something like:
JSHttpGetterThread* pGetter = new JSHttpGetterThread(this);
pGetter->start();
pGetter->wait(10000); //give the thread 10 seconds to download
Or...
Why does the GUI thread have to wait for the "downloader thread" at all? When the app fires up create the downloader thread, connect the finished() signal to some other object, start the downloader thread, and return. When the thread has finished, it will signal the other object which can resume your process.

Resources