[prev in list] [next in list] [prev in thread] [next in thread] 

List:       koffice-devel
Subject:    design discussion: progress classes in KOffice
From:       Jos van den Oever <jos.van.den.oever () kogmbh ! com>
Date:       2010-08-10 7:22:49
Message-ID: 201008101240.50113.jos.van.den.oever () kogmbh ! com
[Download RAW message or body]

Hi all,

I've been using the progress classes quite a bit the last week and come to the 
conclusion that I do not like them. Mainly I think the api of the classes 
KoUpdater, KoUpdaterPrivate, KoProgressUpdater is confusing and overly heavy.

So I've come up with a new design in the form of a header file and I would like 
people to have a look at it and see if they like it.

Progress reporting should be able to deal with
 - named tasks
 - splitting tasks up in subtasks
 - weighted tasks, the weight is an estimate of how much time a task takes 
relative to other tasks on the same level
 - tasks running in threads; for each task progress reporting is allowed to be 
done in one arbitrary thread
 - support having a main progress bar and progress bar for subtasks
 - support logging of progress with timestamps by simply implementing another 
proxy

Advantages over current approach
 - less public classes, less functions, less confusion
 - more robust API due to automatic unwinding of the stack
 - only one range: 0-100
 - no mixing in of unassociated function (interrupt, abort)
 - no use of QObject for more lightweight reporting
 - more foolproof

This design is simply a suggestion which could be implemented if people like 
it better than the current API.

Cheers,
Jos


-- 
Jos van den Oever, software architect
+49 391 25 19 15 53
http://kogmbh.com/legal/

["p.cpp" (text/x-c++src)]

#include "p.h"

/** very incomplete implementation, not relevant for interface discussion **/

class ProgressTaskStackPrivate {
  public:
    ProgressTask createSubTask(int, const QString &) {
      return ProgressTask(this);
    }
};

class ProgressTaskPrivate {
  public:
    ProgressTaskStackPrivate * const stack;
    
    ProgressTaskPrivate(ProgressTaskStackPrivate *stack_)
        :stack(stack_) {}
};

ProgressProxy::~ProgressProxy() {
}

ProgressTaskStack::ProgressTaskStack()
    :d(new ProgressTaskStackPrivate()) {
  
}
ProgressTaskStack::~ProgressTaskStack() {
  // check that all tasks have been deleted already
  delete d;
}
ProgressTask
ProgressTaskStack::createSubTask(int weight, const QString &name) {
  return d->createSubTask(weight, name);
}

ProgressTask::ProgressTask(const ProgressTask& p)
    :d(p.d) {
}
ProgressTask::ProgressTask(ProgressTaskStackPrivate *stack)
  :d(new ProgressTaskPrivate(stack)) {
}

ProgressTask::~ProgressTask() {
  delete d;
}
ProgressTask
ProgressTask::createSubTask(int, const QString &) {
  return ProgressTask(d->stack);
}
void
ProgressTask::start() {
}
void
ProgressTask::setProgress(int) {
}


["p.h" (text/x-chdr)]

#ifndef P_H
#define P_H

#include <QtCore/QString>

class ProgressTask;
class ProgressTaskPrivate;
class ProgressTaskStackPrivate;

/**
 * ProgressProxy is a simple interface for receiving progress events.
 * Implementations of this interface can report to e.g. a progress bar
 * or a profiler.
 **/
class ProgressProxy {
public:
    /**
     * Destructor
     **/
    virtual ~ProgressProxy();
    /**
     * Handle a progress report message for the task to associated task.
     * This function should be implemented in derived classes.
     * The implementation of this function should be thread-safe
     * if tasks that are being reported on will be run from multiple threads.
     * @param value Current progress value, must be in the range from 0 to 100.
     * @param task The task of which the progress is reported
     **/
    virtual void setProgress(int value, const ProgressTask& task) = 0;
};

/**
 * ProgressTaskStack is a class that keeps track of ProgressTask objects.
 * It holds the stack of progress tasks and allows to put new subtasks on the
 * stack by creating them. When the task goes out of scope, it is removed
 * from the stack.
 **/
class ProgressTaskStack {
public:
    /**
     * Constructor
     **/
    ProgressTaskStack();
    /**
     * Destructor
     * All tasks created from this stack should have gone out of scope
     * or have otherwise been deleted before ProgressTaskStack is destructed.
     **/
    ~ProgressTaskStack();
    /**
     * Create a subtask from the current task.
     * The current task is the task that has last been started in this thread.
     *
     **/
    ProgressTask createSubTask(int weight, const QString &name);
private:
    // do not allow copying
    explicit ProgressTaskStack(const ProgressTaskStack &);
    void operator=(const ProgressTaskStack &);
    ProgressTaskStackPrivate * const d;
};

class ProgressTask {
friend class ProgressTaskStackPrivate;
public:
    /**
     * Create a shallow copy of this task.
     **/
    ProgressTask(const ProgressTask &);
    /**
     * Destructor
     **/
    ~ProgressTask();
    /**
     * Return the name of the task.
     **/
    QString getName() const;
    /**
     * Set a proxy to report on the progress status of this task.
     **/
    void addProgressProxy(ProgressProxy *proxy);
    /**
     * Remove a proxy to report on the progress status of this task.
     **/
    void removeProgressProxy(ProgressProxy *proxy);
    /**
     * Create a subtask.
     **/
    ProgressTask createSubTask(int weight, const QString &name);
    /**
     * Start this task.
     * This call also makes this task the current task for the thread,
     * which means the ProgressTaskStack uses this task for creating
     * subtasks of in this thread.
     **/
    void start();
    /**
     * Set the progress of this task.
     * Once you call this, you cannot call createSubTask.
     * A task has either direct progress reporting or subtasks.
     * @param progress Progress of the task in the inclusive range 0-100
     **/
    void setProgress(int progress);
    /**
     * Get the parent task
     * This function is useful for logging purposes where one would like
     * to write out the parents of a particular task.
     * @return parent task or 0 if this is the top tasks
     **/
    const ProgressTask *parent() const;
private:
    // do not allow copying or implicit creation
    ProgressTask();
    void operator=(const ProgressTask &);
    
    ProgressTask(ProgressTaskStackPrivate * stack);
    
    ProgressTaskPrivate * const d;
};

#endif

["main.cpp" (text/x-c++src)]

#include "p.h"

/** use cases **/

/** dummy proxy class for discussion purposes **/
class MyProgressProxy : public ProgressProxy {
public:
    virtual void setProgress(int value, const ProgressTask& currentTask);
};

void
MyProgressProxy::setProgress(int /*value*/, const ProgressTask& /*currentTask*/) {
}

class Document {
  private:
    ProgressTaskStack m_progressstack;
  public:
    ProgressTaskStack* progressStack() {
      return &m_progressstack;
    }
};

void
function1(Document* doc) {
    // initialize all subtasks
    ProgressTask progress = doc->progressStack()->createSubTask(1, "myfunction1");
    progress.start();
    sleep(1);
    progress.setProgress(50);
    sleep(1);
    progress.setProgress(100);
    // destructors clean up and unwind the stack
}

void
function2(Document* doc) {
    // initialize all subtasks
    ProgressTask progress = doc->progressStack()->createSubTask(1, "myfunction2");
    ProgressTask task1 = progress.createSubTask(1, "task1");
    ProgressTask task2 = progress.createSubTask(5, "task2");

    // start work and reporting progress

    task1.start();
    sleep(1);
    task1.setProgress(50);
    sleep(1);
    task1.setProgress(100);
    
    task2.start();
    sleep(5);
    task2.setProgress(50);
    sleep(5);
    task2.setProgress(100);

    // destructors clean up and unwind the stack
}
/** dummy threading class for discussion purposes **/
class Runner {
  private:
    Document *doc;
    ProgressTask task;
  public:
    Runner(Document* d, const ProgressTask &task);
    void start() {}
    void run();
    void wait() {}
  
};

void
parallelfunction(Document *doc) {
    ProgressTask progress = doc->progressStack()->createSubTask(1, "myfunction");
    ProgressTask task1 = progress.createSubTask(1, "task1");
    ProgressTask task2 = progress.createSubTask(5, "task2");
    Runner* runner1 = new Runner(doc, task1);
    Runner* runner2 = new Runner(doc, task2);
    runner1->start();
    runner2->start();
    runner1->wait();
    runner2->wait();
}

Runner::Runner(Document* d, const ProgressTask &t)
        :doc(d), task(t) {
}

void
Runner::run() {
    task.start();
    sleep(1);
    task.setProgress(50);
    sleep(1);
    task.setProgress(100);
}

int
main() {
  Document d;
  
  MyProgressProxy progressproxy;
  ProgressTaskStack stack;
  
  function1(&d);
  function2(&d);
  parallelfunction(&d);
  
  return 0;
}


_______________________________________________
koffice-devel mailing list
koffice-devel@kde.org
https://mail.kde.org/mailman/listinfo/koffice-devel


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic