Opened 9 years ago

Last modified 3 years ago

#378 assigned feature-request

Advanced (pieces) progress bar

Reported by: andar Owned by:
Priority: major Milestone: 2.0.x
Component: Web UI Version:
Keywords: Cc: ufs@…, kernja@…

Description


Attachments (15)

pieces_bar.patch (9.8 KB) - added by James_Kern 7 years ago.
patch for adding pieces bar to status tab, against 1.2.1
pieces_bar_2.patch (96.3 KB) - added by James_Kern 7 years ago.
piecesbar_gtk_js.patch (35.1 KB) - added by James_Kern 7 years ago.
progress.png (2.1 KB) - added by s0undt3ch 6 years ago.
Progress bar screenshot
progress-new.png (9.4 KB) - added by s0undt3ch 6 years ago.
New approach using cairo
pieces_test.py (6.1 KB) - added by s0undt3ch 6 years ago.
Small test which muliplies the data available by 100, like show on the previsous screenshot attachment
with-progress-bar.png (3.0 KB) - added by s0undt3ch 6 years ago.
With an overal progress bar
with-progress-bar1.png (8.0 KB) - added by s0undt3ch 6 years ago.
with-progress-bar2.png (4.6 KB) - added by s0undt3ch 6 years ago.
with-progress-bar3.png (4.7 KB) - added by s0undt3ch 6 years ago.
with-progress-bar4.png (4.3 KB) - added by s0undt3ch 6 years ago.
final-progress-bar.png (10.7 KB) - added by s0undt3ch 6 years ago.
final-progress-bar1.png (4.9 KB) - added by s0undt3ch 6 years ago.
final-progress-bar2.png (26.6 KB) - added by s0undt3ch 6 years ago.
C olors config
piecesbar_webui.patch (6.9 KB) - added by James_Kern 6 years ago.
canvas based piecesbar for web ui

Download all attachments as: .zip

Change History (56)

comment:1 Changed 8 years ago by mvoncken

  • Milestone changed from 1.1.0 to 1.2.0

comment:2 Changed 8 years ago by anonymous

This would be really nice, especially for the individual files view

comment:3 Changed 8 years ago by s0undt3ch

  • Cc ufs@… added

comment:4 Changed 8 years ago by andar

  • Milestone changed from 1.2.0 to Future
  • Version 1.1.0_dev deleted

comment:5 Changed 7 years ago by James_Kern

  • Cc kernja@… added

To get a feel for the code base, I've swapped out the progress bar in the status tab with a bar that shows you the status of each piece in the torrent. Not sure on how to go about getting this merged back in though.

comment:6 Changed 7 years ago by damoxc

Attach a patch so we can have a look at it?

Changed 7 years ago by James_Kern

patch for adding pieces bar to status tab, against 1.2.1

comment:7 Changed 7 years ago by James_Kern

Yea I should've guessed that :)

comment:8 Changed 7 years ago by damoxc

Looks good, although I would change it to return None rather than an empty list, and merge line 486 and 487 so you end up with:

def get_pieces_status(self):
    if not self.handle.has_metadata():
        return None
    return self.handle.status().pieces

Returning None rather than an empty list is less expensive.

andar do you have any thoughts?

comment:9 Changed 7 years ago by James_Kern

bleck, noticed a couple problems. The bar expands to fill the vertical space when the panel is expanded, which looks ugly. Also when a torrent has more pieces than there are pixels wide, the bar shows up as empty. The first shouldn't be too bad, but not sure how easy the second one will be to fix.

comment:10 Changed 7 years ago by damoxc

The first issue can be solved with a gtk setting, can't remember which one it is off the top of my head but widgets can be set to not auto-expand.

As for the second, the only solution I can think of would be to start dropping pieces, or merge pieces together and average, so

if piece_count > bar_width:
    _pieces = pieces[:]
    pieces = []
    while _pieces:
        one = _pieces.pop(0)
        two = _pieces.pop(0)
        # do calc
        pieces.append(one + two / 2)

That is of course pseudo code just to give a general idea of what I was saying.

comment:11 Changed 7 years ago by andar

You could also change the progress bar to a progress grid if you do not have enough space. This would basically split up the vertical space so you can gain some additional rows without losing any piece information by dropping or merging pieces.

Changed 7 years ago by James_Kern

comment:12 Changed 7 years ago by James_Kern

Ok, fixed the expansion issue at the cost of the height being fixed now. For the other issue I went with grouping the pieces like damoxc suggested. It does lose a little bit of information versus using a grid where you can fit more pieces, but I think it still gives a close enough approximation. Also you could still potentially run into the same issue using the grid if a torrent had more pieces then pixels in the bar (unlikely as that may be).

comment:13 Changed 7 years ago by James_Kern

Do you guys have any thoughts on how to do this in the web ui? Looked through the ExtJS docs and didn't see anything promising on first glance. Next thought is to generate an image server-side using cairo.

comment:14 Changed 7 years ago by damoxc

Nah should do this client side, less load you can place on the web server the better.

Could build something along the lines of:

<div class="x-piecesbar-wrapper">
        <div class="x-piecesbar-piece x-piece-complete"></div>
        <div class="x-piecesbar-piece"></div>
        <div class="x-piecesbar-piece"></div>
        <div class="x-piecesbar-piece"></div>
        <div class="x-piecesbar-piece x-piece-complete"></div>
        <div class="x-piecesbar-piece x-piece-complete"></div>
        <div class="x-piecesbar-piece x-piece-complete"></div>
        <div class="x-piecesbar-piece x-piece-complete"></div>
        <div class="x-piecesbar-piece x-piece-complete"></div>
        <div class="x-piecesbar-piece"></div>
        <div class="x-piecesbar-piece"></div>
        <div class="x-piecesbar-piece x-piece-complete"></div>
        <div class="x-piecesbar-piece"></div>
        ...
</div>

This should be built up via a class named Deluge.Piecesbar that extends from Ext.BoxComponent?.

comment:15 Changed 7 years ago by damoxc

Oh and the css would be something like:

.x-piecesbar-piece {
    display: inline; /* or float: left; */
    color: #fff;
    width: 1px;
}

.x-piece-complete {
    color: #000;
}

comment:16 follow-up: Changed 7 years ago by James_Kern

Been a little while, but was bored over the weekend so finally took a stab at implementing this for the WebUI as well. Attached a patch against git trunk.

Only concern right now is that it's a little slow on torrents with lots of pieces (i.e. more than 1000). Doesn't seem to be much of a problem on chrome, but firefox is a little laggy when switching between torrents. I think the problem is clearing all the <div>'s, but not sure of any more efficient way of doing that.

Changed 7 years ago by James_Kern

comment:17 Changed 6 years ago by Cas

  • Type changed from feature-request to patch

comment:18 in reply to: ↑ 16 Changed 6 years ago by s0undt3ch

Replying to James_Kern:

Been a little while, but was bored over the weekend so finally took a stab at implementing this for the WebUI as well. Attached a patch against git trunk.

Only concern right now is that it's a little slow on torrents with lots of pieces (i.e. more than 1000). Doesn't seem to be much of a problem on chrome, but firefox is a little laggy when switching between torrents. I think the problem is clearing all the <div>'s, but not sure of any more efficient way of doing that.

How about drawing on a canvas?

comment:19 Changed 6 years ago by s0undt3ch

So, should this be done for 1.4?

comment:20 Changed 6 years ago by s0undt3ch

Ppl, I'm trying to extend the pieces information. Ie, each piece should have 4 states:

  • 0 - Missing
  • 1 - Present but not downloaded
  • 2 - Downloading
  • 3 - Downloaded

So far I've managed to build this information, but, I'm at the end of my night shift and my head is getting too tired. How can I adapt the reduce function to this?

Here's an example list:

[3, 2, 1, 1, 1, 0, 0, 2, 0, 1, 0, 2, 0, 2, 1, 0, 2, 0, 0, 0, 2,
0, 0, 0, 2, 0, 0, 2, 2, 1, 0, 2, 1, 0, 1, 1, 0, 0, 2, 0, 0, 0,
0, 0, 0, 3, 3, 0, 0, 2, 0, 2, 2, 2, 0, 2, 2, 1]

comment:21 Changed 6 years ago by s0undt3ch

>>> x = [3, 2, 1, 1, 1, 0, 0, 2, 0, 1, 0, 2, 0, 2, 1, 0, 2, 0, 0, 0,
2, 0, 0, 0, 2, 0, 0, 2, 2, 1, 0, 2, 1, 0, 1, 1, 0, 0, 2, 0, 0, 0, 0,
0, 0, 3, 3, 0, 0, 2, 0, 2, 2, 2, 0, 2, 2, 1]
>>> t = len(x)*1.0 
>>> xx = [] 
>>> v = x.pop(0) 
>>> n = 1 
>>> while True: 
...     if not x: 
...         break 
...     vv = x.pop(0) 
...     if vv == v: 
...         n += 1 
...     else: 
...         xx.append(n/t) 
...         n = 1 
...  
>>> xx 
[0.017241379310344827, 0.017241379310344827, 0.017241379310344827, 0.017241379310344827,
0.017241379310344827, 0.017241379310344827, 0.017241379310344827, 0.017241379310344827,
0.017241379310344827, 0.017241379310344827, 0.017241379310344827, 0.017241379310344827,
0.017241379310344827, 0.017241379310344827, 0.017241379310344827, 0.017241379310344827,
0.017241379310344827, 0.017241379310344827, 0.017241379310344827, 0.017241379310344827,
0.017241379310344827, 0.017241379310344827, 0.017241379310344827, 0.017241379310344827,
0.017241379310344827, 0.017241379310344827, 0.017241379310344827, 0.017241379310344827,
0.017241379310344827, 0.017241379310344827, 0.017241379310344827, 0.017241379310344827,
0.017241379310344827, 0.017241379310344827, 0.017241379310344827, 0.017241379310344827,
0.017241379310344827, 0.017241379310344827, 0.017241379310344827, 0.017241379310344827,
0.017241379310344827, 0.017241379310344827, 0.017241379310344827, 0.017241379310344827,
0.051724137931034482, 0.017241379310344827, 0.017241379310344827, 0.017241379310344827,
0.017241379310344827, 0.017241379310344827, 0.017241379310344827, 0.017241379310344827,
0.017241379310344827, 0.017241379310344827, 0.017241379310344827]
>>> len(xx) 
55 
>>> t 
58.0 
>>>

Didn't reduce that much, but the data was not helping much too ;)

Is this the way to go?

The final values and width percentages, considering 58 is 100%

comment:22 Changed 6 years ago by s0undt3ch

Actually:

>>> x = [3, 2, 1, 1, 1, 0, 0, 2, 0, 1, 0, 2, 0, 2, 1, 0, 2, 0, 0, 0, 2, 0, 0, 0,
...      2, 0, 0, 2, 2, 1, 0, 2, 1, 0, 1, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 3, 0,
...      0, 2, 0, 2, 2, 2, 0, 2, 2, 1]
>>> t = len(x)*1.0
>>> xx = []
>>> v = None
>>> n = None
>>> 
>>> while True:
...     if not x:
...         break
...     if v is None:
...         v = x.pop(0)
...         n = 1
...     else:
...         vv = x.pop(0)
...         if v == vv:
...             n += 1
...         else:
...             xx.append((v, n/t))
...             v = vv
...             n = 1
... 
>>> 
>>> 
>>> print xx
[(3, 0.017241379310344827), (2, 0.017241379310344827),
(1, 0.051724137931034482), (0, 0.034482758620689655),
(2, 0.017241379310344827), (0, 0.017241379310344827),
(1, 0.017241379310344827), (0, 0.017241379310344827),
(2, 0.017241379310344827), (0, 0.017241379310344827),
(2, 0.017241379310344827), (1, 0.017241379310344827),
(0, 0.017241379310344827), (2, 0.017241379310344827),
(0, 0.051724137931034482), (2, 0.017241379310344827),
(0, 0.051724137931034482), (2, 0.017241379310344827),
(0, 0.034482758620689655), (2, 0.034482758620689655),
(1, 0.017241379310344827), (0, 0.017241379310344827),
(2, 0.017241379310344827), (1, 0.017241379310344827),
(0, 0.017241379310344827), (1, 0.034482758620689655),
(0, 0.034482758620689655), (2, 0.017241379310344827),
(0, 0.10344827586206896), (3, 0.034482758620689655),
(0, 0.034482758620689655), (2, 0.017241379310344827),
(0, 0.017241379310344827), (2, 0.051724137931034482),
(0, 0.017241379310344827), (2, 0.034482758620689655)]
>>> print len(xx)
36
>>>

comment:23 Changed 6 years ago by s0undt3ch

Here's a GTK example, nevermind the colors used ;)

import pygtk
pygtk.require('2.0')
import gtk

class PiecesBar(gtk.DrawingArea):
    def __init__(self):
        gtk.DrawingArea.__init__(self)
        self.width = 0
        self.height = 0
        self.pieces = []

        self.connect('size-allocate', self.on_size_allocate)
        self.connect('expose-event', self.update)
        self.connect('realize', self.on_realize)

        self.show()

    def on_realize(self, widget):
        map = widget.get_colormap()
        done_color = map.alloc_color("#325891")
        down_color = map.alloc_color("#67DB63")
        wait_color = map.alloc_color("#EBF57A")
        miss_color = map.alloc_color("#FF4D36")
        outline_color = map.alloc_color("#888888")

        self.colors = {
            0: widget.window.new_gc(foreground=miss_color),
            1: widget.window.new_gc(foreground=wait_color),
            2: widget.window.new_gc(foreground=down_color),
            3: widget.window.new_gc(foreground=done_color)
        }
        self.gc_done = widget.window.new_gc(foreground=done_color)
        self.gc_down = widget.window.new_gc(foreground=down_color)
        self.gc_wait = widget.window.new_gc(foreground=wait_color)
        self.gc_miss = widget.window.new_gc(foreground=miss_color)
        self.gc_outline = widget.window.new_gc(foreground=outline_color)

    def on_size_allocate(self, widget, size):
        self.width = size.width
        self.height = size.height

    def update(self, widget=None, event=None):
        num_pieces = len(self.pieces)

        if num_pieces < 1:
            self.clear()
            return None

        self.window.draw_rectangle(self.gc_outline, False, 0, 0,
                                   self.width - 1, self.height - 1)

        width = self.width - 2
        pieces = self.collapse_pieces()

        start_pos = 1

        for state, wpercent in pieces:
            print state, wpercent
            pwidth = width*wpercent
            print 1, pwidth
            self.draw_piece(state, start_pos, 1, pwidth, self.height - 2)
            start_pos += pwidth

    def collapse_pieces(self):
        num_pieces = len(self.pieces)*1.0
        opieces = self.pieces[:]
        npieces = []
        v = None
        n = None
        while True:
            if not opieces:
                break
            if v is None:
                v = opieces.pop(0)
                n = 1
            else:
                vv = opieces.pop(0)
                if v == vv:
                    n += 1
                else:
                    npieces.append((v, n/num_pieces))
                    v = vv
                    n = 1
        return npieces

    def draw_piece(self, piece, start_x, start_y, width, height):
        self.window.draw_rectangle(
            self.colors[piece], True, start_x, start_y, width, height
        )

    def clear(self):
        self.pieces = [0]
        self.update()

    def get_text(self):
        return ""

    def set_text(self, text):
        pass

pieces = PiecesBar()
pieces.pieces = [3, 2, 1, 1, 1, 0, 0, 2, 0, 1, 0, 2, 2, 2, 1, 2, 2, 0, 0, 0, 2,
                 1, 1, 1, 2, 0, 0, 2, 2, 1, 0, 2, 1, 0, 1, 1, 0, 0, 2, 3, 3, 3,
                 3, 3, 0, 3, 3, 0, 0, 2, 0, 2, 2, 2, 0, 2, 2, 1]

window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.add(pieces)
window.show_all()
gtk.main()

How about this?

comment:24 Changed 6 years ago by s0undt3ch

Still needs a percent complete label added, like the progressbar has.

Changed 6 years ago by s0undt3ch

Progress bar screenshot

Changed 6 years ago by s0undt3ch

New approach using cairo

Changed 6 years ago by s0undt3ch

Small test which muliplies the data available by 100, like show on the previsous screenshot attachment

comment:25 Changed 6 years ago by s0undt3ch

Using cairo, like shown on the attached pieces_test.py, not data is lost, and event if there's huge amount of pieces for a really small space, all data will be shown. The pieces are only collapsed to reduce the number of rectangle draws. I like this approach much better. How about it? I'll take up the GTK part of the task of this is accepted.

comment:26 Changed 6 years ago by s0undt3ch

By the way, this is partially done on a local branch. I'll upload it to deluge's git if there's interest.

comment:27 Changed 6 years ago by James_Kern

The cairo approach is nice! For the web side of this we'd have to change to using canvas, since I think that's the only way to support using floats for width. I took a first pass at it and it didn't seem too hard to switch to. If there's actual interest in getting this in I'll commit to doing that part.

Changed 6 years ago by s0undt3ch

With an overal progress bar

comment:28 Changed 6 years ago by s0undt3ch

How about also including an overall progress bar on the bottom? Screenshot attached.

comment:29 follow-up: Changed 6 years ago by James_Kern

I don't think the progress bar is really necessary, since there's one shown above in the main torrent list. The bar is already a pretty busy area visually so I'd prefer not add something else to it too.

comment:30 in reply to: ↑ 29 Changed 6 years ago by s0undt3ch

Replying to James_Kern:

I don't think the progress bar is really necessary, since there's one shown above in the main torrent list. The bar is already a pretty busy area visually so I'd prefer not add something else to it too.

My idea for this pieces bar is to either show the progress bar in the torrent list, or, show the pieces bar. Not both. In the torrent details, I'm open for discussion, same behavior?

comment:31 Changed 6 years ago by s0undt3ch

Now, I see. The original patch was just for the status tab :) Well, what I'm after is to replace the dull progress bar with the pieces bar.

comment:32 Changed 6 years ago by shnurapet

Keep it nice and simple, please.

Changed 6 years ago by s0undt3ch

Changed 6 years ago by s0undt3ch

Changed 6 years ago by s0undt3ch

Changed 6 years ago by s0undt3ch

comment:33 Changed 6 years ago by s0undt3ch

The last 4 screenshots attached, with-progress-barX.png, show the current status of the implementation, and removes the need to have the regular progress bar above the pieces bar.

comment:34 Changed 6 years ago by s0undt3ch

There's a testing branch now for every one to try and report bugs before merging to master:

Changed 6 years ago by s0undt3ch

Changed 6 years ago by s0undt3ch

comment:35 Changed 6 years ago by s0undt3ch

Final pieces bar screenshots(2)

Changed 6 years ago by s0undt3ch

C olors config

comment:36 Changed 6 years ago by Cas

  • Milestone changed from Future to 1.4.0
  • Owner changed from andar to s0undt3ch
  • Status changed from new to assigned
  • Type changed from patch to feature-request

comment:37 follow-up: Changed 6 years ago by James_Kern

I've mostly ported the new implementation to the web ui (haven't looked into hooking it into the preferences) on the pieces-progress-bar branch. Don't know how to push changes back up using git (or if I should even) so I'm attaching a patch here instead

Changed 6 years ago by James_Kern

canvas based piecesbar for web ui

comment:38 follow-up: Changed 6 years ago by damoxc

Excellent thanks, I'm currently in the progress of migrating master to extjs4 (extjs4-port on git) so once I've done that I will integrate your patch!

I might modify it a little bit to change it into a widget so it can be used else where if anyone wants to in a plugin or something if that's okay?

comment:39 in reply to: ↑ 37 Changed 6 years ago by s0undt3ch

Replying to James_Kern:

I've mostly ported the new implementation to the web ui (haven't looked into hooking it into the preferences) on the pieces-progress-bar branch. Don't know how to push changes back up using git (or if I should even) so I'm attaching a patch here instead

Great Stuff!

comment:40 in reply to: ↑ 38 Changed 6 years ago by James_Kern

Replying to damoxc:

Excellent thanks, I'm currently in the progress of migrating master to extjs4 (extjs4-port on git) so once I've done that I will integrate your patch!

I might modify it a little bit to change it into a widget so it can be used else where if anyone wants to in a plugin or something if that's okay?

Yea, absolutely. If you need any help with the extjs4 port I'd be happy to lend a hand

comment:41 Changed 3 years ago by Cas

  • Component changed from GTK-UI to Web-UI
  • Owner s0undt3ch deleted
Note: See TracTickets for help on using tickets.