Browse Source

Add back & forward communication between Worker and Main thread

doc-issue-template
MrS0m30n3 9 years ago
parent
commit
6377928119
2 changed files with 119 additions and 9 deletions
  1. 84
      youtube_dl_gui/downloadmanager.py
  2. 44
      youtube_dl_gui/mainframe.py

84
youtube_dl_gui/downloadmanager.py

@ -166,6 +166,21 @@ class DownloadManager(Thread):
""" """
self.urls_list.append(url) self.urls_list.append(url)
def send_to_worker(self, data):
"""Send data to the Workers.
Args:
data (dict): Python dictionary that holds the 'index'
which is used to identify the Worker thread and the data which
can be any of the Worker's class valid data. For a list of valid
data keys see __init__() under the Worker class.
"""
if 'index' in data:
for worker in self._workers:
if worker.has_index(data['index']):
worker.update_data(data)
def _talk_to_gui(self, data): def _talk_to_gui(self, data):
"""Send data back to the GUI using wxCallAfter and wxPublisher. """Send data back to the GUI using wxCallAfter and wxPublisher.
@ -174,7 +189,7 @@ class DownloadManager(Thread):
download process. download process.
Note: Note:
DownloadManager supports 3 signals.
DownloadManager supports 4 signals.
1) closing: The download process is closing. 1) closing: The download process is closing.
2) closed: The download process has closed. 2) closed: The download process has closed.
3) finished: The download process was completed normally. 3) finished: The download process was completed normally.
@ -243,9 +258,12 @@ class Worker(Thread):
self._successful = 0 self._successful = 0
self._running = True self._running = True
self._wait_for_reply = False
self._data = { self._data = {
'playlist_index': None, 'playlist_index': None,
'playlist_size': None, 'playlist_size': None,
'new_filename': None,
'extension': None, 'extension': None,
'filesize': None, 'filesize': None,
'filename': None, 'filename': None,
@ -270,6 +288,13 @@ class Worker(Thread):
ret_code == YoutubeDLDownloader.ALREADY): ret_code == YoutubeDLDownloader.ALREADY):
self._successful += 1 self._successful += 1
# Ask GUI for name updates
self._talk_to_gui('receive', {'source': 'filename', 'dest': 'new_filename'})
# Wait until you get a reply
while self._wait_for_reply:
time.sleep(self.WAIT_TIME)
self._reset() self._reset()
time.sleep(self.WAIT_TIME) time.sleep(self.WAIT_TIME)
@ -303,6 +328,19 @@ class Worker(Thread):
"""Return True if the worker has no job else False. """ """Return True if the worker has no job else False. """
return self._data['url'] is None return self._data['url'] is None
def has_index(self, index):
"""Return True if index is equal to self._data['index'] else False. """
return self._data['index'] == index
def update_data(self, data):
"""Update self._data from the given data. """
if self._wait_for_reply:
# Update data only if a receive request has been issued
for key in data:
self._data[key] = data[key]
self._wait_for_reply = False
@property @property
def successful(self): def successful(self):
"""Return the number of successful downloads for current worker. """ """Return the number of successful downloads for current worker. """
@ -359,10 +397,44 @@ class Worker(Thread):
) )
if len(temp_dict): if len(temp_dict):
temp_dict['index'] = self._data['index']
self._talk_to_gui(temp_dict)
self._talk_to_gui('send', temp_dict)
def _talk_to_gui(self, data):
"""Send data back to the GUI. """
CallAfter(Publisher.sendMessage, WORKER_PUB_TOPIC, data)
def _talk_to_gui(self, signal, data):
"""Communicate with the GUI using wxCallAfter and wxPublisher.
Send/Ask data to/from the GUI. Note that if the signal is 'receive'
then the Worker will wait until it receives a reply from the GUI.
Args:
signal (string): Unique string that informs the GUI about the
communication procedure.
data (dict): Python dictionary which holds the data to be sent
back to the GUI. If the signal is 'send' then the dictionary
contains the updates for the GUI (e.g. percentage, eta). If
the signal is 'receive' then the dictionary contains exactly
three keys. The 'index' (row) from which we want to retrieve
the data, the 'source' which identifies a column in the
wxListCtrl widget and the 'dest' which tells the wxListCtrl
under which key to store the retrieved data.
Note:
Worker class supports 2 signals.
1) send: The Worker sends data back to the GUI
(e.g. Send status updates).
2) receive: The Worker asks data from the GUI
(e.g. Receive the name of a file).
Structure:
('send', {'index': <item_row>, data_to_send*})
('receive', {'index': <item_row>, 'source': 'source_key', 'dest': 'destination_key'})
"""
data['index'] = self._data['index']
if signal == 'receive':
self._wait_for_reply = True
CallAfter(Publisher.sendMessage, WORKER_PUB_TOPIC, (signal, data))

44
youtube_dl_gui/mainframe.py

@ -173,7 +173,7 @@ class MainFrame(wx.Frame):
# Set threads wxCallAfter handlers using subscribe # Set threads wxCallAfter handlers using subscribe
self._set_publisher(self._update_handler, UPDATE_PUB_TOPIC) self._set_publisher(self._update_handler, UPDATE_PUB_TOPIC)
self._set_publisher(self._status_list_handler, WORKER_PUB_TOPIC)
self._set_publisher(self._download_worker_handler, WORKER_PUB_TOPIC)
self._set_publisher(self._download_manager_handler, MANAGER_PUB_TOPIC) self._set_publisher(self._download_manager_handler, MANAGER_PUB_TOPIC)
def _set_publisher(self, handler, topic): def _set_publisher(self, handler, topic):
@ -332,7 +332,7 @@ class MainFrame(wx.Frame):
if not success: if not success:
self._status_bar_write(self.OPEN_DIR_ERR.format(dir=self.opt_manager.options['save_path'])) self._status_bar_write(self.OPEN_DIR_ERR.format(dir=self.opt_manager.options['save_path']))
def _status_list_handler(self, msg):
def _download_worker_handler(self, msg):
"""downloadmanager.Worker thread handler. """downloadmanager.Worker thread handler.
Handles messages from the Worker thread. Handles messages from the Worker thread.
@ -341,7 +341,13 @@ class MainFrame(wx.Frame):
See downloadmanager.Worker _talk_to_gui() method. See downloadmanager.Worker _talk_to_gui() method.
""" """
self._status_list.write(msg.data)
signal, data = msg.data
if signal == 'send':
self._status_list.write(data)
if signal == 'receive':
self.download_manager.send_to_worker(self._status_list.get(data))
def _download_manager_handler(self, msg): def _download_manager_handler(self, msg):
"""downloadmanager.DownloadManager thread handler. """downloadmanager.DownloadManager thread handler.
@ -529,6 +535,38 @@ class ListCtrl(wx.ListCtrl, ListCtrlAutoWidthMixin):
self._url_list = set() self._url_list = set()
self._set_columns() self._set_columns()
def get(self, data):
"""Return data from ListCtrl.
Args:
data (dict): Dictionary which contains three keys. The 'index'
that identifies the current row, the 'source' which identifies
a column in the wxListCtrl and the 'dest' which tells
wxListCtrl under which key to store the retrieved value. For
more informations see the _talk_to_gui() method under
downloadmanager.py Worker class.
Returns:
A dictionary which holds the 'index' (row) and the data from the
given row-column combination.
Example:
args: data = {'index': 0, 'source': 'filename', 'dest': 'new_filename'}
The wxListCtrl will store the value from the 'filename' column
into a new dictionary with a key value 'new_filename'.
return: {'index': 0, 'new_filename': 'The filename retrieved'}
"""
value = None
# If the source column exists
if data['source'] in self.columns:
value = self.GetItemText(data['index'], self.columns[data['source']][0])
return {'index': data['index'], data['dest']: value}
def write(self, data): def write(self, data):
"""Write data on ListCtrl row-column. """Write data on ListCtrl row-column.

Loading…
Cancel
Save