library/wizard/src/modules/Progress.rb
# ***************************************************************************
#
# Copyright (c) 2002 - 2012 Novell, Inc.
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, contact Novell, Inc.
#
# To contact Novell about this file by physical or electronic mail,
# you may find current contact information at www.novell.com
#
# ***************************************************************************
# File: modules/Progress.ycp
#
# Functions for progress bar.<br>
# <pre>
# Dialog Title
#
# [x] Stage 1
# [x] Stage 2
# => Stage 3
# - Stage 4
# - Stage 5
#
# Progress Title
# [============================90%=======================------]
#
# </pre>
# Example of progress bar usage (don't forget the translation marks in your code):
# Progress bar supposes main wizard dialog is created.<pre>
# Progress::Simple ("Some progress bar", "Progress runs here...", 3, "");
# Progress::NextStep (); // the 1st one does nothing!
# Progress::NextStep ();
# Progress::NextStep ();
# Progress::NextStep ();</pre>
#
# Another example:<pre>
# Progress::New ("Complex progress bar", " ", 100, [
# "Stage1", "Stage2", "Stage3",
# ], [
# "Stage 1 ...", "Stage 2 ...", "Stage 3 ...", "Finished",
# ], "Help text");
# Progress::NextStage ();
# Progress::NextStageStep (20);
# Progress::Stage (0, "I am back", 2);
# Progress::Title ("Still in stage 0");
# Progress::NextStageStep (90);
# Progress::Finish ();</pre>
#
# It is possible to add a detailed subprogress above the main progress bar:
#
# <pre>
# // create a standard progress
# Progress::New(...);
#
# // add a subprogress with 42 steps
# Progress::SubprogressType(`progress, 42);
# Progress::SubprogressTitle("Subprogress label");
#
# // set the subprogress value
# Progress::SubprogressValue(12);
# Progress::SubprogressValue(24);
#
# // remove the subprogress (it's only for the current task/stage)
# Progress::SubprogressType(`none, nil);
#
# // next stage
# Progress::NextStage();
# </pre>
#
# See also hand made documentation.
# <a href="../Progress.html">Progress.html</a>
require "yast"
module Yast
class ProgressClass < Module
include Yast::Logger
def main
Yast.import "UI"
textdomain "base"
Yast.import "CommandLine"
Yast.import "Wizard"
Yast.import "Mode"
Yast.import "Directory"
Yast.import "FileUtils"
# *******************************************************************
# // !!! IMPORTANT !!!
# // If you add here a new variable which is valid only for the current
# // progress do not forget to add it to PushState() and PopState()
# // functions which are are used for nested progresses!
# *******************************************************************
# Number of stages.
@stages = 0
# Number of steps
@steps = 0
# Current stage
@current_stage = 0
# Current step
@current_step = 0
# list of stage-titles
@titles = []
# is progress bar used?
@visible = true
# superior progress (stages) bar
@super_steps = 0
@super_step = 0
@super_stages = []
# remember the last max. value of the subprogress bar
@last_subprogress_max = 0
@progress_running = 0
# remember cumulated number of steps for nested progresses
@progress_max = 0
@progress_val = 0
# stack with the running progresses
# the top of the stack is the end of the list
@progress_stack = []
end
def IsRunning
# Check if any progress bar exists. If it does not, we're not running
# (querying progress counter is not enough, a module ran previously
# might have failed to reset the counter properly)
@progress_running > 0 && UI.WidgetExists(:progress_replace_point)
end
# push the current progress into the stack
def PushState
current_subprogress = CurrentSubprogressType()
state = {
# global variable
"stages" => @stages,
"steps" => @steps,
"current_step" => @current_step,
"current_stage" => @current_stage,
"titles" => @titles,
"last_subprogress_max" => @last_subprogress_max,
"visible" => @visible,
# state of the widgets
"subprogress_type" => current_subprogress,
"progress_label" => Convert.to_string(
UI.QueryWidget(Id(:pb), :Label)
),
"progress_value" => Convert.to_integer(
UI.QueryWidget(Id(:pb), :Value)
),
"progress_max" => @progress_max,
"progress_val" => @progress_val
}
case current_subprogress
when :progress
Ops.set(
state,
"subprogress_label",
Convert.to_string(UI.QueryWidget(Id(:subprogress_progress), :Label))
)
Ops.set(
state,
"subprogress_value",
Convert.to_integer(UI.QueryWidget(Id(:subprogress_progress), :Value))
)
when :tick
Ops.set(
state,
"subprogress_label",
Convert.to_string(UI.QueryWidget(Id(:subprogress_tick), :Label))
)
end
Builtins.y2milestone("Current state: %1", state)
@progress_stack = Builtins.add(@progress_stack, state)
nil
end
# pop the progress state from the stack and set it
def PopState
# pop the config
state = Ops.get(
@progress_stack,
Ops.subtract(Builtins.size(@progress_stack), 1),
{}
)
@progress_stack = Builtins.remove(
@progress_stack,
Ops.subtract(Builtins.size(@progress_stack), 1)
)
Builtins.y2milestone("setting up the previous state: %1", state)
# refresh the variables
@stages = Ops.get_integer(state, "stages", 0)
@steps = Ops.get_integer(state, "steps", 0)
@current_step = Ops.get_integer(state, "current_step", 0)
@current_stage = Ops.get_integer(state, "current_stage", 0)
@titles = Ops.get_list(state, "titles", [])
@last_subprogress_max = Ops.get_integer(state, "last_subprogress_max", 0)
@progress_max = Ops.get_integer(state, "progress_max", 0)
@progress_val = Ops.get_integer(state, "progress_val", 0)
pb_value = Ops.get_integer(state, "progress_value", 0)
pb_value = Ops.add(pb_value.nil? ? 0 : pb_value, 1)
# refresh the progress widget, add one step for the embedded progress
try_replace_widget(
Id(:progress_replace_point),
ProgressBar(
Id(:pb),
Ops.get_string(state, "progress_label", ""),
@steps,
pb_value
)
)
type = Ops.get_symbol(state, "subprogress_type", :none)
SubprogressType(type, @last_subprogress_max)
if [:progress, :tick].include?(type)
SubprogressTitle(Ops.get_string(state, "subprogress_label", ""))
SubprogressValue(Ops.get_integer(state, "subprogress_value", 0))
end
nil
end
# return size of the progress stack
def StackSize
Builtins.size(@progress_stack)
end
# return the value on the top of the stack
# the stack is not changed
def TopState
Ops.get(
@progress_stack,
Ops.subtract(Builtins.size(@progress_stack), 1),
{}
)
end
# Sets progress bar state:
# on = normal operation, off = All Progress:: calls return immediatelly.
# @param [Boolean] state on or off
# @return previous state
def set(state)
prev = @visible
@visible = state
prev
end
# Returns currently selected visibility status of all UI-modifying Progress:: functions.
#
# @return [Boolean] whether the progress bar is used
# @see #Progress::set
# @see #Progress::off
# @see #Progress::on
def status
@visible
end
# Turns progress bar off. All Progress:: calls return immediatelly.
# @deprecated set
def off
# no "deprecated" warning
# because it is ok to use this function in testsuites
@visible = false
nil
end
# Turns progress bar on after calling Progress::off.
# @deprecated set
def on
Builtins.y2warning(-1, "Deprecated function. Use Progress::set instead")
@visible = true
nil
end
# @param [Symbol] kind `todo, `current or `done
# @return UI mark for stages
def Mark(kind)
return "-" if kind == :todo
return UI.Glyph(:BulletArrowRight) if kind == :current
return UI.Glyph(:CheckMark) if kind == :done
"?@%!"
end
# @param [Fixnum] stage stage number
# @return widget `id(...) for the marker
def MarkId(stage)
Id(Builtins.sformat("mark_stage_%1", stage))
end
# New complex progress bar with stages.
# @param [String] window_title title of the window
# @param [String] progress_title title of the progress bar. Pass at least " "
# (one space) if you want some progress bar title.
# @param [Fixnum] length number of steps. If 0, no progress bar is created,
# there are only stages and bottom title. THIS IS NOT
# NUMBER OF STAGES!
# @param [Array<String>] stg list of strings - stage names. If it is nil, then there
# are no stages.
# @param [Array] tits Titles corresponding to stages. When stage changes,
# progress bar title changes to one of these titles. May
# be nil/empty.
# @param [String] help_text help text
def New(window_title, progress_title, length, stg, tits, help_text)
stg = deep_copy(stg)
tits = deep_copy(tits)
return if !@visible
return if Mode.commandline
# a progress is already running, remember the current status
PushState() if IsRunning()
Builtins.y2milestone(
"Progress::New(%1, %2, %3)",
window_title,
length,
stg
)
orig_current_step = @current_step
@steps = length
@stages = Builtins.size(stg)
@titles = deep_copy(tits)
@current_step = -1
@current_stage = -1
if Ops.less_than(length, Builtins.size(stg))
Builtins.y2warning(
"Number of stages (%1) is greater than number of steps (%2)",
Builtins.size(stg),
length
)
end
if progress_title == ""
# Reserve space for future progress bar labels. The ProgressBar
# widget will suppress the label above the progress bar if the
# initial label string is empty.
progress_title = " "
end
# do not replace the UI, there is a progress already running
if IsRunning()
@progress_max = Ops.multiply(@progress_max, @steps)
if StackSize() == 1
@progress_val = Ops.multiply(orig_current_step, @steps)
else
prev_state = TopState()
prev_progress_val = Ops.get_integer(prev_state, "progress_val", 0)
@progress_val = Ops.multiply(prev_progress_val, @steps)
end
# set the maximum value of the progress bar
try_replace_widget(
Id(:progress_replace_point),
ProgressBar(Id(:pb), progress_title, @progress_max, @progress_val)
)
Builtins.y2debug("New progress: %1/%2", @progress_val, @progress_max)
@progress_running += 1
return
else
@progress_max = @steps
end
bar = VBox(ProgressBar(Id(:pb), progress_title, length, 0)) # progressbar only
if 0 != @stages
bar = VBox(VSpacing(1))
i = 0
label_heading = Mark(:todo)
items = VBox()
Builtins.foreach(stg) do |item|
items = Builtins.add(
items,
HBox(
HSpacing(1),
# check_ycp wants this text to be translatable. I do not know why.
# HSquash + MinWidth(4) reserves a defined space for 'mark' plus 'emtpy space'
# see bnc #395752
HSquash(MinWidth(4, Heading(MarkId(i), label_heading))),
Label(item),
HStretch()
)
)
i = Ops.add(i, 1)
end
bar = Builtins.add(bar, Left(HBox(HSquash(items))))
bar = if 0 == @steps
Builtins.add(
bar,
VBox(
VStretch(),
ReplacePoint(Id(:subprogress_replace_point), Empty()),
ReplacePoint(
Id(:progress_replace_point),
Label(Id(:pb), Opt(:hstretch), progress_title)
),
VSpacing(2)
)
)
else
Builtins.add(
bar,
VBox(
VStretch(),
ReplacePoint(Id(:subprogress_replace_point), Empty()),
ReplacePoint(
Id(:progress_replace_point),
ProgressBar(Id(:pb), progress_title, length, 0)
),
VSpacing(2)
)
)
end
end
# patch from Michal Srb https://bugzilla.novell.com/show_bug.cgi?id=406890#c7
try_replace_widget(Id(:contents), bar) unless Mode.test
if !UI.WizardCommand(term(:SetDialogHeading, window_title))
UI.ChangeWidget(Id(:title), :Value, window_title)
UI.RecalcLayout
end
Wizard.SetHelpText(help_text) if "" != help_text && nil != help_text
Wizard.DisableBackButton
Wizard.DisableNextButton
@progress_running += 1
nil
end
# Get current subprogress type
# @return [Symbol] Current type of the subprogress widget - can be `progress, `tick or `none
def CurrentSubprogressType
ret = :none
return ret if !@visible || Mode.commandline
# is there the subprogress progress widget?
if UI.WidgetExists(:subprogress_progress) == true
ret = :progress
# or is there the tick subprogress widget?
elsif UI.WidgetExists(:subprogress_tick) == true
ret = :tick
end
ret
end
# Set value of the subprogress
# @param [Fixnum] value Current value of the subprogress, if a tick subprogress is running the value is ignored and the next tick is displayed
def SubprogressValue(value)
return if !@visible || Mode.commandline
current_type = CurrentSubprogressType()
# normal progress
case current_type
when :progress
UI.ChangeWidget(Id(:subprogress_progress), :Value, value)
# tick progress
when :tick
UI.ChangeWidget(Id(:subprogress_tick), :Alive, true)
else
Builtins.y2warning("No subprogress is defined, cannot set the value!")
end
nil
end
# Create (or remove) a new subprogress above the progress bar, can be use for detailed progress of the current task
# @param [Symbol] type type of the subprogress widget, can be `progress (standard progress),
# `tick (tick progress) or `none (no subprogress, intended for removing the progress bar from the dialog)
# @param [Fixnum] max_value maximum value for `progress type, for the other types it is not relevant (use any integer value or nil)
def SubprogressType(type, max_value)
return if !@visible || Mode.commandline
Builtins.y2debug(
"SubprogressType: type: %1, max_value: %2",
type,
max_value
)
if type == CurrentSubprogressType()
case type
when :progress
# just reset the current value of the progress bar if the requested progress is the same
if max_value == @last_subprogress_max
Builtins.y2milestone("Resetting the subprogressbar...")
SubprogressValue(0)
return
end
when :tick
# just restart the animation
UI.ChangeWidget(Id(:subprogress_tick), :Alive, true)
else
Builtins.y2milestone("Subprogress initialization skipped")
return
end
end
widget = Empty()
case type
when :progress
widget = ProgressBar(Id(:subprogress_progress), " ", max_value, 0)
when :tick
widget = BusyIndicator(Id(:subprogress_tick), " ", 3000)
when :none
widget = Empty()
else
Builtins.y2error("Unknown subprogress type: %1", type)
end
Builtins.y2debug("widget: %1", widget)
try_replace_widget(Id(:subprogress_replace_point), widget)
# remember the max. value
@last_subprogress_max = max_value
nil
end
# Set the label of the subprogress
# @param [String] title New label for the subprogress
def SubprogressTitle(title)
return if !@visible || Mode.commandline
current_type = CurrentSubprogressType()
case current_type
when :progress
UI.ChangeWidget(Id(:subprogress_progress), :Label, title)
when :tick
UI.ChangeWidget(Id(:subprogress_tick), :Label, title)
else
Builtins.y2warning("No subprogress is defined, cannot set the label!")
end
nil
end
# @deprecated Use {#New} instead.
# Obsolete function adding icon-support to progress dialog.
# We don't use icons in popups any more.
# @param [String] window_title
# @param [String] progress_title
# @param [Fixnum] length
# @param [Array<String>] stg
# @param [Array] tits
# @param [String] help_textmap
# @param [Array<Array<String>>] icons_definition
# @see Function Progress::New()
def NewProgressIcons(window_title, progress_title, length, stg, tits, help_textmap, _icons_definition)
Builtins.y2warning(-1, "#{__method__} is deprecated. Use Progress::New instead!")
New(window_title, progress_title, length, stg, tits, help_textmap)
end
# Create simple progress bar with no stages, only with progress bar.
# @param [String] window_title Title of the window.
# @param [String] progress_title Title of the progress bar.
# @param [Fixnum] length Number of steps.
# @param [String] help_text Help text.
def Simple(window_title, progress_title, length, help_text)
New(window_title, progress_title, length, [], [], help_text)
nil
end
# Uses current_step
def UpdateProgressBar
if Ops.greater_than(@current_step, @steps)
Builtins.y2error(
-2,
"Progress bar has only %1 steps, not %2.",
@steps,
@current_step
)
return
end
progress_value = @current_step
if StackSize() != 0
# recalculate the progress bar value according to the parent progress
prev_state = TopState()
prev_step = Ops.get_integer(prev_state, "current_step", 0)
progress_value = Ops.add(
Ops.multiply(prev_step, @steps),
Ops.greater_than(@current_step, 0) ? @current_step : 0
)
end
Builtins.y2debug(
"New progress value: %1, current_step: %2/%3 (%4%%)",
progress_value,
@current_step,
@steps,
Ops.divide(
Ops.multiply(
100.0,
Convert.convert(progress_value, from: "integer", to: "float")
),
Convert.convert(@progress_max, from: "integer", to: "float")
)
)
UI.ChangeWidget(Id(:pb), :Value, progress_value)
nil
end
# the bar is either `ProgressBar or `Label
# @param [String] title
def SetProgressBarTitle(title)
UI.ChangeWidget(Id(:pb), (0 == @steps) ? :Value : :Label, title)
nil
end
# Some people say it is the best operating system ever. But now to the
# function. Advances progress bar value by 1.
def NextStep
return if !@visible || Mode.commandline || 0 == @steps
@current_step = Ops.add(@current_step, 1)
UpdateProgressBar()
nil
end
# Advance stage, advance step by 1 and set progress bar caption to
# that defined in New.
def NextStage
return if !@visible
NextStep()
if 0 == @stages || Ops.greater_than(@current_stage, @stages)
Builtins.y2error("Non-existing stage requested.")
return
end
@current_stage = Ops.add(@current_stage, 1)
# do not update the UI in a nested progress
return if Ops.greater_than(StackSize(), 0)
if Mode.commandline
if Ops.less_than(@current_stage, @stages) &&
Ops.less_than(@current_stage, Builtins.size(@titles))
CommandLine.PrintVerbose(Ops.get_string(@titles, @current_stage, ""))
end
return
end
if Ops.greater_than(@current_stage, 0)
UI.ChangeWidget(
MarkId(Ops.subtract(@current_stage, 1)),
:Value,
Mark(:done)
)
end
# we may be past the last stage
if Ops.less_than(@current_stage, @stages)
SetProgressBarTitle(Ops.get_string(@titles, @current_stage, "")) if Ops.less_than(@current_stage, Builtins.size(@titles))
UI.ChangeWidget(MarkId(@current_stage), :Value, Mark(:current))
end
nil
end
# Changes progress bar value to st.
# @param [Fixnum] stet new value
def Step(step)
return if !@visible || Mode.commandline || 0 == @steps
return if Ops.less_than(step, 0) || Ops.greater_than(step, @steps)
@current_step = step
UpdateProgressBar()
nil
end
# Go to stage st, change progress bar title to title and set progress
# bar step to step.
# @param [Fixnum] stage New stage.
# @param [String] title New title for progress bar. If nil, title specified in
# New is used.
# @param [Fixnum] step New step or -1 if step should not change.
def Stage(stage, title, step)
return if !@visible
Step(step) if -1 != step
# another progress is running
# do not change the current stage, calculate the target step
if Ops.greater_than(StackSize(), 0)
UpdateProgressBar()
return
end
if !Mode.commandline && Ops.greater_or_equal(@current_stage, 0)
UI.ChangeWidget(
MarkId(@current_stage),
:Value,
Mark(Ops.greater_than(stage, @current_stage) ? :done : :todo)
)
end
@current_stage = stage
s = ""
s = Ops.get_string(@titles, @current_stage, "") if Ops.less_than(@current_stage, Builtins.size(@titles))
s = title if nil != title
if Ops.less_than(@current_stage, Builtins.size(@titles))
if Mode.commandline
CommandLine.PrintVerbose(s)
return
else
SetProgressBarTitle(s)
end
end
UI.ChangeWidget(MarkId(@current_stage), :Value, Mark(:current)) if Ops.less_than(@current_stage, @stages)
nil
end
# Jumps to the next stage and sets step to st.
# @param [Fixnum] step new progress bar value
def NextStageStep(step)
return if !@visible || Mode.commandline
NextStage()
Step(step)
nil
end
# Change progress bar title.
# @param [String] title. Use ""(empty string) if you want an empty progress bar.
def Title(title)
SetProgressBarTitle(title) if @visible && !Mode.commandline
nil
end
# Moves progress bar to the end and marks all stages as completed.
def Finish
return if !@visible || Mode.commandline
# decrease the reference counter
@progress_running -= 1
# set the previous state
if Ops.greater_than(StackSize(), 0)
PopState()
return
end
if 0 != @stages
# unwind remaining stages
NextStage() while Ops.less_than(@current_stage, @stages)
end
if 0 != @steps
@current_step = @steps
UpdateProgressBar()
end
SetProgressBarTitle(" ")
nil
end
# Creates a higher-level progress bar made of stages. Currently it is
# placed instead of help text.
# @param [String] title title of the progress...
# @param [Array<String>] stages list of stage descriptions
def OpenSuperior(title, stages)
stages = deep_copy(stages)
if UI.HasSpecialWidget(:Wizard)
Wizard.OpenAcceptAbortStepsDialog
UI.WizardCommand(term(:AddStepHeading, title))
idx = 0
@super_steps = Builtins.size(stages)
@super_step = -1
Builtins.foreach(stages) do |s|
id = Builtins.sformat("super_progress_%1", idx)
UI.WizardCommand(term(:AddStep, s, id))
end
UI.WizardCommand(term(:UpdateSteps)) # old behaviour
else
left = VBox(VStretch())
right = VBox(VStretch())
idx = 0
@super_steps = Builtins.size(stages)
@super_step = -1
Builtins.foreach(stages) do |i|
id = Builtins.sformat("super_progress_%1", idx)
left = Builtins.add(left, Heading(Id(id), "- "))
right = Builtins.add(right, Label(Opt(:hstretch), i))
left = Builtins.add(left, VStretch())
right = Builtins.add(right, VStretch())
idx = Ops.add(idx, 1)
end
left = Builtins.add(left, HSpacing(4))
right = Builtins.add(right, HStretch())
Wizard.ReplaceHelp(
VBox(
HBox(HSpacing(1), Frame(title, HBox(HSpacing(1), left, right))),
VSpacing(0.5)
)
)
end
nil
end
# Replaces stages of superior progress by an empty help text.
def CloseSuperior
if UI.HasSpecialWidget(:Wizard)
UI.CloseDialog
else
Wizard.RestoreHelp("")
end
@super_steps = 0
@super_step = 0
nil
end
# Make one step in a superior progress bar.
def StepSuperior
if Ops.greater_or_equal(@super_step, 0) &&
Ops.less_than(@super_step, @super_steps) && !UI.HasSpecialWidget(:Wizard)
UI.ChangeWidget(
Id(Builtins.sformat("super_progress_%1", @super_step)),
:Value,
UI.Glyph(:CheckMark)
)
end
@super_step = Ops.add(@super_step, 1)
return if Ops.greater_or_equal(@super_step, @super_steps)
if UI.HasSpecialWidget(:Wizard)
UI.WizardCommand(
term(
:SetCurrentStep,
Builtins.sformat("super_progress_%1", @super_step)
)
)
else
UI.ChangeWidget(
Id(Builtins.sformat("super_progress_%1", @super_step)),
:Value,
UI.Glyph(:BulletArrowRight)
)
end
nil
end
# Try replacing a ReplacePoint widget's content with new content, but only
# if the ReplacePoint actually exists: It might be hidden by another dialog
# that was opened on top of the current one (bsc#1187676).
def try_replace_widget(widget_id, new_content)
if UI.WidgetExists(widget_id)
UI.ReplaceWidget(widget_id, new_content)
else
log.warn("No widget with ID #{widget_id} in the current dialog")
UI.DumpWidgetTree
end
end
publish function: :IsRunning, type: "boolean ()"
publish function: :CurrentSubprogressType, type: "symbol ()"
publish function: :SubprogressTitle, type: "void (string)"
publish function: :SubprogressValue, type: "void (integer)"
publish function: :SubprogressType, type: "void (symbol, integer)"
publish function: :set, type: "boolean (boolean)"
publish function: :status, type: "boolean ()"
publish function: :off, type: "void ()"
publish function: :on, type: "void ()"
publish function: :New, type: "void (string, string, integer, list <string>, list, string)"
publish function: :NewProgressIcons, type: "void (string, string, integer, list <string>, list, string, list <list <string>>)"
publish function: :Simple, type: "void (string, string, integer, string)"
publish function: :NextStep, type: "void ()"
publish function: :NextStage, type: "void ()"
publish function: :Step, type: "void (integer)"
publish function: :Stage, type: "void (integer, string, integer)"
publish function: :NextStageStep, type: "void (integer)"
publish function: :Title, type: "void (string)"
publish function: :Finish, type: "void ()"
publish function: :OpenSuperior, type: "void (string, list <string>)"
publish function: :CloseSuperior, type: "void ()"
publish function: :StepSuperior, type: "void ()"
end
Progress = ProgressClass.new
Progress.main
end