- Feature Articles
- CodeSOD
- Error'd
- Forums
-
Other Articles
- Random Article
- Other Series
- Alex's Soapbox
- Announcements
- Best of…
- Best of Email
- Best of the Sidebar
- Bring Your Own Code
- Coded Smorgasbord
- Mandatory Fun Day
- Off Topic
- Representative Line
- News Roundup
- Editor's Soapbox
- Software on the Rocks
- Souvenir Potpourri
- Sponsor Post
- Tales from the Interview
- The Daily WTF: Live
- Virtudyne
Admin
Except when
Second
is 59, where the code displays one dot rather than the three displayed by the proposed math.Admin
Well no, for 15 and 18 , the code prints "...", whereas for 16 and 19, it prints "." Carefully crafted exceptions to the expected.
Admin
Yes, I really think that at 59 seconds the "progress" indicator should be more informative. "All your modulus belong to us," perhaps?
Admin
... not to mention the stumbling and staggering that happens between 14 and 21.
Challenge: set up the numbers so as to make the dots do a little dance.
Admin
There is another WTF: why does the first case-statement jump from 12 to 16 and not to 15? Later on, from 19 to 21 this is 'corrected', but it smells a bit
Case 0, 3, 6, 9, 12, 16, 19, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 59 Case 1, 4, 7, 10, 13, 17, 20, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49, 52, 55, 58
12 = 1 dot 13 = 2 dot 14 = 3 dots 15 = 3 dots 16 = 1 dot 17 = 2 dots 18 = 3 dots 19 = 1 dot 20 = 2 dots 21 = 1 dot
Admin
What happens if the current user isn't called Tim?
Admin
There are many ways to crack this nut even state machine variables.
This is definitely a bad way, almost expected to see 60 case statements or nested if else to be truely "entripisy" with calls to three time servers to check the time and output a XML-> CSV -> HTML->[insert bloted report writer here] generate report and every iteration open and close a database to read all records.
Admin
Showing the user that stuff is progressing is nice and helpful. Showing the user that time is elapsing is pointless. In this case, they CBA to do the former, but by adding a bit of stutter, they are making it look (wrongly) like they aren't just ticking off seconds.
Admin
The worse problem is when is it called if on multiples of seconds the dots do not progress at all
0 secs . 3 secs .
Or on longer calls a minute apart same pattern
Should just display progression of dots on every call from a private counter, 0 = 1 dot, 1 = 2 dots, all other values 3 dots.
NO maths NO time calls
Admin
Except now you've added a different problem if the function is called too quickly. We just can't offer any good refactoring advice on a function where we simply don't know the operating conditions. For all we know this could be working from a separate timer thread. Or god knows what.
Admin
Well it is an old VB app. I would assume there is some other loop calling this sub and in that loop there is a call to DoEvents someplace. Back in that single threaded world where busy processes could be unresponsive to any kind of user input for long periods and it all running on 16-bit windows or almost as bad Windows 9x, in terms of stability it actually was worth while to just let the user know the computer had not crashed and something was still happening even if you were just marking time.
Admin
Incidentally, "throbbers?"
Never heard that one before. But in this case, following the example of a well known pop band, I'd call this version "gristle."
Admin
What does too quickly matter, if on a system not a web page the FASTEST screen update is 60Hz limited by the monitor update rate, or if really snazzy gaming system running at double rate 120Hz. You will see an alias of the updates.
If this is being put in we are doing a long job, in rare case of somebody actually caring about this I doubt speed of update being realistic was their primary concer. just ticking the requirements of progress indicator has been checked. I very much doubt anyone really tests their progress indicator having seen many do weird things like like take twice as long to finish after 100% than get to 100%.
Admin
OK... I HAD to do this...
● ●● ●●● ● ●● ●●● ● ●● ●●● ● ●● ●●● ● ●● ●●● ●●● ● ●● ●●● ● ●● ● ●● ●●● ● ●● ●●● ● ●● ●●● ● ●● ●●● ● ●● ●●● ● ●● ●●● ● ●● ●●● ● ●● ●●● ● ●● ●●● ● ●● ●●● ● ●● ●●● ● ●● ●●● ● ●● ●
Admin
Dammit, this thing messes up newlines. :P
Admin
Morse code also uses - . Just sayin' And what happens if the time is FILE_NOT_FOUND? Think people, think!
Admin
This is a perfect counter-example for people who argue that FizzBuzz has no application to real-world code.
Admin
Not in VB.NET it isn't.
Admin
Me.NotTim.Now.Frist, of course.
Admin
I never heard Throbbing Gristle called pop before...
Admin
It's on a rare occasion that they get called music, come to that.
Admin
It's relativistic code, Steve. It always shows progress based on the time in Tim's reference frame.
Admin
Throbbers have been known since the 90s - they were originally crafted for web browsers and were that spinning N or spinning E (depending on the browser) that animated while it was busy downloading stuff from the Internet.
That's what they were called then, I guess the little thing that replaced those huge things are still called throbbers even though they don't really throb, and really either bounce back and forth or spin. Some refer to those as spinners, but that can be confused with the spin control in a GUI (the number box with elevator buttons to increase or decrease the value in the box).
Admin
And of course all they really show you is that the code to drive the throbber is working.
Or in this case, maybe not.
Admin
I expected many WTFs with progress bars, so I guess that's kind of harmless?
My biggest WTF is usually how adding a meaningful progress bar is usually a far from trivial task, especially if it should stretch across a variable number of subtasks.
Where previously you had just plain and simple function calls, you now need a task-management layer, just in order to correctly scale the start- and end-percentages of each subtask.
Depending on the language used, it may also not be trivial now to move data calculated by the first task to the second task, so the design suddenly encourages passing data around by global state (be it global variables, god objects, or temporary files).
Similar for error messages; Useful error messages often require access to more data than the function, where the error is detected, and depending on what the language support, on a dead-line this tends to encourage tighter coupling of code.
Admin
One approach that I've seen described is to have a fixed curve describing the rate of travel, with an exponential decay to make it progress ever more slowly as it advances, and then jumps to 100% once the task actually finishes. My own thought is that it then becomes more like a throbber that's shaped like a progress bar, but if you can't produce a meaningful progress bar, it's better than most ordinary throbbers.
Or you make a progress indicator that just displays a count of the amount of work done, which allows the user to see that some actual work is being done while not having to commit to what 37% actually means. One useful thing this adds is that if the overall task contains subtasks that are or are not activated depending on other data in the overall task, that variation is automatically taken into account.
Admin
That's not really any more meaingful though. Like say you have a progress bar that goes: Now at 37/42. Just what information does that actually convey? If the work is asymetrical (and it always will be) for all the user knows task 40 could be 90% of the total workload.
Really in spite of what our inner engineer wants them to be progress bars are just widgets that exist to tell the user the program isn't hanging or frozen but is actually doing something. And for that any animation will do.
Admin
In our cast the task is a simulation consisting of several steps, depending on the configuration. There is no reliable way to estimate how long each step will take ahead of time, but within each task a progress can be stated.
If we just naively assign each task an equal share of the 0-100 range, the end-user would still get useful information. They'll probably running similar simulations dozens of times, so even if the last 10% take 90% of the time, they'll just know to expect that, but still get useful estimate how long the task will take out of how long it took to reach 92%.
Admin
Those exponential-decay progress bars are probably the worst possible solution psychologically. They basically constantly tell you "it will be done any moment", but its never true. This just provokes impatience. And if the task is too long, it will look like the bar is hanging either way.
Admin
Any information the user will get will always boil down to 2 things: 1. The program is working and not hanging. 2. The progress bar is stupid because 92% isn't actually 92%. That's why I dislike labeled progress bars in general. An unlabeled progress bar or spinning widgit or something conveys the same information only without giving the user something to be annoyed by.
Admin
I was thinking more of a thing that indicates how many "pieces" of work the program has processed, without any indication of how many there are, just to show that it's advancing. It's for handling those cases where until we have processed all the data, we cannot know how many there are, or where finding out how many there are is almost as costly as processing them. (Example: changing rights on files and directories in a large tree. Example: compiling a report on the results of a large database query.)
Subject to the thing that it isn't really a good idea to make a thing that looks like a progress bar, but doesn't show actual progress, or where the measure of progress isn't the right one.
Case in point: there's a progress bar in the Guild Wars 2 launcher/updater when it is downloading updates. It shows progress in terms of the number of "files" (actually chunks in a big huge enormous single file that's a sort of virtual filesystem). The problem? The "files" are not all the same size, and the size varies by multiple orders of magnitude from one "file" to another. It attracts complaints on the forums, and the usual suggestion is to change it so it's based on the quantity of data rather than the number of "files".
Admin
Doing a good progress bar is actually pretty hard. If I don't know how long tasks take I tend to take the opposite approach to Steve - assume later tasks will take longer so allocate them more. If progress is actually even this means the bar starts slow and speeds up, which pleases the user - people much prefer to be told they'll wait 10 minutes and then be ready in 5, than be told they'll wait 2 minutes and then be ready in 5.
Often though just doing 'tasks completed: 7/10' is fine. Task 9 might take 90% of the time, but after running the process a few times the user will know this, and as long as behaviour is vaguely consistent they'll adjust their expectations accordingly.
I wonder if there's a reason for the 15-19-21 thing here or if it's just a mistake.
Admin
The issue is that if task 7 takes 5 minutes the user might not get any indication that the program hasn't frozen during that task. And we are back to square 1. This is why it's better to have a constant animation, preferably one animated from a separate thread to ensure the user gets visual feedback saying "look at me, I am still working, not dead".
Admin
But it should still reflect progress being made imo. It's great that it's updating the dots on its own thread, but that just leaves me thinking it may be doing something while actually it may have gone into an infinite loop
Admin
Sounds a lot like progress reports from many developers, which is why so many tasks are always "90% complete" ;)
Admin
I assume that this subroutine is meant to be called each time a task is completed. If this is consistently called one or more times a second, this code mostly works, but second%3 + 1 is simpler and would work better. If the time between calls is random and often more than a second, either way gives a random animation, but as "Just another Embedded Designer" pointed out, it can "freeze" through several calls. I think a better approach uses two static variables, one to see if enough time has elapsed, and one to change the display:
if ((Me.Tim.Now.Second - seconds) >= 1) or ((Me.Tim.Now.Second - seconds) < 0) then dots = (dots + 1) % 3 seconds = Me.Tim.Now.Second Select Case dots Case 0 Me.StatusBarPanel1.Text = Str + "." Case 1 Me.StatusBarPanel1.Text = Str + ".." Case 2 Me.StatusBarPanel1.Text = Str + "..."
End Select End If
Apologies if the grammar is incorrect - I don't even know what language this is. (PASCAL? Has anyone used that in the last 30 years, outside of first-year programming classes?) The second clause in the 'if' statement could be improved, but that depends on the details of the language. The intention is to catch when Me.Tim.Now.Second has rolled over from 59 to 0. If this and 'seconds' is an unsigned integer and handled like c, the second clause is not even needed because the subtraction will give a very large number in the first clause, but if they are signed, the rollover could give an update in under a second. Subtracting date-time codes and getting the difference in seconds would be better, but that is very dependent on the language and library.
Maybe there is a better way to convert 'dots' to a string than a case statement, and I would love to see that.
Admin
I forgot about this site filtering single end-of-lines. Trying it with 'p' in angle brackets:
if ((Me.Tim.Now.Second - seconds) >= 1) or ((Me.Tim.Now.Second - seconds) < 0) then
dots = (dots + 1) % 3
seconds = Me.Tim.Now.Second
Select Case dots
Case 0
Me.StatusBarPanel1.Text = Str + "."
Case 1
Me.StatusBarPanel1.Text = Str + ".."
Case 2
Me.StatusBarPanel1.Text = Str + "..."
End Select
End If
Admin
The first throbber I recall dealing with for work was on a web page. One submitted a form on the prior page, and then this code was invoked for the next page. It was my introduction to multipart/alternative, if I recall correctly. It was really just a classic ASCII-art spinner, just over HTTP.
The bug report said it would pause for long periods of time, and when it did update, sometimes it would spin in the wrong direction.
Looking at the code, it didn't take me long to fix the first issue. The code was written in Perl, and just printed to STDOUT, which wouldn't be a terminal. So
$|=1;
fixed that. The second issue was fixed by having the spinner only update every 1000 operations rather than every. That resulted in an overall performance improvement, because the individual operations took less time than the buffered print statements.Admin
That's why I like the approach with WinRAR (I've not seen it elsewhere). You have a progress bar through the number of files being operated on, and a progress bar through the current file being operated on. So you might get:
Progress: 56% *****
This file: 95% *********
The "this file" bar zips quite quickly which performs the function of the throbber, but "progress" bar indicates how far on the whole task has got. And the "this file" indicates if there's a big jobbie slowing the completion of the "progress" bar.
Admin
For a task that takes five minutes arguably a progress bar is the wrong solution. Better to provide a mechanism for the user to come back later and get the result, or send it to them somehow. Sitting around waiting for something to load for five minutes is a bad UX (though probably unavoidable in some cases).
Admin
But without locking his computer and displaying an indicator that your program is working how is he going to know that it started, is working and hasn't hanged? The ultimate goal is, you must remember, to make sure he does not get confused, frustrated and start clicking 10 instances open thinking the previous 9 have hung.
Admin
I've seen this in a few places - WinSCP for one. The Borland installers (and programs that used their install dialogs, which they made available for you) also had something similar, but with a "disk free space" indicator as well.