1

I am having a bit of trouble understanding callbacks. I have the following usecase:

From my gui i start a new thread called videoDown (which is its own class). From this thread i want to push the data back to the gui which i have implemented a callback system for. BUT to update the GUI i need to be in the GUI thread and not the thread i currently reside in (which is the videoDown thread).

Code where i start my thread (class Downloader):

def download_single(self, json_data):
    form_data = json.loads(json_data)
    print self.app
    url = form_data["name"]
    dt = form_data["dt"]  # Download type is audio or video
    videoDown = videoDownload(url, dt, self.dd,callback=self.cb,callback_args=("hello", "world",self.app))
    videoDown.start()

videoDownload thread:

class videoDownload(threading.Thread):
def __init__(self,url, dt, dd,callback=None, callback_args=None, *args, **kwargs):
    threading.Thread.__init__(self)
    self.callback = callback
    self.url = url
    self.dt = dt
    self.dd = dd
    self.callback_args = callback_args
    if self.callback is not None:
        self.callback(*self.callback_args)

def run(self):
    if self.url.__contains__("https://www.youtube.com/watch?v="):
        if self.dt == 'audio':
            self._downloadVid(self.url, vid=False)
        else:
            self._downloadVid(self.url, vid=True)
    else:
        print "Incorrect url"

def _downloadVid(self, url, vid=False, order_reverse=False, vinName=None):
    video = pafy.new(url)

    if self.callback is not None: //<--CALLBACK IN HERE
        self.callback(*self.callback_args)
    streams = video.allstreams
    for stream in streams:
        print stream

    print video
    name = u''.join(video.title).encode('utf8')
    name = re.sub("[<>:\"/\\|?*]", "", name)
    if not vid:
        file = video.getbestaudio()
    else:
        file = video.getbest()
    if (order_reverse):
        file.download(self.dd + vinName + name + ".mp4", quiet=False, callback=self.mycb)
    else:
        file.download(self.dd + name + ".mp4", quiet=False, callback=self.mycb)

Callback (also in class Downloader):

def cb(self,param1, param2,param3):
    print threading.current_thread()

How exactly do i implement it so i can give data from my video download thread back to the gui thread while the current thread is set to this video download thread.

What do i need to change, i have been struggling with this for hours.

~Greetings

2 Answers 2

1

Maybe use a Queue instead of a callback. Initialise the queue in your main thread, pass the object to the download thread and call get() on the Queue object to block until an item is passed to it.

q = Queue.Queue()
d = ("hello", "world", self.app)
videoDown = videoDownload(url, dt, self.dd, queue=q, data=d)
returned_data = q.get()

In the videoDownload object pass the data back to the main thread via the Queue

if self.queue and self.data:
    self.queue.put(self.data)
Sign up to request clarification or add additional context in comments.

8 Comments

Wont this block my GUI? And im trying to do it with callbacks cause i want to change stuff inside the gui with returned data.
If you cannot block like this in the GUI thread you can call get(block=False) and poll for the returned data.
How do i poll for returned data
Does your GUI have an event loop? If so get() should probably be called there. Either use a mechanism like select to allow you to wait on multiple events, or add a timeout to the wait on GUI events and poll the queue with q.get(block=False).
I use htmlpy so i honestly have no idea.
|
1

With a lot of help from luke. I came up with the following answer.

In my javascript i created the following function:

    function start() {
        Downloader.trydasd(); //ignore the nameconvention. We all try sometimes
        setTimeout(start, 3000);
    }

    start();

Which is implemented in python like this:

@htmlPy.Slot()
def trydasd(self):
    if not self.q.empty():
        print self.q.get(block=False)

After that I changed my init to create a queue like this:

    def __init__(self, app):
        super(Downloader, self).__init__()
        # Initialize the class here, if required.
        self.app = app
        self.q = Queue.Queue()

After that i changed my download_single function to this:

 def download_single(self, json_data):
    form_data = json.loads(json_data)
    print self.app
    url = form_data["name"]
    dt = form_data["dt"]  # Download type is audio or video
    d = ("hello", "world", self.app)
    videoDown = videoDownload(url, dt, self.dd, queue=self.q, data=d)
    videoDown.start()

And lastly my download thread is now this:

class videoDownload(threading.Thread):
def __init__(self,url, dt, dd,queue=None,data=None, *args, **kwargs):
    threading.Thread.__init__(self)
    self.queue = queue
    self.data = data
    self.url = url
    self.dt = dt
    self.dd = dd

def run(self):
    if self.url.__contains__("https://www.youtube.com/watch?v="):
        if self.dt == 'audio':
            self._downloadVid(self.url, vid=False)
        else:
            self._downloadVid(self.url, vid=True)
    else:
        print "Incorrect url"

def _downloadVid(self, url, vid=False, order_reverse=False, vinName=None):
    video = pafy.new(url)
    if self.queue and self.data:
        self.queue.put(video)

    name = u''.join(video.title).encode('utf8')
    name = re.sub("[<>:\"/\\|?*]", "", name)
    if not vid:
        file = video.getbestaudio()
    else:
        file = video.getbest()
    if (order_reverse):
        file.download(self.dd + vinName + name + ".mp4", quiet=False, callback=self.mycb)
    else:
        file.download(self.dd + name + ".mp4", quiet=False, callback=self.mycb)

This solved my issue, any other way does NOT work and will block the UI.

Thank you a lot luke!

1 Comment

No problem, glad we got there in the end.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.