From 94e6f0f7a6d84475f318fd6f651b8b3b00f25f5c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 7 Feb 2021 23:41:53 +0100 Subject: [PATCH] Logging progress for DRC, introducing 'abstract progress' concept --- src/buddies/src/bd/bdInit.cc | 26 +--- src/drc/drc/built-in-macros/_drc_engine.rb | 16 +- .../drc/built-in-macros/drc_interpreters.lym | 5 + src/gsi/gsi/gsiDeclTl.cc | 40 ++--- src/lay/lay/layLogViewerDialog.cc | 22 +++ src/lay/lay/layLogViewerDialog.h | 12 ++ src/lay/lay/layProgress.cc | 66 +++++---- src/lay/lay/layProgress.h | 7 - src/lay/lay/layProgressWidget.cc | 91 ++++++++++-- src/lay/lay/layProgressWidget.h | 10 ++ src/lay/lay/laySaltDownloadManager.cc | 2 - src/lay/lay/layTextProgress.cc | 4 + src/lym/lym/lymMacro.cc | 3 + src/tl/tl/tlProgress.cc | 81 +++++++++- src/tl/tl/tlProgress.h | 140 ++++++++++++++++-- src/tl/tl/tlThreadedWorkers.cc | 12 -- 16 files changed, 418 insertions(+), 119 deletions(-) diff --git a/src/buddies/src/bd/bdInit.cc b/src/buddies/src/bd/bdInit.cc index 261ab0c1a..0d3160ccc 100644 --- a/src/buddies/src/bd/bdInit.cc +++ b/src/buddies/src/bd/bdInit.cc @@ -71,13 +71,10 @@ public: ProgressAdaptor (int verbosity); virtual ~ProgressAdaptor (); - virtual void register_object (tl::Progress *progress); - virtual void unregister_object (tl::Progress *progress); virtual void trigger (tl::Progress *progress); virtual void yield (tl::Progress *progress); private: - std::list mp_objects; int m_verbosity; std::string m_progress_text, m_progress_value; }; @@ -93,36 +90,19 @@ ProgressAdaptor::~ProgressAdaptor () // .. nothing yet .. } -void -ProgressAdaptor::register_object (tl::Progress *progress) -{ - mp_objects.push_back (progress); // this keeps the outmost one visible. push_front would make the latest one visible. -} - -void -ProgressAdaptor::unregister_object (tl::Progress *progress) -{ - for (std::list::iterator k = mp_objects.begin (); k != mp_objects.end (); ++k) { - if (*k == progress) { - mp_objects.erase (k); - return; - } - } -} - void ProgressAdaptor::trigger (tl::Progress *progress) { - if (! mp_objects.empty () && mp_objects.front () == progress && tl::verbosity () >= m_verbosity) { + if (progress && first () == progress && tl::verbosity () >= m_verbosity) { - std::string text = mp_objects.front ()->desc (); + std::string text = progress->desc (); if (m_progress_text != text) { tl::info << text << " .."; m_progress_text = text; } - std::string value = mp_objects.front ()->formatted_value (); + std::string value = progress->formatted_value (); if (m_progress_value != value) { tl::info << ".. " << value; m_progress_value = value; diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index 2d449e49f..8cb066c0b 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -498,6 +498,20 @@ module DRC end end + # %DRC% + # @name warn + # @brief Prints a warning + # @synopsis warn(message) + # Similar to \log, but the message is printed formatted as a warning + + def warn(arg) + if @log_file + @log_file.puts("WARNING: " + arg) + else + RBA::Logger::warn(arg) + end + end + # %DRC% # @name log_file # @brief Specify the log file where to send to log to @@ -1840,7 +1854,7 @@ CODE def run_timed(desc, obj) - info(desc) + log(desc) # enable progress if obj.is_a?(RBA::Region) || obj.is_a?(RBA::Edges) || obj.is_a?(RBA::EdgePairs) || obj.is_a?(RBA::Texts) diff --git a/src/drc/drc/built-in-macros/drc_interpreters.lym b/src/drc/drc/built-in-macros/drc_interpreters.lym index 6ada00b60..f1affc3d7 100644 --- a/src/drc/drc/built-in-macros/drc_interpreters.lym +++ b/src/drc/drc/built-in-macros/drc_interpreters.lym @@ -25,6 +25,8 @@ module DRC drc._rdb_index = rdb_index drc._generator = generator + drc_progress = RBA::AbstractProgress::new("DRC: " + macro.path) + begin # Set a debugger scope so that our errors end up with the debugger set to the DRC's line @@ -46,6 +48,9 @@ module DRC # cleans up and creates layout and report views drc._finish + # unlocks the UI + drc_progress._destroy + end timer.stop diff --git a/src/gsi/gsi/gsiDeclTl.cc b/src/gsi/gsi/gsiDeclTl.cc index ef05a59e3..ccb800826 100644 --- a/src/gsi/gsi/gsiDeclTl.cc +++ b/src/gsi/gsi/gsiDeclTl.cc @@ -201,25 +201,6 @@ Class decl_Timer ("tl", "Timer", // ---------------------------------------------------------------- // Progress reporter objects -namespace tl { - - template <> struct type_traits : public type_traits { - typedef tl::false_tag has_copy_constructor; - typedef tl::false_tag has_default_constructor; - }; - - template <> struct type_traits : public type_traits { - typedef tl::false_tag has_copy_constructor; - typedef tl::false_tag has_default_constructor; - }; - - template <> struct type_traits : public type_traits { - typedef tl::false_tag has_copy_constructor; - typedef tl::false_tag has_default_constructor; - }; - -} - namespace gsi { @@ -247,6 +228,27 @@ Class decl_Progress ("tl", "Progress", "This class has been introduced in version 0.23.\n" ); +static tl::AbstractProgress *abstract_progress (const std::string &desc) +{ + return new tl::AbstractProgress (desc); +} + +Class decl_AbstractProgress (decl_Progress, "tl", "AbstractProgress", + gsi::constructor ("new", &abstract_progress, gsi::arg ("desc"), + "@brief Creates an abstract progress reporter with the given description\n" + ), + "@brief The abstract progress reporter\n" + "\n" + "The abstract progress reporter acts as a 'bracket' for a sequence of operations which are connected " + "logically. For example, a DRC script consists of multiple operations. An abstract progress reportert " + "is instantiated during the run time of the DRC script. This way, the application leaves the UI open while " + "the DRC executes and log messages can be collected.\n" + "\n" + "The abstract progress does not have a value.\n" + "\n" + "This class has been introduced in version 0.27.\n" +); + static tl::RelativeProgress *rel_progress_2 (const std::string &desc, size_t max) { return new tl::RelativeProgress (desc, max); diff --git a/src/lay/lay/layLogViewerDialog.cc b/src/lay/lay/layLogViewerDialog.cc index 5493d7aae..e0ca83912 100644 --- a/src/lay/lay/layLogViewerDialog.cc +++ b/src/lay/lay/layLogViewerDialog.cc @@ -200,11 +200,33 @@ LogFile::timeout () } } +void +LogFile::set_max_entries (size_t n) +{ + QMutexLocker locker (&m_lock); + + m_max_entries = n; + + while (m_messages.size () > m_max_entries) { + m_messages.pop_front (); + } +} + +size_t +LogFile::max_entries () const +{ + return m_max_entries; +} + void LogFile::add (LogFileEntry::mode_type mode, const std::string &msg, bool continued) { QMutexLocker locker (&m_lock); + if (m_max_entries == 0) { + return; + } + if (m_messages.size () >= m_max_entries) { m_messages.pop_front (); } diff --git a/src/lay/lay/layLogViewerDialog.h b/src/lay/lay/layLogViewerDialog.h index 69caeb327..c9da714a3 100644 --- a/src/lay/lay/layLogViewerDialog.h +++ b/src/lay/lay/layLogViewerDialog.h @@ -192,6 +192,18 @@ public slots: return m_log_receiver; } + /** + * @brief Sets the maximum number of entries to show + * + * Setting this value to 0 basically disables the log collection + */ + void set_max_entries (size_t n); + + /** + * @brief Gets the maximum number of entries to show + */ + size_t max_entries () const; + private slots: void timeout (); diff --git a/src/lay/lay/layProgress.cc b/src/lay/lay/layProgress.cc index 28c13a600..36624f20d 100644 --- a/src/lay/lay/layProgress.cc +++ b/src/lay/lay/layProgress.cc @@ -86,14 +86,13 @@ ProgressReporter::set_progress_bar (lay::ProgressBar *pb) void ProgressReporter::register_object (tl::Progress *progress) { - if (mp_objects.empty ()) { + if (begin () == end ()) { // to avoid recursions of any kind, disallow any user interaction except // cancelling the operation QApplication::instance ()->installEventFilter (this); } - mp_objects.push_back (*progress); // this keeps the outmost one visible. push_front would make the latest one visible. - // mp_objects.push_front (progress); + tl::ProgressAdaptor::register_object (progress); if (m_start_time == tl::Clock () && ! m_pw_visible) { m_start_time = tl::Clock::current (); @@ -104,33 +103,47 @@ ProgressReporter::register_object (tl::Progress *progress) set_visible (true); } - update_and_yield (); + if (progress->is_abstract ()) { + if (mp_pb) { + mp_pb->update_progress (progress); + } + process_events (); + } else { + update_and_yield (); + } } void ProgressReporter::unregister_object (tl::Progress *progress) { - progress->unlink (); + tl::ProgressAdaptor::unregister_object (progress); // close or refresh window - if (mp_objects.empty ()) { + if (begin () == end ()) { + if (m_pw_visible) { set_visible (false); } + m_start_time = tl::Clock (); - } - update_and_yield (); + if (mp_pb) { + mp_pb->update_progress (0); + } + + process_events (); - if (mp_objects.empty ()) { QApplication::instance ()->removeEventFilter (this); + + } else { + update_and_yield (); } } void ProgressReporter::trigger (tl::Progress * /*progress*/) { - if (! mp_objects.empty ()) { + if (begin () != end ()) { // make dialog visible after some time has passed if (! m_pw_visible && (tl::Clock::current () - m_start_time).seconds () > 1.0) { set_visible (true); @@ -152,27 +165,22 @@ ProgressReporter::yield (tl::Progress * /*progress*/) } } -void -ProgressReporter::signal_break () -{ - for (tl::list::iterator k = mp_objects.begin (); k != mp_objects.end (); ++k) { - k->signal_break (); - } -} - void ProgressReporter::update_and_yield () { - if (m_pw_visible && ! mp_objects.empty ()) { - if (mp_pb) { - mp_pb->update_progress (mp_objects.first ()); - QWidget *w = mp_pb->progress_get_widget (); - if (w) { - mp_objects.first ()->render_progress (w); - } - } - process_events (); // Qt4 seems to need this + if (! m_pw_visible) { + return; } + + if (mp_pb && first ()) { + mp_pb->update_progress (first ()); + QWidget *w = mp_pb->progress_get_widget (); + if (w) { + first ()->render_progress (w); + } + } + + process_events (); // Qt4 seems to need this } void @@ -202,8 +210,8 @@ ProgressReporter::set_visible (bool vis) if (mp_pb) { if (!vis) { mp_pb->progress_remove_widget (); - } else if (mp_pb->progress_wants_widget () && mp_objects.first ()) { - mp_pb->progress_add_widget (mp_objects.first ()->progress_widget ()); + } else if (mp_pb->progress_wants_widget () && first ()) { + mp_pb->progress_add_widget (first ()->progress_widget ()); } } diff --git a/src/lay/lay/layProgress.h b/src/lay/lay/layProgress.h index 570a0506d..7bb7023c0 100644 --- a/src/lay/lay/layProgress.h +++ b/src/lay/lay/layProgress.h @@ -68,16 +68,9 @@ public: virtual void yield (tl::Progress *progress); virtual bool eventFilter (QObject *dest, QEvent *event); - void signal_break (); void set_progress_bar (lay::ProgressBar *pb); - bool is_busy () const - { - return !mp_objects.empty (); - } - private: - tl::list mp_objects; tl::Clock m_start_time; lay::ProgressBar *mp_pb; bool m_pw_visible; diff --git a/src/lay/lay/layProgressWidget.cc b/src/lay/lay/layProgressWidget.cc index 60c61830c..3c4cfe670 100644 --- a/src/lay/lay/layProgressWidget.cc +++ b/src/lay/lay/layProgressWidget.cc @@ -60,7 +60,7 @@ private: ProgressBarWidget::ProgressBarWidget (QWidget *parent, const char *name) : QWidget (parent), - m_value (0.0), m_width (64), m_length (0), m_fw (1), m_bw (0) + m_value (0.0), m_width (200), m_length (0), m_fw (1), m_bw (0) { setObjectName (QString::fromUtf8 (name)); setMinimumSize (64, 10); @@ -135,20 +135,49 @@ ProgressBarWidget::resizeEvent (QResizeEvent *) // -------------------------------------------------------------------- -ProgressWidget::ProgressWidget (ProgressReporter *pr, QWidget *parent, bool full_width) +ProgressWidget::ProgressWidget (ProgressReporter *pr, QWidget *parent, bool fw) : QFrame (parent), - mp_widget (0), mp_pr (pr), m_log_file (6, true) + mp_widget (0), mp_pr (pr), m_log_file (0, true), m_log_visible (false) { QVBoxLayout *top_layout = new QVBoxLayout (this); top_layout->addStretch (1); + mp_log_frame = new QFrame (this); + mp_log_frame->setFrameShape (QFrame::NoFrame); + mp_log_frame->hide (); + top_layout->addWidget (mp_log_frame); + + QVBoxLayout *log_layout = new QVBoxLayout (mp_log_frame); + + QListView *log_view = new QListView (this); + log_view->setModel (&m_log_file); + log_view->setUniformItemSizes (true); + log_layout->addWidget (log_view); + + QFrame *attn_frame = new QFrame (this); + attn_frame->setFrameShape (QFrame::NoFrame); + attn_frame->hide (); + log_layout->addWidget (attn_frame); + + QHBoxLayout *attn_layout = new QHBoxLayout (attn_frame); + attn_layout->setContentsMargins (0, 0, 0, 0); + + QLabel *attn_label1 = new QLabel (attn_frame); + attn_label1->setPixmap (QPixmap (QString::fromUtf8 (":/warn_16.png"))); + attn_layout->addWidget (attn_label1); + + QLabel *attn_label2 = new QLabel (attn_frame); + attn_label2->setText (tr ("There are errors or warnings")); + attn_layout->addWidget (attn_label2); + + attn_layout->addStretch (1); + + connect (&m_log_file, SIGNAL (layoutChanged ()), log_view, SLOT (scrollToBottom ())); + connect (&m_log_file, SIGNAL (attention_changed (bool)), attn_frame, SLOT (setVisible (bool))); + QFrame *bar_frame = new QFrame (this); top_layout->addWidget (bar_frame); - QListView *log_list = new QListView (this); - log_list->setModel (&m_log_file); - top_layout->addWidget (log_list); - top_layout->addStretch (1); // this does not allow the label to control the overall size, so a long string does not hurt: @@ -162,12 +191,11 @@ ProgressWidget::ProgressWidget (ProgressReporter *pr, QWidget *parent, bool full int col = 0; - if (! full_width) { - layout->addItem (new QSpacerItem (8, 8, QSizePolicy::Expanding, QSizePolicy::Expanding), 0, col, 1, 1); - layout->setColumnStretch (col++, 1); - } + layout->addItem (new QSpacerItem (8, 8, QSizePolicy::Expanding, QSizePolicy::Expanding), 0, col, 1, 1); + m_left_col = col++; mp_label = new QLabel (bar_frame); + layout->setColumnStretch(col, 2); layout->addWidget (mp_label, 0, col++, 1, 1); layout->addItem (new QSpacerItem (8, 8, QSizePolicy::Fixed, QSizePolicy::Fixed), 0, col++, 1, 1); @@ -176,7 +204,6 @@ ProgressWidget::ProgressWidget (ProgressReporter *pr, QWidget *parent, bool full progress_bar_frame->setFrameStyle (QFrame::Box | QFrame::Plain); progress_bar_frame->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Expanding); layout->addWidget (progress_bar_frame, 0, col, 1, 1); - layout->setColumnStretch(col++, 2); QGridLayout *pbf_layout = new QGridLayout (progress_bar_frame); progress_bar_frame->setLayout (pbf_layout); @@ -196,16 +223,41 @@ ProgressWidget::ProgressWidget (ProgressReporter *pr, QWidget *parent, bool full mp_cancel_button->setText (QObject::tr ("Cancel")); layout->addWidget (mp_cancel_button, 0, col++, 1, 1); - if (! full_width) { - layout->addItem (new QSpacerItem (8, 8, QSizePolicy::Expanding, QSizePolicy::Expanding), 0, col, 1, 1); - layout->setColumnStretch (col++, 1); - } + layout->addItem (new QSpacerItem (8, 8, QSizePolicy::Expanding, QSizePolicy::Expanding), 0, col, 1, 1); + m_right_col = col++; layout->addItem (new QSpacerItem (10, 10, QSizePolicy::Fixed, QSizePolicy::Fixed), 1, 0, 1, col); m_widget_col = col; connect (mp_cancel_button, SIGNAL (clicked ()), this, SLOT (signal_break ())); + + set_full_width (fw); +} + +void +ProgressWidget::set_log_visible (bool f) +{ + if (f != m_log_visible) { + m_log_visible = f; + mp_log_frame->setVisible (f); + set_full_width (m_full_width); + } +} +void +ProgressWidget::set_full_width (bool fw) +{ + m_full_width = fw; + + bool f = (fw || m_log_visible); + mp_layout->setColumnStretch (m_left_col, f ? 0 : 1); + mp_layout->setColumnStretch (m_right_col, f ? 0 : 1); +} + +bool +ProgressWidget::full_width () const +{ + return m_full_width; } QWidget * @@ -238,6 +290,13 @@ ProgressWidget::remove_widget () void ProgressWidget::set_progress (tl::Progress *progress) { + if (! progress || progress->is_abstract ()) { + m_log_file.clear (); + m_log_file.set_max_entries (progress ? 1000 : 0); + set_log_visible (progress != 0); + return; + } + bool can_cancel = false; std::string text; diff --git a/src/lay/lay/layProgressWidget.h b/src/lay/lay/layProgressWidget.h index c47ea9a8d..0600f7e5d 100644 --- a/src/lay/lay/layProgressWidget.h +++ b/src/lay/lay/layProgressWidget.h @@ -37,6 +37,8 @@ class QToolButton; class QLabel; class QToolButton; class QGridLayout; +class QListView; +class QFrame; namespace tl { @@ -60,6 +62,8 @@ public: void add_widget (QWidget *widget); void remove_widget (); QWidget *get_widget () const; + void set_full_width (bool fw); + bool full_width () const; QSize sizeHint () const; @@ -75,6 +79,12 @@ private: QToolButton *mp_cancel_button; ProgressReporter *mp_pr; lay::LogFile m_log_file; + QFrame *mp_log_frame; + bool m_full_width; + int m_left_col, m_right_col; + bool m_log_visible; + + void set_log_visible (bool f); }; } diff --git a/src/lay/lay/laySaltDownloadManager.cc b/src/lay/lay/laySaltDownloadManager.cc index e2f5d07a9..bc3ed4f4e 100644 --- a/src/lay/lay/laySaltDownloadManager.cc +++ b/src/lay/lay/laySaltDownloadManager.cc @@ -409,8 +409,6 @@ namespace mp_dialog->mark_fetching (m_name); } - virtual void register_object (tl::Progress * /*progress*/) { } - virtual void unregister_object (tl::Progress * /*progress*/) { } virtual void yield (tl::Progress * /*progress*/) { } virtual void trigger (tl::Progress *progress) diff --git a/src/lay/lay/layTextProgress.cc b/src/lay/lay/layTextProgress.cc index 829b84896..6d67fc2b8 100644 --- a/src/lay/lay/layTextProgress.cc +++ b/src/lay/lay/layTextProgress.cc @@ -35,6 +35,10 @@ TextProgress::TextProgress (int verbosity) void TextProgress::update_progress (tl::Progress *progress) { + if (! progress || progress->is_abstract ()) { + return; + } + std::string text = progress->desc (); if (m_progress_text != text && tl::verbosity () >= m_verbosity) { tl::info << text << " .."; diff --git a/src/lym/lym/lymMacro.cc b/src/lym/lym/lymMacro.cc index 17026ae56..c8d20fee9 100644 --- a/src/lym/lym/lymMacro.cc +++ b/src/lym/lym/lymMacro.cc @@ -34,6 +34,7 @@ #include "tlXMLParser.h" #include "tlGlobPattern.h" #include "tlInclude.h" +#include "tlProgress.h" #include "rba.h" #include "pya.h" @@ -1022,6 +1023,8 @@ int Macro::run () const try { + tl::ProgressGarbageCollector progress_gc; + gsi::Interpreter *ip = script_interpreter (interpreter ()); if (ip) { diff --git a/src/tl/tl/tlProgress.cc b/src/tl/tl/tlProgress.cc index 4015e0107..88d7e4ee0 100644 --- a/src/tl/tl/tlProgress.cc +++ b/src/tl/tl/tlProgress.cc @@ -46,6 +46,18 @@ ProgressAdaptor::~ProgressAdaptor () tl::Progress::register_adaptor (0); } +void +ProgressAdaptor::register_object (Progress *progress) +{ + mp_objects.push_back (progress); // this keeps the outmost one visible. push_front would make the latest one visible. +} + +void +ProgressAdaptor::unregister_object (Progress *progress) +{ + progress->unlink (); +} + void ProgressAdaptor::prev (ProgressAdaptor *pa) { @@ -58,6 +70,59 @@ ProgressAdaptor::prev () return mp_prev; } +void +ProgressAdaptor::signal_break () +{ + for (tl::list::iterator k = mp_objects.begin (); k != mp_objects.end (); ++k) { + k->signal_break (); + } +} + +tl::Progress * +ProgressAdaptor::first () +{ + for (tl::list::iterator k = mp_objects.begin (); k != mp_objects.end (); ++k) { + if (! k->is_abstract ()) { + return k.operator-> (); + } + } + return 0; +} + +// --------------------------------------------------------------------------------------------- +// ProgressGarbageCollector implementation + +ProgressGarbageCollector::ProgressGarbageCollector () +{ + tl::ProgressAdaptor *a = tl::Progress::adaptor (); + if (a) { + for (tl::ProgressAdaptor::iterator p = a->begin (); p != a->end (); ++p) { + mp_valid_objects.insert (p.operator-> ()); + } + } +} + +ProgressGarbageCollector::~ProgressGarbageCollector () +{ + tl::ProgressAdaptor *a = tl::Progress::adaptor (); + if (a) { + + for (tl::ProgressAdaptor::iterator p = a->begin (); p != a->end (); ) { + + tl::ProgressAdaptor::iterator pn = p; + ++pn; + + if (mp_valid_objects.find (p.operator-> ()) == mp_valid_objects.end ()) { + a->unregister_object (p.operator-> ()); + } + + p = pn; + + } + + } +} + // --------------------------------------------------------------------------------------------- // Progress implementation @@ -187,7 +252,21 @@ bool Progress::test(bool force_yield) } // --------------------------------------------------------------------------------------------- -// Progress implementation +// AbstractProgress implementation + +AbstractProgress::AbstractProgress (const std::string &desc) + : tl::Progress (desc) +{ + initialize (); +} + +AbstractProgress::~AbstractProgress () +{ + shutdown (); +} + +// --------------------------------------------------------------------------------------------- +// RelativeProgress implementation RelativeProgress::RelativeProgress (const std::string &desc, size_t max_count, size_t yield_interval) : Progress (desc, yield_interval) diff --git a/src/tl/tl/tlProgress.h b/src/tl/tl/tlProgress.h index d8bf73917..e0001cd28 100644 --- a/src/tl/tl/tlProgress.h +++ b/src/tl/tl/tlProgress.h @@ -31,12 +31,57 @@ #include "tlTimer.h" #include "tlList.h" +#include + class QWidget; namespace tl { class Progress; +class RelativeProgress; +class AbstractProgress; +class AbsoluteProgress; + +template <> struct type_traits : public type_traits { + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + +template <> struct type_traits : public type_traits { + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + +template <> struct type_traits : public type_traits { + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + +template <> struct type_traits : public type_traits { + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + +/** + * @brief A helper class to clean up pending progress objects + * + * Pending progress objects may be created in scripts. If scripts are aborted + * (e.g. in the debugger), progress objects may stay behing a block the application. + * To prevent this, this object keeps track of progress objects created between + * it's constructor and destructor and cleans up the objects created but not + * destroyed. + */ + +class TL_PUBLIC ProgressGarbageCollector +{ +public: + ProgressGarbageCollector (); + ~ProgressGarbageCollector (); + +private: + std::set mp_valid_objects; +}; /** * @brief The receivers for progress reports @@ -48,20 +93,45 @@ class Progress; class TL_PUBLIC ProgressAdaptor { -public: +public: + typedef tl::list::iterator iterator; + ProgressAdaptor (); virtual ~ProgressAdaptor (); - virtual void register_object (Progress *progress) = 0; - virtual void unregister_object (Progress *progress) = 0; + virtual void register_object (Progress *progress); + virtual void unregister_object (Progress *progress); virtual void trigger (Progress *progress) = 0; virtual void yield (Progress *progress) = 0; void prev (ProgressAdaptor *pa); ProgressAdaptor *prev (); + bool is_busy () const + { + return !mp_objects.empty (); + } + + tl::Progress *first (); + + void signal_break (); + +protected: + iterator begin () + { + return mp_objects.begin (); + } + + iterator end () + { + return mp_objects.end (); + } + private: + friend class ProgressGarbageCollector; + ProgressAdaptor *mp_prev; + tl::list mp_objects; }; /** @@ -77,8 +147,6 @@ public: BreakException () : tl::Exception ("Operation cancelled") { } }; -class Progress; - /** * @brief A "progress" reporter class * @@ -138,6 +206,15 @@ public: */ virtual double value () const = 0; + /** + * @brief Returns true if the progress is an abstract one + * + * Abstract progress objcts don't have a value but mark a section begin executed as a top level progress. + * Technically they will open a channel for the UI - e.g. leaving a progress dialog open while the + * operation is running. + */ + virtual bool is_abstract () const = 0; + /** * @brief Creates a widget that renders the progress graphically * @@ -217,6 +294,7 @@ protected: private: friend class ProgressAdaptor; + friend class ProgressGarbageCollector; std::string m_desc; std::string m_title; @@ -231,6 +309,43 @@ private: static void register_adaptor (tl::ProgressAdaptor *pa); }; +/** + * @brief The abstract progress + * + * An abstract progress object can be used as a top-level progress object to mark a section + * in an operation flow. This will provide a hint for the UI to leave the progress dialog open + * for example. + */ +class TL_PUBLIC AbstractProgress + : public Progress +{ +public: + /** + * @brief Constructor + */ + AbstractProgress (const std::string &desc); + + /** + * @brief Destructor + */ + ~AbstractProgress (); + + /** + * @brief Delivers the current progress as a string (empty for the abstract progress) + */ + std::string formatted_value () const { return std::string (); } + + /** + * @brief Delivers the relative progress (0 for the abstract progress) + */ + double value () const { return 0.0; } + + /** + * @brief Indicates this progress reporter is abstract + */ + bool is_abstract() const { return true; } +}; + /** * @brief A relative progress value * @@ -253,9 +368,6 @@ public: */ RelativeProgress (const std::string &desc, size_t max_count = 0, size_t yield_interval = 1000); - /** - * @brief Destructor - */ ~RelativeProgress (); /** @@ -271,6 +383,11 @@ public: */ double value () const; + /** + * @brief Indicates this progress reporter isn't abstract + */ + bool is_abstract() const { return false; } + /** * @brief Set the format of the output. * @@ -345,7 +462,12 @@ public: */ double value () const; - /** + /** + * @brief Indicates this progress reporter isn't abstract + */ + bool is_abstract() const { return false; } + + /** * @brief Set the format of the output. * * This is a sprintf format string with the value being diff --git a/src/tl/tl/tlThreadedWorkers.cc b/src/tl/tl/tlThreadedWorkers.cc index 7cd63cf76..48ddbabd0 100644 --- a/src/tl/tl/tlThreadedWorkers.cc +++ b/src/tl/tl/tlThreadedWorkers.cc @@ -512,8 +512,6 @@ class TL_PUBLIC WorkerProgressAdaptor : public tl::ProgressAdaptor public: WorkerProgressAdaptor (Worker *worker); - virtual void register_object (Progress *progress); - virtual void unregister_object (Progress *progress); virtual void trigger (Progress *progress); virtual void yield (Progress *progress); @@ -527,16 +525,6 @@ WorkerProgressAdaptor::WorkerProgressAdaptor (Worker *worker) // .. nothing yet .. } -void WorkerProgressAdaptor::register_object (Progress * /*progress*/) -{ - // .. nothing yet .. -} - -void WorkerProgressAdaptor::unregister_object (Progress * /*progress*/) -{ - // .. nothing yet .. -} - void WorkerProgressAdaptor::trigger (Progress * /*progress*/) { // .. nothing yet ..