0

I want to define an R6 class that sets up, updates and closes a progress bar. For these 3 tasks, I have 3 functions. The first, setup_progressbar(), calls R's txtProgressbar() which returns an object (say, pb) which needs to be passed on to the second and third functions, update_progressbar() and close_progressbar(). But the object pb is not found by the latter two functions.

library(R6)
myprogressbar <- R6Class("my_progress_bar",
                         public = list(
                             n = numeric(1),
                             initialize = function(n) {
                                 stopifnot(n >= 1)
                                 self$n <- n
                             },
                             setup_progressbar = function() {
                                 pb <- txtProgressBar(max = self$n)
                             },
                             update_progressbar = function(i) {
                                 setTxtProgressBar(pb, i)
                             },
                             close_progressbar = function () {
                                 close(pb)
                                 cat("\n")
                             }
                         ))
mypb <- myprogressbar$new(10)
mypb$setup_progressbar()
mypb$update_progressbar(3) # Error in setTxtProgressBar(pb, i) : object 'pb' not found

I tried to add pb to self in the hope it would be found, but then I obtain "cannot add bindings to a locked environment".

Note: In my actual (non-minimal) example, the i is found/provided/visible, so that's not an additional problem (most likely this is just a problem in the above minimal working example once fixed beyond the 'pb' not found error).

Marius Hofert
  • 6,546
  • 10
  • 48
  • 102
  • What exactly do you mean by "isn't quite natural"? I'm afraid that's just how R6 objects work. You can read more about them at https://adv-r.hadley.nz/r6.html. It's unclear exactly what your requirements are. – MrFlick Jan 03 '21 at 19:33
  • I found it strange that `initialize()` needs all such type of arguments (as `pb`) as argument (and thus some sort of default so that it isn't required to be provided by `$new()`). There could be many such arguments and thus they all need to be part of `initialize()`. Thanks for the reference, I read it but that particular case (passing arguments between functions) is not covered, at least I haven't seen it. I saw from https://homerhanumat.github.io/r-notes/reference-classes-with-package-r6.html that all functions have access to `self` which is why I then 'forced' `pb` to be part of it. – Marius Hofert Jan 03 '21 at 19:39

1 Answers1

2

The following works:

library(R6)
myprogressbar <- R6Class("my_progress_bar",
                         public = list(
                             n = numeric(1),
                             pb = NULL, # provide as argument
                             initialize = function(n, pb = NULL) { # provide with default so that $new() doesn't require 'pb'
                                 stopifnot(n >= 1)
                                 self$n <- n
                             },
                             setup_progressbar = function() {
                                 self$pb <- txtProgressBar(max = self$n)
                             },
                             update_progressbar = function(i) {
                                 setTxtProgressBar(self$pb, i)
                             },
                             close_progressbar = function () {
                                 close(self$pb)
                                 cat("\n")
                             }
                         ))

mypb <- myprogressbar$new(10)
mypb$setup_progressbar()
mypb$update_progressbar(3) 
Marius Hofert
  • 6,546
  • 10
  • 48
  • 102