From 102890e2fe014e970c8d89e54ee06e5dc9e69ea5 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Wed, 16 Mar 2016 19:38:15 +0300 Subject: [PATCH 01/32] file transfers init commit --- src/file_transfers.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/file_transfers.py diff --git a/src/file_transfers.py b/src/file_transfers.py new file mode 100644 index 0000000..40ac685 --- /dev/null +++ b/src/file_transfers.py @@ -0,0 +1 @@ +# TODO: add support of file transfers and avatars From c0cf8e9fa948996da795e43bbce48f477c62a6bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=92=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=B8=D0=BC=D0=B8=D1=80=D0=BE=D0=B2=D0=B8=D1=87?= Date: Wed, 16 Mar 2016 19:54:46 +0300 Subject: [PATCH 02/32] todo splitting --- src/file_transfers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/file_transfers.py b/src/file_transfers.py index 40ac685..9d4995b 100644 --- a/src/file_transfers.py +++ b/src/file_transfers.py @@ -1 +1,2 @@ -# TODO: add support of file transfers and avatars +# TODO: add support of file transfers +# TODO: add support of avatars From 44daae2b80dd677f88548c9e5faca1fd9b3b1132 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Wed, 16 Mar 2016 21:16:58 +0300 Subject: [PATCH 03/32] callback for file transfer request created --- src/callbacks.py | 48 +++++++++++++++++++++++++++++++++++++++++++++++- src/profile.py | 11 +++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/callbacks.py b/src/callbacks.py index 3b8d960..fb8768f 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -28,6 +28,10 @@ _invoker = Invoker() def invoke_in_main_thread(fn, *args, **kwargs): QtCore.QCoreApplication.postEvent(_invoker, InvokeEvent(fn, *args, **kwargs)) +# ----------------------------------------------------------------------------------------------------------------- +# Callbacks - current user +# ----------------------------------------------------------------------------------------------------------------- + def self_connection_status(tox_link): """ @@ -45,6 +49,11 @@ def self_connection_status(tox_link): return wrapped +# ----------------------------------------------------------------------------------------------------------------- +# Callbacks - friends +# ----------------------------------------------------------------------------------------------------------------- + + def friend_status(tox, friend_num, new_status, user_data): """ Check friend's status (none, busy, away) @@ -116,6 +125,40 @@ def friend_request(tox, public_key, message, message_size, user_data): tox_id = bin_to_string(public_key, TOX_PUBLIC_KEY_SIZE) invoke_in_main_thread(profile.process_friend_request, tox_id, message.decode('utf-8')) +# ----------------------------------------------------------------------------------------------------------------- +# Callbacks - file transfers +# ----------------------------------------------------------------------------------------------------------------- + + +def tox_file_recv(window, tray): + def wrapped(tox, friend_number, file_number, file_type, size, file_name, file_name_size, user_data): + profile = Profile.get_instance() + settings = Settings.get_instance() + if file_type == TOX_FILE_KIND['DATA']: + print 'file' + file_name = file_name[:].decode('utf8') + invoke_in_main_thread(profile.incoming_file_transfer, + friend_number, + file_number, + size, + file_name) + if not window.isActiveWindow(): + friend = profile.get_friend_by_number(friend_number) + if settings['notifications']: + invoke_in_main_thread(tray_notification, 'File from ' + friend.name, file_name, tray) + if settings['sound_notifications']: + sound_notification(SOUND_NOTIFICATION['FILE_TRANSFER']) + else: # AVATAR + print 'Avatar' + invoke_in_main_thread(profile.incoming_avatar, + friend_number, + file_number) + return wrapped + +# ----------------------------------------------------------------------------------------------------------------- +# Callbacks - initialization +# ----------------------------------------------------------------------------------------------------------------- + def init_callbacks(tox, window, tray): """ @@ -124,10 +167,13 @@ def init_callbacks(tox, window, tray): :param window: main window :param tray: tray (for notifications) """ + tox.callback_self_connection_status(self_connection_status(tox), 0) + tox.callback_friend_status(friend_status, 0) tox.callback_friend_message(friend_message(window, tray), 0) - tox.callback_self_connection_status(self_connection_status(tox), 0) tox.callback_friend_connection_status(friend_connection_status, 0) tox.callback_friend_name(friend_name, 0) tox.callback_friend_status_message(friend_status_message, 0) tox.callback_friend_request(friend_request, 0) + + tox.callback_file_recv(tox_file_recv(window, tray), 0) diff --git a/src/profile.py b/src/profile.py index 5305968..a8fcd70 100644 --- a/src/profile.py +++ b/src/profile.py @@ -304,6 +304,7 @@ class Profile(Contact, Singleton): self._screen = screen self._messages = screen.messages self._tox = tox + self._file_transfers = [] # list of file transfers settings = Settings.get_instance() self._show_online = settings['show_online_friends'] screen.online_contacts.setChecked(self._show_online) @@ -688,6 +689,16 @@ class Profile(Contact, Singleton): for friend in self._friends: friend.status = None + # ----------------------------------------------------------------------------------------------------------------- + # File transfers support + # ----------------------------------------------------------------------------------------------------------------- + + def incoming_file_transfer(self, friend_number, file_number, size, file_name): + pass + + def incoming_avatar(self, friend_number, file_number): + pass + def tox_factory(data=None, settings=None): """ From 1a996522f194cc1d391f0a5151fd543cfc00df3a Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Wed, 16 Mar 2016 22:59:15 +0300 Subject: [PATCH 04/32] profile.py updates - avatars and file transfers --- src/callbacks.py | 3 ++- src/mainscreen.py | 9 ++++++++- src/menu.py | 2 +- src/profile.py | 30 +++++++++++++++++++++++++++--- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/callbacks.py b/src/callbacks.py index fb8768f..a80fb5d 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -152,7 +152,8 @@ def tox_file_recv(window, tray): print 'Avatar' invoke_in_main_thread(profile.incoming_avatar, friend_number, - file_number) + file_number, + size) return wrapped # ----------------------------------------------------------------------------------------------------------------- diff --git a/src/mainscreen.py b/src/mainscreen.py index 7c56f2f..93a1c43 100644 --- a/src/mainscreen.py +++ b/src/mainscreen.py @@ -102,6 +102,7 @@ class MainWindow(QtGui.QMainWindow): self.sendMessageButton.setGeometry(QtCore.QRect(550, 10, 60, 110)) self.sendMessageButton.setObjectName("sendMessageButton") self.sendMessageButton.clicked.connect(self.send_message) + self.fileTransferButton.clicked.connect(self.send_file) self.screenshotButton.setText(QtGui.QApplication.translate("Form", "Screenshot", None, QtGui.QApplication.UnicodeUTF8)) self.fileTransferButton.setText(QtGui.QApplication.translate("Form", "File", None, QtGui.QApplication.UnicodeUTF8)) self.sendMessageButton.setText(QtGui.QApplication.translate("Form", "Send", None, QtGui.QApplication.UnicodeUTF8)) @@ -268,13 +269,19 @@ class MainWindow(QtGui.QMainWindow): self.int_s.show() # ----------------------------------------------------------------------------------------------------------------- - # Messages + # Messages and file transfers # ----------------------------------------------------------------------------------------------------------------- def send_message(self): text = self.messageEdit.toPlainText() self.profile.send_message(text) + def send_file(self): + if self.profile.is_active_online(): # active friend exists and online + name = QtGui.QFileDialog.getOpenFileName(self, 'Choose file') + if name: + self.profile.send_file(name) + # ----------------------------------------------------------------------------------------------------------------- # Functions which called when user open context menu in friends list # ----------------------------------------------------------------------------------------------------------------- diff --git a/src/menu.py b/src/menu.py index aa54276..60c92e0 100644 --- a/src/menu.py +++ b/src/menu.py @@ -165,7 +165,7 @@ class ProfileSettings(CenteredWidget): Profile.get_instance().reset_avatar() def set_avatar(self): - name = QtGui.QFileDialog.getOpenFileName(self, 'Open file') + name = QtGui.QFileDialog.getOpenFileName(self, 'Open file', None, 'Image Files (*.png *.jpg *.bmp)') print name if name[0]: with open(name[0], 'rb') as f: diff --git a/src/profile.py b/src/profile.py index a8fcd70..26157fd 100644 --- a/src/profile.py +++ b/src/profile.py @@ -56,7 +56,7 @@ class ProfileHelper(object): @staticmethod def export_profile(new_path): - new_path += ProfileHelper._path.split('/')[-1] + new_path += os.path.basename(ProfileHelper._path) with open(ProfileHelper._path, 'rb') as fin: data = fin.read() with open(new_path, 'wb') as fout: @@ -167,6 +167,14 @@ class Contact(object): f.write(avatar) self.load_avatar() + def get_avatar_hash(self): + avatar_path = (Settings.get_default_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) + if not os.path.isfile(avatar_path): # load default image + avatar_path = curr_directory() + '/images/avatar.png' + with open(avatar_path, 'rb') as fl: + data = fl.read() + return Tox.hash(data) + class Friend(Contact): """ @@ -304,7 +312,7 @@ class Profile(Contact, Singleton): self._screen = screen self._messages = screen.messages self._tox = tox - self._file_transfers = [] # list of file transfers + self._file_transfers = [] # list of file transfers settings = Settings.get_instance() self._show_online = settings['show_online_friends'] screen.online_contacts.setChecked(self._show_online) @@ -696,7 +704,23 @@ class Profile(Contact, Singleton): def incoming_file_transfer(self, friend_number, file_number, size, file_name): pass - def incoming_avatar(self, friend_number, file_number): + def incoming_avatar(self, friend_number, file_number, size): + friend = self.get_friend_by_number(friend_number) + if not size: + friend.reset_avatar() + self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) + else: + hash = friend.get_avatar_hash() + new_avatar_hash = self._tox.file_get_file_id(friend_number, file_number) + if hash == new_avatar_hash: # avatar is the same + self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) # ignore file + else: + pass + + def send_avatar(self, friend_number): + pass + + def send_file(self, path): pass From cc1c4904e20f862c9c22cdf79a0a13c8fd7ce13a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=92=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=B8=D0=BC=D0=B8=D1=80=D0=BE=D0=B2=D0=B8=D1=87?= Date: Wed, 16 Mar 2016 23:11:18 +0300 Subject: [PATCH 05/32] transfers init --- src/file_transfers.py | 44 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/file_transfers.py b/src/file_transfers.py index 9d4995b..486410e 100644 --- a/src/file_transfers.py +++ b/src/file_transfers.py @@ -1,2 +1,46 @@ # TODO: add support of file transfers # TODO: add support of avatars +from toxcore_enums_and_consts import TOX_FILE_KIND +from os.path import basename, getsize + + +class FileTransfer(object): + def __init__(self, path, tox, friend_number): + self._path = path + self._tox = tox + self._friend_number = friend_number + + def set_tox(self, tox): + self._tox = tox + + +class SendTransfer(FileTransfer): + def __init__(self, path, tox, friend_number): + super(self.__class__, self).__init__(path, tox, friend_number) + self._file_number = tox.file_send(friend_number, TOX_FILE_KIND['DATA'], getsize(path), None, basename(path)) + + def get_file_number(self): + return self._file_number + + def send_chunk(self, position, size): + with open(self._path) as f: + f.seek(position) + data = f.read(size) + return self._tox.file_send_chunk(self._friend_number, self._file_number, position, data) + + +class ReceiveTransfer(FileTransfer): + def __init__(self, path, tox, friend_number): + super(self.__class__, self).__init__(path, tox, friend_number) + + def write_chunk(self, position, data): + size = getsize(self._path) + if size < position + len(data): + with open(self._path, 'w') as f: + f.write('\0' * (position + len(data) - size)) + f.seek(position) + f.write(data) + else: + with open(self._path, 'w') as f: + f.seek(position) + f.write(data) From 257271cf939d1c6b576ecf865a8679752255a1b2 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Wed, 16 Mar 2016 23:12:51 +0300 Subject: [PATCH 06/32] bug fixes --- src/menu.py | 2 +- src/profile.py | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/menu.py b/src/menu.py index 60c92e0..ab77c47 100644 --- a/src/menu.py +++ b/src/menu.py @@ -165,7 +165,7 @@ class ProfileSettings(CenteredWidget): Profile.get_instance().reset_avatar() def set_avatar(self): - name = QtGui.QFileDialog.getOpenFileName(self, 'Open file', None, 'Image Files (*.png *.jpg *.bmp)') + name = QtGui.QFileDialog.getOpenFileName(self, 'Open file', None, 'Image Files (*.png)') print name if name[0]: with open(name[0], 'rb') as f: diff --git a/src/profile.py b/src/profile.py index 26157fd..9ef7a44 100644 --- a/src/profile.py +++ b/src/profile.py @@ -170,7 +170,7 @@ class Contact(object): def get_avatar_hash(self): avatar_path = (Settings.get_default_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) if not os.path.isfile(avatar_path): # load default image - avatar_path = curr_directory() + '/images/avatar.png' + return 0 with open(avatar_path, 'rb') as fl: data = fl.read() return Tox.hash(data) @@ -705,6 +705,12 @@ class Profile(Contact, Singleton): pass def incoming_avatar(self, friend_number, file_number, size): + """ + Friend changed avatar + :param friend_number: friend number + :param file_number: file number + :param size: size of avatar or 0 (default avatar) + """ friend = self.get_friend_by_number(friend_number) if not size: friend.reset_avatar() From 24109004cfdb115418bf82759ea0c99caa8b293a Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Wed, 16 Mar 2016 23:56:35 +0300 Subject: [PATCH 07/32] file transfers - profile.py update --- src/callbacks.py | 5 ++++- src/profile.py | 8 +++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/callbacks.py b/src/callbacks.py index a80fb5d..d95622d 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -136,7 +136,7 @@ def tox_file_recv(window, tray): settings = Settings.get_instance() if file_type == TOX_FILE_KIND['DATA']: print 'file' - file_name = file_name[:].decode('utf8') + file_name = file_name[:file_name_size].decode('utf8') invoke_in_main_thread(profile.incoming_file_transfer, friend_number, file_number, @@ -156,6 +156,9 @@ def tox_file_recv(window, tray): size) return wrapped + +def file_recv_chunk(tox, friend_number, file_number, position, chunk, length, user_data): + Profile.get_instance().incoming_chunk(friend_number, file_number, position, chunk, length) # ----------------------------------------------------------------------------------------------------------------- # Callbacks - initialization # ----------------------------------------------------------------------------------------------------------------- diff --git a/src/profile.py b/src/profile.py index 9ef7a44..d5266aa 100644 --- a/src/profile.py +++ b/src/profile.py @@ -8,6 +8,7 @@ from ctypes import * from util import curr_time, log, Singleton, curr_directory, convert_time from tox_dns import tox_dns from history import * +from file_transfers import * import time @@ -696,13 +697,15 @@ class Profile(Contact, Singleton): self.status = None for friend in self._friends: friend.status = None + # TODO: FT reset # ----------------------------------------------------------------------------------------------------------------- # File transfers support # ----------------------------------------------------------------------------------------------------------------- def incoming_file_transfer(self, friend_number, file_number, size, file_name): - pass + rt = ReceiveTransfer(Settings.get_default_path() + file_name, self._tox, friend_number) + self._file_transfers.append(rt) def incoming_avatar(self, friend_number, file_number, size): """ @@ -723,6 +726,9 @@ class Profile(Contact, Singleton): else: pass + def incoming_chunk(self, friend_number, file_number, position, chunk, length): + pass + def send_avatar(self, friend_number): pass From 6025c70f22d42e0ed6b0f49f29b3ca710a35f558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=92=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=B8=D0=BC=D0=B8=D1=80=D0=BE=D0=B2=D0=B8=D1=87?= Date: Thu, 17 Mar 2016 00:07:09 +0300 Subject: [PATCH 08/32] some improvements --- src/file_transfers.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/file_transfers.py b/src/file_transfers.py index 486410e..b52795d 100644 --- a/src/file_transfers.py +++ b/src/file_transfers.py @@ -18,29 +18,30 @@ class SendTransfer(FileTransfer): def __init__(self, path, tox, friend_number): super(self.__class__, self).__init__(path, tox, friend_number) self._file_number = tox.file_send(friend_number, TOX_FILE_KIND['DATA'], getsize(path), None, basename(path)) + self._file = open(path, 'rb') def get_file_number(self): return self._file_number def send_chunk(self, position, size): - with open(self._path) as f: - f.seek(position) - data = f.read(size) + self._file.seek(position) + data = self._file.read(size) return self._tox.file_send_chunk(self._friend_number, self._file_number, position, data) class ReceiveTransfer(FileTransfer): def __init__(self, path, tox, friend_number): super(self.__class__, self).__init__(path, tox, friend_number) + self._file = open(self._path, 'wb') def write_chunk(self, position, data): size = getsize(self._path) if size < position + len(data): - with open(self._path, 'w') as f: - f.write('\0' * (position + len(data) - size)) - f.seek(position) - f.write(data) + self._file.write('\0' * (position + len(data) - size)) + self._file.seek(position) + self._file.write(data) + self._file.flush() else: - with open(self._path, 'w') as f: - f.seek(position) - f.write(data) + self._file.seek(position) + self._file.write(data) + self._file.flush() From 4ce7f6c6a5ecd26ed5544fcc02869f1dafc077ee Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 17 Mar 2016 12:54:05 +0300 Subject: [PATCH 09/32] callback for chunk update --- src/callbacks.py | 3 ++- src/profile.py | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/callbacks.py b/src/callbacks.py index d95622d..efdcc33 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -158,7 +158,7 @@ def tox_file_recv(window, tray): def file_recv_chunk(tox, friend_number, file_number, position, chunk, length, user_data): - Profile.get_instance().incoming_chunk(friend_number, file_number, position, chunk, length) + Profile.get_instance().incoming_chunk(friend_number, file_number, position, chunk if length else None) # ----------------------------------------------------------------------------------------------------------------- # Callbacks - initialization # ----------------------------------------------------------------------------------------------------------------- @@ -181,3 +181,4 @@ def init_callbacks(tox, window, tray): tox.callback_friend_request(friend_request, 0) tox.callback_file_recv(tox_file_recv(window, tray), 0) + tox.callback_file_recv_chunk(file_recv_chunk, 0) diff --git a/src/profile.py b/src/profile.py index d5266aa..5ce5cf2 100644 --- a/src/profile.py +++ b/src/profile.py @@ -706,6 +706,7 @@ class Profile(Contact, Singleton): def incoming_file_transfer(self, friend_number, file_number, size, file_name): rt = ReceiveTransfer(Settings.get_default_path() + file_name, self._tox, friend_number) self._file_transfers.append(rt) + self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) def incoming_avatar(self, friend_number, file_number, size): """ @@ -726,8 +727,10 @@ class Profile(Contact, Singleton): else: pass - def incoming_chunk(self, friend_number, file_number, position, chunk, length): - pass + def incoming_chunk(self, friend_number, file_number, position, data): + # TODO: better transfer filtering + transfer = filter(lambda x: x._friend_number == friend_number, self._file_transfers)[0] + transfer.write_chunk(position, data) def send_avatar(self, friend_number): pass From 90cabe1a4585eb1d3da809c3bccf29d46bcd905f Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 17 Mar 2016 18:55:18 +0300 Subject: [PATCH 10/32] receiving (test mode) --- src/callbacks.py | 7 ++++++- src/file_transfers.py | 14 +++++++------- src/tox.py | 4 ++-- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/callbacks.py b/src/callbacks.py index efdcc33..c1ca6e4 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -4,6 +4,7 @@ from settings import Settings from profile import Profile from toxcore_enums_and_consts import * from tox import bin_to_string +from ctypes import c_char_p, cast, pointer class InvokeEvent(QtCore.QEvent): @@ -158,7 +159,11 @@ def tox_file_recv(window, tray): def file_recv_chunk(tox, friend_number, file_number, position, chunk, length, user_data): - Profile.get_instance().incoming_chunk(friend_number, file_number, position, chunk if length else None) + print 'Len:', type(length) + print 'Chunk', type(chunk) + print 'Pos', type(position) + #chunk = cast(pointer(chunk), c_char_p)[0] + Profile.get_instance().incoming_chunk(friend_number, file_number, position, chunk[:length] if length else None) # ----------------------------------------------------------------------------------------------------------------- # Callbacks - initialization # ----------------------------------------------------------------------------------------------------------------- diff --git a/src/file_transfers.py b/src/file_transfers.py index b52795d..7352140 100644 --- a/src/file_transfers.py +++ b/src/file_transfers.py @@ -35,13 +35,13 @@ class ReceiveTransfer(FileTransfer): self._file = open(self._path, 'wb') def write_chunk(self, position, data): - size = getsize(self._path) - if size < position + len(data): - self._file.write('\0' * (position + len(data) - size)) + if data is not None: + size = getsize(self._path) + if size < position: + self._file.seek(0, 2) + self._file.write('\0' * (position - size)) self._file.seek(position) - self._file.write(data) + self._file.write(''.join(chr(x) for x in data)) self._file.flush() else: - self._file.seek(position) - self._file.write(data) - self._file.flush() + self._file.close() diff --git a/src/tox.py b/src/tox.py index 3ec7bed..2767660 100644 --- a/src/tox.py +++ b/src/tox.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from ctypes import c_char_p, Structure, CDLL, c_bool, addressof, c_int, c_size_t, POINTER, c_uint16, c_void_p, c_uint64 -from ctypes import create_string_buffer, ArgumentError, CFUNCTYPE, c_uint32, sizeof +from ctypes import create_string_buffer, ArgumentError, CFUNCTYPE, c_uint32, sizeof, c_uint8 from platform import system from toxcore_enums_and_consts import * @@ -1351,7 +1351,7 @@ class Tox(object): pointer (c_void_p) to user_data :param user_data: pointer (c_void_p) to user data """ - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint64, c_char_p, c_size_t, c_void_p) + c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint64, POINTER(c_uint8), c_size_t, c_void_p) self.file_recv_chunk_cb = c_callback(callback) self.libtoxcore.tox_callback_file_recv_chunk(self._tox_pointer, self.file_recv_chunk_cb, user_data) From 33290216df16ea342f330e6669975d9a79bb7f7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=92=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=B8=D0=BC=D0=B8=D1=80=D0=BE=D0=B2=D0=B8=D1=87?= Date: Thu, 17 Mar 2016 19:28:52 +0300 Subject: [PATCH 11/32] bug fix --- src/callbacks.py | 2 +- src/file_transfers.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/callbacks.py b/src/callbacks.py index c1ca6e4..683f73d 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -137,7 +137,7 @@ def tox_file_recv(window, tray): settings = Settings.get_instance() if file_type == TOX_FILE_KIND['DATA']: print 'file' - file_name = file_name[:file_name_size].decode('utf8') + file_name = file_name[:file_name_size] invoke_in_main_thread(profile.incoming_file_transfer, friend_number, file_number, diff --git a/src/file_transfers.py b/src/file_transfers.py index 7352140..92a9657 100644 --- a/src/file_transfers.py +++ b/src/file_transfers.py @@ -33,6 +33,7 @@ class ReceiveTransfer(FileTransfer): def __init__(self, path, tox, friend_number): super(self.__class__, self).__init__(path, tox, friend_number) self._file = open(self._path, 'wb') + self._file.truncate(0) def write_chunk(self, position, data): if data is not None: From 9768cfdd1ed3fb54e556fe07cb9ba9d369690711 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 17 Mar 2016 20:36:34 +0300 Subject: [PATCH 12/32] avatars of friends support --- src/callbacks.py | 10 +++++----- src/profile.py | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/callbacks.py b/src/callbacks.py index 683f73d..548a5b4 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -159,11 +159,11 @@ def tox_file_recv(window, tray): def file_recv_chunk(tox, friend_number, file_number, position, chunk, length, user_data): - print 'Len:', type(length) - print 'Chunk', type(chunk) - print 'Pos', type(position) - #chunk = cast(pointer(chunk), c_char_p)[0] - Profile.get_instance().incoming_chunk(friend_number, file_number, position, chunk[:length] if length else None) + invoke_in_main_thread(Profile.get_instance().incoming_chunk, + friend_number, + file_number, + position, + chunk[:length] if length else None) # ----------------------------------------------------------------------------------------------------------------- # Callbacks - initialization # ----------------------------------------------------------------------------------------------------------------- diff --git a/src/profile.py b/src/profile.py index 5ce5cf2..9fc3720 100644 --- a/src/profile.py +++ b/src/profile.py @@ -313,7 +313,7 @@ class Profile(Contact, Singleton): self._screen = screen self._messages = screen.messages self._tox = tox - self._file_transfers = [] # list of file transfers + self._file_transfers = {} # dict of file transfers settings = Settings.get_instance() self._show_online = settings['show_online_friends'] screen.online_contacts.setChecked(self._show_online) @@ -705,7 +705,7 @@ class Profile(Contact, Singleton): def incoming_file_transfer(self, friend_number, file_number, size, file_name): rt = ReceiveTransfer(Settings.get_default_path() + file_name, self._tox, friend_number) - self._file_transfers.append(rt) + self._file_transfers[(friend_number, file_number)] = rt self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) def incoming_avatar(self, friend_number, file_number, size): @@ -724,13 +724,19 @@ class Profile(Contact, Singleton): new_avatar_hash = self._tox.file_get_file_id(friend_number, file_number) if hash == new_avatar_hash: # avatar is the same self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) # ignore file - else: - pass + else: # get new avatar + path = ProfileHelper.get_path() + '/avatars/{}.png'.format(friend.tox_id) + rt = ReceiveTransfer(path, self._tox, friend_number) + self._file_transfers[(friend_number, file_number)] = rt + self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) def incoming_chunk(self, friend_number, file_number, position, data): - # TODO: better transfer filtering - transfer = filter(lambda x: x._friend_number == friend_number, self._file_transfers)[0] + transfer = self._file_transfers[(friend_number, file_number)] transfer.write_chunk(position, data) + if data is None: + del self._file_transfers[(friend_number, file_number)] + friend = self.get_friend_by_number(friend_number) + friend.load_avatar() def send_avatar(self, friend_number): pass From 498545c3bfbc85c7ae799c670553ee3feb18f7f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=92=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=B8=D0=BC=D0=B8=D1=80=D0=BE=D0=B2=D0=B8=D1=87?= Date: Thu, 17 Mar 2016 20:46:18 +0300 Subject: [PATCH 13/32] upd transfers --- src/file_transfers.py | 47 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/src/file_transfers.py b/src/file_transfers.py index 92a9657..9f4cd42 100644 --- a/src/file_transfers.py +++ b/src/file_transfers.py @@ -4,15 +4,42 @@ from toxcore_enums_and_consts import TOX_FILE_KIND from os.path import basename, getsize +TOX_FILE_TRANSFER_STATE = { + 'RUNNING': 0, + 'PAUSED': 1, + 'CANCELED': 2, + 'FINISHED': 3, +} + + class FileTransfer(object): - def __init__(self, path, tox, friend_number): + def __init__(self, path, tox, friend_number, file_number = None): self._path = path self._tox = tox self._friend_number = friend_number + self.state = TOX_FILE_TRANSFER_STATE['RUNNING'] + self._file_number = file_number def set_tox(self, tox): self._tox = tox + def get_file_number(self): + return self._file_number + + def get_friend_number(self): + return self._friend_number + + def send_control(self, control): + if self._tox.file_control(self._friend_number, self._file_number, control): + self.state = control + + def get_file_id(self): + return self._tox.file_get_file_id(self._friend_number, self._file_number) + + def file_seek(self): + # TODO implement + pass + class SendTransfer(FileTransfer): def __init__(self, path, tox, friend_number): @@ -20,9 +47,6 @@ class SendTransfer(FileTransfer): self._file_number = tox.file_send(friend_number, TOX_FILE_KIND['DATA'], getsize(path), None, basename(path)) self._file = open(path, 'rb') - def get_file_number(self): - return self._file_number - def send_chunk(self, position, size): self._file.seek(position) data = self._file.read(size) @@ -30,19 +54,22 @@ class SendTransfer(FileTransfer): class ReceiveTransfer(FileTransfer): - def __init__(self, path, tox, friend_number): - super(self.__class__, self).__init__(path, tox, friend_number) + def __init__(self, path, tox, friend_number, file_number): + super(self.__class__, self).__init__(path, tox, friend_number, file_number) self._file = open(self._path, 'wb') self._file.truncate(0) + self._size = 0 def write_chunk(self, position, data): if data is not None: - size = getsize(self._path) - if size < position: + data = ''.join(chr(x) for x in data) + if self._size < position: self._file.seek(0, 2) - self._file.write('\0' * (position - size)) + self._file.write('\0' * (position - self._size)) self._file.seek(position) - self._file.write(''.join(chr(x) for x in data)) + self._file.write(data) self._file.flush() + if position + len(data) > self._size: + self._size = position + len(data) else: self._file.close() From 1bb145728f1167c5c7d0f28c5488b19d2b481d51 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 17 Mar 2016 23:49:27 +0300 Subject: [PATCH 14/32] sending updates --- src/callbacks.py | 15 +++++++++++++++ src/mainscreen.py | 2 +- src/profile.py | 12 +++++++++--- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/callbacks.py b/src/callbacks.py index 548a5b4..df63bde 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -164,6 +164,19 @@ def file_recv_chunk(tox, friend_number, file_number, position, chunk, length, us file_number, position, chunk[:length] if length else None) + + +def file_chunk_request(tox, friend_number, file_number, position, size, user_data): + invoke_in_main_thread(Profile.get_instance().outgoing_chunck, + friend_number, + file_number, + position, + size) + + +def file_recv_control(tox, friend_number, file_number, file_control, user_data): + pass + # ----------------------------------------------------------------------------------------------------------------- # Callbacks - initialization # ----------------------------------------------------------------------------------------------------------------- @@ -187,3 +200,5 @@ def init_callbacks(tox, window, tray): tox.callback_file_recv(tox_file_recv(window, tray), 0) tox.callback_file_recv_chunk(file_recv_chunk, 0) + tox.callback_file_chunk_request(file_chunk_request, 0) + tox.callback_file_recv_control(file_recv_control, 0) diff --git a/src/mainscreen.py b/src/mainscreen.py index 93a1c43..be1f550 100644 --- a/src/mainscreen.py +++ b/src/mainscreen.py @@ -280,7 +280,7 @@ class MainWindow(QtGui.QMainWindow): if self.profile.is_active_online(): # active friend exists and online name = QtGui.QFileDialog.getOpenFileName(self, 'Choose file') if name: - self.profile.send_file(name) + self.profile.send_file(name[0]) # ----------------------------------------------------------------------------------------------------------------- # Functions which called when user open context menu in friends list diff --git a/src/profile.py b/src/profile.py index 9fc3720..cc6966b 100644 --- a/src/profile.py +++ b/src/profile.py @@ -704,7 +704,7 @@ class Profile(Contact, Singleton): # ----------------------------------------------------------------------------------------------------------------- def incoming_file_transfer(self, friend_number, file_number, size, file_name): - rt = ReceiveTransfer(Settings.get_default_path() + file_name, self._tox, friend_number) + rt = ReceiveTransfer(Settings.get_default_path() + file_name, self._tox, friend_number, file_number) self._file_transfers[(friend_number, file_number)] = rt self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) @@ -726,7 +726,7 @@ class Profile(Contact, Singleton): self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) # ignore file else: # get new avatar path = ProfileHelper.get_path() + '/avatars/{}.png'.format(friend.tox_id) - rt = ReceiveTransfer(path, self._tox, friend_number) + rt = ReceiveTransfer(path, self._tox, friend_number, file_number) self._file_transfers[(friend_number, file_number)] = rt self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) @@ -742,7 +742,13 @@ class Profile(Contact, Singleton): pass def send_file(self, path): - pass + friend_number = self.get_active_number() + st = SendTransfer(path, self._tox, friend_number) + self._file_transfers[(friend_number, st.get_file_number())] = st + + def outgoing_chunk(self, friend_number, file_number, position, size): + transfer = self._file_transfers[(friend_number, file_number)] + transfer.send_chunk(position, size) def tox_factory(data=None, settings=None): From 42703c9bc25c8e47ff26d98fbae5e737e7bd66d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=92=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=B8=D0=BC=D0=B8=D1=80=D0=BE=D0=B2=D0=B8=D1=87?= Date: Thu, 17 Mar 2016 23:51:15 +0300 Subject: [PATCH 15/32] hotfix --- src/file_transfers.py | 4 +++- src/tox.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/file_transfers.py b/src/file_transfers.py index 9f4cd42..b983d81 100644 --- a/src/file_transfers.py +++ b/src/file_transfers.py @@ -2,6 +2,7 @@ # TODO: add support of avatars from toxcore_enums_and_consts import TOX_FILE_KIND from os.path import basename, getsize +from time import time TOX_FILE_TRANSFER_STATE = { @@ -13,12 +14,13 @@ TOX_FILE_TRANSFER_STATE = { class FileTransfer(object): - def __init__(self, path, tox, friend_number, file_number = None): + def __init__(self, path, tox, friend_number, file_number=None): self._path = path self._tox = tox self._friend_number = friend_number self.state = TOX_FILE_TRANSFER_STATE['RUNNING'] self._file_number = file_number + self._creation_time = time() def set_tox(self, tox): self._tox = tox diff --git a/src/tox.py b/src/tox.py index 2767660..b1fb202 100644 --- a/src/tox.py +++ b/src/tox.py @@ -36,7 +36,7 @@ class LibToxCore(object): def string_to_bin(tox_id): - return c_char_p(tox_id.decode('hex')) + return c_char_p(tox_id.decode('hex')) if tox_id is not None else None def bin_to_string(raw_id, length): From 6d56b2aa5b9e7bfcc79adb546dab327dc2461195 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Thu, 17 Mar 2016 23:58:38 +0300 Subject: [PATCH 16/32] send fix --- src/callbacks.py | 2 +- src/mainscreen.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/callbacks.py b/src/callbacks.py index df63bde..7c7cacd 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -167,7 +167,7 @@ def file_recv_chunk(tox, friend_number, file_number, position, chunk, length, us def file_chunk_request(tox, friend_number, file_number, position, size, user_data): - invoke_in_main_thread(Profile.get_instance().outgoing_chunck, + invoke_in_main_thread(Profile.get_instance().outgoing_chunk, friend_number, file_number, position, diff --git a/src/mainscreen.py b/src/mainscreen.py index be1f550..7beb48d 100644 --- a/src/mainscreen.py +++ b/src/mainscreen.py @@ -279,7 +279,7 @@ class MainWindow(QtGui.QMainWindow): def send_file(self): if self.profile.is_active_online(): # active friend exists and online name = QtGui.QFileDialog.getOpenFileName(self, 'Choose file') - if name: + if name[0]: self.profile.send_file(name[0]) # ----------------------------------------------------------------------------------------------------------------- From 9d5353640bc875ee029d1166852f4bb252881b9a Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 18 Mar 2016 16:20:07 +0300 Subject: [PATCH 17/32] avatars support --- src/callbacks.py | 2 ++ src/file_transfers.py | 55 +++++++++++++++++++++++++++++++++++++------ src/profile.py | 37 +++++++++++++---------------- 3 files changed, 66 insertions(+), 28 deletions(-) diff --git a/src/callbacks.py b/src/callbacks.py index 7c7cacd..496b72d 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -74,6 +74,8 @@ def friend_connection_status(tox, friend_num, new_status, user_data): friend = profile.get_friend_by_number(friend_num) if new_status == TOX_CONNECTION['NONE']: invoke_in_main_thread(friend.set_status, None) + elif friend.status is None: + invoke_in_main_thread(profile.send_avatar, friend_num) invoke_in_main_thread(profile.update_filtration) diff --git a/src/file_transfers.py b/src/file_transfers.py index b983d81..3cb0dce 100644 --- a/src/file_transfers.py +++ b/src/file_transfers.py @@ -1,8 +1,11 @@ # TODO: add support of file transfers # TODO: add support of avatars -from toxcore_enums_and_consts import TOX_FILE_KIND -from os.path import basename, getsize +from toxcore_enums_and_consts import TOX_FILE_KIND, TOX_FILE_CONTROL +from os.path import basename, getsize, exists +from os import remove from time import time +from tox import Tox +import profile TOX_FILE_TRANSFER_STATE = { @@ -44,10 +47,15 @@ class FileTransfer(object): class SendTransfer(FileTransfer): - def __init__(self, path, tox, friend_number): - super(self.__class__, self).__init__(path, tox, friend_number) - self._file_number = tox.file_send(friend_number, TOX_FILE_KIND['DATA'], getsize(path), None, basename(path)) - self._file = open(path, 'rb') + def __init__(self, path, tox, friend_number, kind=TOX_FILE_KIND['DATA'], file_id=None): + super(SendTransfer, self).__init__(path, tox, friend_number) + self._file_number = tox.file_send(friend_number, + kind, + getsize(path) if path else 0, + file_id, + basename(path) if path else '') + if path is not None: + self._file = open(path, 'rb') def send_chunk(self, position, size): self._file.seek(position) @@ -55,9 +63,19 @@ class SendTransfer(FileTransfer): return self._tox.file_send_chunk(self._friend_number, self._file_number, position, data) +class SendAvatar(SendTransfer): + def __init__(self, path, tox, friend_number): + if path is None: + super(SendAvatar, self).__init__(path, tox, friend_number, TOX_FILE_KIND['AVATAR']) + else: + with open(path, 'rb') as fl: + hash = Tox.hash(fl.read()) + super(self.__class__, self).__init__(path, tox, friend_number, TOX_FILE_KIND['AVATAR'], hash) + + class ReceiveTransfer(FileTransfer): def __init__(self, path, tox, friend_number, file_number): - super(self.__class__, self).__init__(path, tox, friend_number, file_number) + super(ReceiveTransfer, self).__init__(path, tox, friend_number, file_number) self._file = open(self._path, 'wb') self._file.truncate(0) self._size = 0 @@ -75,3 +93,26 @@ class ReceiveTransfer(FileTransfer): self._size = position + len(data) else: self._file.close() + self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] + + +class ReceiveAvatar(ReceiveTransfer): + def __init__(self, tox, friend_number, file_number, has_size=True): + path = profile.ProfileHelper.get_path() + '/avatars/{}.png'.format(tox.friend_get_public_key(friend_number)) + super(ReceiveAvatar, self).__init__(path, tox, friend_number, file_number) + if exists(path): + if not has_size: + remove(path) + self.send_control(TOX_FILE_CONTROL['CANCEL']) + self.state = TOX_FILE_TRANSFER_STATE['CANCELED'] + else: + hash = self.get_file_id() + with open(path, 'rb') as fl: + existing_hash = Tox.hash(fl.read()) + if hash == existing_hash: + self.send_control(TOX_FILE_CONTROL['CANCEL']) + self.state = TOX_FILE_TRANSFER_STATE['CANCELED'] + else: + self.send_control(TOX_FILE_CONTROL['RESUME']) + else: + self.send_control(TOX_FILE_CONTROL['RESUME']) diff --git a/src/profile.py b/src/profile.py index cc6966b..98d6a15 100644 --- a/src/profile.py +++ b/src/profile.py @@ -148,7 +148,7 @@ class Contact(object): """ Tries to load avatar of contact or uses default avatar """ - avatar_path = (Settings.get_default_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) + avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) if not os.path.isfile(avatar_path): # load default image avatar_path = curr_directory() + '/images/avatar.png' pixmap = QtGui.QPixmap(QtCore.QSize(64, 64)) @@ -157,19 +157,19 @@ class Contact(object): self._widget.avatar_label.repaint() def reset_avatar(self): - avatar_path = (Settings.get_default_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) + avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) if os.path.isfile(avatar_path): os.remove(avatar_path) self.load_avatar() def set_avatar(self, avatar): - avatar_path = (Settings.get_default_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) + avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) with open(avatar_path, 'wb') as f: f.write(avatar) self.load_avatar() def get_avatar_hash(self): - avatar_path = (Settings.get_default_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) + avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) if not os.path.isfile(avatar_path): # load default image return 0 with open(avatar_path, 'rb') as fl: @@ -715,31 +715,26 @@ class Profile(Contact, Singleton): :param file_number: file number :param size: size of avatar or 0 (default avatar) """ - friend = self.get_friend_by_number(friend_number) - if not size: - friend.reset_avatar() - self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) + ra = ReceiveAvatar(self._tox, friend_number, file_number, size) + if ra.state != TOX_FILE_TRANSFER_STATE['CANCELED']: + self._file_transfers[(friend_number, file_number)] = ra else: - hash = friend.get_avatar_hash() - new_avatar_hash = self._tox.file_get_file_id(friend_number, file_number) - if hash == new_avatar_hash: # avatar is the same - self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) # ignore file - else: # get new avatar - path = ProfileHelper.get_path() + '/avatars/{}.png'.format(friend.tox_id) - rt = ReceiveTransfer(path, self._tox, friend_number, file_number) - self._file_transfers[(friend_number, file_number)] = rt - self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) + self.get_friend_by_number(friend_number).load_avatar() def incoming_chunk(self, friend_number, file_number, position, data): transfer = self._file_transfers[(friend_number, file_number)] transfer.write_chunk(position, data) - if data is None: + if transfer.state: + if type(transfer) is ReceiveAvatar: + self.get_friend_by_number(friend_number).load_avatar() del self._file_transfers[(friend_number, file_number)] - friend = self.get_friend_by_number(friend_number) - friend.load_avatar() def send_avatar(self, friend_number): - pass + avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) + if not os.path.isfile(avatar_path): # reset image + avatar_path = None + sa = SendAvatar(avatar_path, self._tox, friend_number) + self._file_transfers[(friend_number, sa.get_file_number())] = sa def send_file(self, path): friend_number = self.get_active_number() From 976a6c139dddeea9c1f5d164a2d7102036d08afc Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 18 Mar 2016 16:50:32 +0300 Subject: [PATCH 18/32] sending bug fix --- src/file_transfers.py | 12 +++++++----- src/notifications.py | 4 +++- src/profile.py | 15 +++++++++------ 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/file_transfers.py b/src/file_transfers.py index 3cb0dce..ec00773 100644 --- a/src/file_transfers.py +++ b/src/file_transfers.py @@ -1,5 +1,3 @@ -# TODO: add support of file transfers -# TODO: add support of avatars from toxcore_enums_and_consts import TOX_FILE_KIND, TOX_FILE_CONTROL from os.path import basename, getsize, exists from os import remove @@ -58,9 +56,13 @@ class SendTransfer(FileTransfer): self._file = open(path, 'rb') def send_chunk(self, position, size): - self._file.seek(position) - data = self._file.read(size) - return self._tox.file_send_chunk(self._friend_number, self._file_number, position, data) + if size: + self._file.seek(position) + data = self._file.read(size) + return self._tox.file_send_chunk(self._friend_number, self._file_number, position, data) + else: + self._file.close() + self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] class SendAvatar(SendTransfer): diff --git a/src/notifications.py b/src/notifications.py index d090afd..b736f0e 100644 --- a/src/notifications.py +++ b/src/notifications.py @@ -2,6 +2,7 @@ from PySide import QtGui from PySide.phonon import Phonon from util import curr_directory # TODO: make app icon active +# TODO: add all sound notifications SOUND_NOTIFICATION = { @@ -19,9 +20,10 @@ def tray_notification(title, text, tray): def sound_notification(t): - # TODO: add other sound notifications if t == SOUND_NOTIFICATION['MESSAGE']: f = curr_directory() + '/sounds/message.wav' + elif t == SOUND_NOTIFICATION['FILE_TRANSFER']: + f = curr_directory() + '/sounds/file.wav' else: return m = Phonon.MediaSource(f) diff --git a/src/profile.py b/src/profile.py index 98d6a15..70f591e 100644 --- a/src/profile.py +++ b/src/profile.py @@ -313,7 +313,7 @@ class Profile(Contact, Singleton): self._screen = screen self._messages = screen.messages self._tox = tox - self._file_transfers = {} # dict of file transfers + self._file_transfers = {} # dict of file transfers. key - tuple (friend_number, file_number) settings = Settings.get_instance() self._show_online = settings['show_online_friends'] screen.online_contacts.setChecked(self._show_online) @@ -533,11 +533,12 @@ class Profile(Contact, Singleton): Save history to db """ print 'In save' - if Settings.get_instance()['save_history']: - for friend in self._friends: - messages = friend.get_corr_for_saving() - self._history.save_messages_to_db(friend.tox_id, messages) - del self._history + if hasattr(self, '_history'): + if Settings.get_instance()['save_history']: + for friend in self._friends: + messages = friend.get_corr_for_saving() + self._history.save_messages_to_db(friend.tox_id, messages) + del self._history def clear_history(self, num=None): if num is not None: @@ -744,6 +745,8 @@ class Profile(Contact, Singleton): def outgoing_chunk(self, friend_number, file_number, position, size): transfer = self._file_transfers[(friend_number, file_number)] transfer.send_chunk(position, size) + if transfer.state: + del self._file_transfers[(friend_number, file_number)] def tox_factory(data=None, settings=None): From ff8aeef8afe004a2744e2d610c1dfd754cbad0e7 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Fri, 18 Mar 2016 19:33:54 +0300 Subject: [PATCH 19/32] autoaccept of files added --- src/mainscreen.py | 37 +++++++++++++++++++++++++------------ src/menu.py | 35 +++++++++++++++++++++++++---------- src/profile.py | 23 ++++++++++++++++++++--- src/settings.py | 1 + 4 files changed, 71 insertions(+), 25 deletions(-) diff --git a/src/mainscreen.py b/src/mainscreen.py index 7beb48d..9eda4ec 100644 --- a/src/mainscreen.py +++ b/src/mainscreen.py @@ -288,38 +288,51 @@ class MainWindow(QtGui.QMainWindow): def friend_right_click(self, pos): item = self.friends_list.itemAt(pos) + num = self.friends_list.indexFromItem(item).row() + friend = Profile.get_instance().get_friend_by_number(num) + settings = Settings.get_instance() + allowed = friend.tox_id in settings['auto_accept_from_friends'] + auto = 'Disallow auto accept' if allowed else 'Allow auto accept' if item is not None: self.listMenu = QtGui.QMenu() set_alias_item = self.listMenu.addAction('Set alias') clear_history_item = self.listMenu.addAction('Clear history') copy_key_item = self.listMenu.addAction('Copy public key') + auto_accept_item = self.listMenu.addAction(auto) remove_item = self.listMenu.addAction('Remove friend') - self.connect(set_alias_item, QtCore.SIGNAL("triggered()"), lambda: self.set_alias(item)) - self.connect(remove_item, QtCore.SIGNAL("triggered()"), lambda: self.remove_friend(item)) - self.connect(copy_key_item, QtCore.SIGNAL("triggered()"), lambda: self.copy_friend_key(item)) - self.connect(clear_history_item, QtCore.SIGNAL("triggered()"), lambda: self.clear_history(item)) + self.connect(set_alias_item, QtCore.SIGNAL("triggered()"), lambda: self.set_alias(num)) + self.connect(remove_item, QtCore.SIGNAL("triggered()"), lambda: self.remove_friend(num)) + self.connect(copy_key_item, QtCore.SIGNAL("triggered()"), lambda: self.copy_friend_key(num)) + self.connect(clear_history_item, QtCore.SIGNAL("triggered()"), lambda: self.clear_history(num)) + self.connect(auto_accept_item, QtCore.SIGNAL("triggered()"), lambda: self.auto_accept(num, not allowed)) parent_position = self.friends_list.mapToGlobal(QtCore.QPoint(0, 0)) self.listMenu.move(parent_position + pos) self.listMenu.show() - def set_alias(self, item): - num = self.friends_list.indexFromItem(item).row() + def set_alias(self, num): self.profile.set_alias(num) - def remove_friend(self, item): - num = self.friends_list.indexFromItem(item).row() + def remove_friend(self, num): self.profile.delete_friend(num) - def copy_friend_key(self, item): - num = self.friends_list.indexFromItem(item).row() + def copy_friend_key(self, num): tox_id = self.profile.friend_public_key(num) clipboard = QtGui.QApplication.clipboard() clipboard.setText(tox_id) - def clear_history(self, item): - num = self.friends_list.indexFromItem(item).row() + def clear_history(self, num): self.profile.clear_history(num) + def auto_accept(self, num, value): + settings = Settings.get_instance() + tox_id = self.profile.friend_public_key(num) + if value: + settings['auto_accept_from_friends'].append(tox_id) + else: + index = settings['auto_accept_from_friends'].index(tox_id) + del settings['auto_accept_from_friends'][index] + settings.save() + # ----------------------------------------------------------------------------------------------------------------- # Functions which called when user click somewhere else # ----------------------------------------------------------------------------------------------------------------- diff --git a/src/menu.py b/src/menu.py index ab77c47..71cc8a5 100644 --- a/src/menu.py +++ b/src/menu.py @@ -1,7 +1,7 @@ from PySide import QtCore, QtGui from settings import Settings from profile import Profile, ProfileHelper -from util import get_style +from util import get_style, curr_directory class CenteredWidget(QtGui.QWidget): @@ -174,11 +174,12 @@ class ProfileSettings(CenteredWidget): def export_profile(self): directory = QtGui.QFileDialog.getExistingDirectory() + '/' - ProfileHelper.export_profile(directory) - settings = Settings.get_instance() - settings.export(directory) - profile = Profile.get_instance() - profile.export_history(directory) + if directory != '/': + ProfileHelper.export_profile(directory) + settings = Settings.get_instance() + settings.export(directory) + profile = Profile.get_instance() + profile.export_history(directory) def closeEvent(self, event): profile = Profile.get_instance() @@ -275,22 +276,30 @@ class PrivacySettings(CenteredWidget): self.fileautoaccept.setGeometry(QtCore.QRect(40, 60, 271, 22)) self.fileautoaccept.setObjectName("fileautoaccept") self.typingNotifications = QtGui.QCheckBox(self) - self.typingNotifications.setGeometry(QtCore.QRect(40, 90, 350, 31)) - self.typingNotifications.setBaseSize(QtCore.QSize(350, 200)) + self.typingNotifications.setGeometry(QtCore.QRect(40, 90, 350, 30)) self.typingNotifications.setObjectName("typingNotifications") - + self.auto_path = QtGui.QLabel(self) + self.auto_path.setGeometry(QtCore.QRect(40, 120, 350, 30)) + self.path = QtGui.QPlainTextEdit(self) + self.path.setGeometry(QtCore.QRect(10, 160, 330, 30)) + self.change_path = QtGui.QPushButton(self) + self.change_path.setGeometry(QtCore.QRect(230, 120, 100, 30)) self.retranslateUi() settings = Settings.get_instance() self.typingNotifications.setChecked(settings['typing_notifications']) self.fileautoaccept.setChecked(settings['allow_auto_accept']) self.saveHistory.setChecked(settings['save_history']) + self.path.setPlainText(settings['auto_accept_path'] or curr_directory()) + self.change_path.clicked.connect(self.new_path) QtCore.QMetaObject.connectSlotsByName(self) def retranslateUi(self): self.setWindowTitle(QtGui.QApplication.translate("privacySettings", "Privacy settings", None, QtGui.QApplication.UnicodeUTF8)) self.saveHistory.setText(QtGui.QApplication.translate("privacySettings", "Save chat history", None, QtGui.QApplication.UnicodeUTF8)) - self.fileautoaccept.setText(QtGui.QApplication.translate("privacySettings", "Allow file autoaccept", None, QtGui.QApplication.UnicodeUTF8)) + self.fileautoaccept.setText(QtGui.QApplication.translate("privacySettings", "Allow file auto accept", None, QtGui.QApplication.UnicodeUTF8)) self.typingNotifications.setText(QtGui.QApplication.translate("privacySettings", "Send typing notifications", None, QtGui.QApplication.UnicodeUTF8)) + self.auto_path.setText(QtGui.QApplication.translate("privacySettings", "Auto accept default path:", None, QtGui.QApplication.UnicodeUTF8)) + self.change_path.setText(QtGui.QApplication.translate("privacySettings", "Change", None, QtGui.QApplication.UnicodeUTF8)) def closeEvent(self, event): settings = Settings.get_instance() @@ -299,8 +308,14 @@ class PrivacySettings(CenteredWidget): if settings['save_history'] and not self.saveHistory.isChecked(): # clear history Profile.get_instance().clear_history() settings['save_history'] = self.saveHistory.isChecked() + settings['auto_accept_path'] = self.path.toPlainText() settings.save() + def new_path(self): + directory = QtGui.QFileDialog.getExistingDirectory() + '/' + if directory != '/': + self.path.setPlainText(directory) + class NotificationsSettings(CenteredWidget): """Notifications settings form""" diff --git a/src/profile.py b/src/profile.py index 70f591e..d7b45dc 100644 --- a/src/profile.py +++ b/src/profile.py @@ -705,9 +705,16 @@ class Profile(Contact, Singleton): # ----------------------------------------------------------------------------------------------------------------- def incoming_file_transfer(self, friend_number, file_number, size, file_name): - rt = ReceiveTransfer(Settings.get_default_path() + file_name, self._tox, friend_number, file_number) - self._file_transfers[(friend_number, file_number)] = rt - self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) + settings = Settings.get_instance() + friend = self.get_friend_by_number(friend_number) + if settings['allow_auto_accept'] and friend.tox_id in settings['auto_accept_from_friends']: + path = settings['auto_accept_path'] or curr_directory() + rt = ReceiveTransfer(path + '/' + file_name, self._tox, friend_number, file_number) + self._file_transfers[(friend_number, file_number)] = rt + self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) + else: + self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) + # TODO: show info about incoming transfer def incoming_avatar(self, friend_number, file_number, size): """ @@ -748,6 +755,16 @@ class Profile(Contact, Singleton): if transfer.state: del self._file_transfers[(friend_number, file_number)] + def reset_avatar(self): + super(Profile, self).reset_avatar() + for friend in filter(lambda x: x.status is not None, self._friends): + self.send_avatar(friend.number) + + def set_avatar(self, data): + super(Profile, self).set_avatar(data) + for friend in filter(lambda x: x.status is not None, self._friends): + self.send_avatar(friend.number) + def tox_factory(data=None, settings=None): """ diff --git a/src/settings.py b/src/settings.py index 651b692..9308e08 100644 --- a/src/settings.py +++ b/src/settings.py @@ -53,6 +53,7 @@ class Settings(Singleton, dict): 'save_history': False, 'allow_inline': True, 'allow_auto_accept': False, + 'auto_accept_path': None, 'show_online_friends': False, 'auto_accept_from_friends': [], 'friends_aliases': [], From 52bfacb97832e2b1628c125028f751e1b288051b Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 19 Mar 2016 00:28:53 +0300 Subject: [PATCH 20/32] file transfer item #1 --- src/list_items.py | 36 ++++++++++++++++++++++++++++++++++++ src/profile.py | 18 +++++++++++++++--- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/list_items.py b/src/list_items.py index 79529cf..5072484 100644 --- a/src/list_items.py +++ b/src/list_items.py @@ -154,3 +154,39 @@ class StatusCircle(QtGui.QWidget): paint.setPen(color) paint.drawEllipse(center, rad_x + 3, rad_y + 3) paint.end() + + +class FileTransferItem(QtGui.QListWidget): + def __init__(self, file_name, time, user, friend_number, file_number, parent=None): + QtGui.QListWidget.__init__(self, parent) + self.name = QtGui.QLabel(self) + self.name.setGeometry(QtCore.QRect(0, 2, 95, 20)) + self.name.setTextFormat(QtCore.Qt.PlainText) + font = QtGui.QFont() + font.setFamily("Times New Roman") + font.setPointSize(11) + font.setBold(True) + self.name.setFont(font) + self.name.setObjectName("name") + self.name.setText(file_name) + + self.time = QtGui.QLabel(self) + self.time.setGeometry(QtCore.QRect(500, 0, 50, 25)) + font = QtGui.QFont() + font.setFamily("Times New Roman") + font.setPointSize(10) + font.setBold(False) + self.time.setFont(font) + self.time.setObjectName("time") + self.time.setText(time) + + self.cancel = QtGui.QPushButton(self) + self.cancel.setGeometry(QtCore.QRect(100, 2, 200, 20)) + self.cancel.setText("Cancel") + self.cancel.clicked.connect(lambda: self.click(friend_number, file_number)) + + def click(self, friend_number, file_number): + from profile import Profile + profile = Profile.get_instance() + profile.cancel_transfer(friend_number, file_number) + self.name.setText('Cancelled') diff --git a/src/profile.py b/src/profile.py index d7b45dc..a1a9d29 100644 --- a/src/profile.py +++ b/src/profile.py @@ -1,4 +1,4 @@ -from list_items import MessageItem, ContactItem +from list_items import MessageItem, ContactItem, FileTransferItem from PySide import QtCore, QtGui from tox import Tox import os @@ -557,7 +557,7 @@ class Profile(Contact, Singleton): self._history.export(directory) # ----------------------------------------------------------------------------------------------------------------- - # Factories for friend and message items + # Factories for friend, message and file transfer items # ----------------------------------------------------------------------------------------------------------------- def create_friend_item(self): @@ -580,6 +580,14 @@ class Profile(Contact, Singleton): self._messages.setItemWidget(elem, item) self._messages.repaint() + def create_file_transfer_item(self, file_name, friend_number, file_number): + item = FileTransferItem(file_name, curr_time(), '', friend_number, file_number) + elem = QtGui.QListWidgetItem(self._messages) + elem.setSizeHint(QtCore.QSize(500, 100)) + self._messages.addItem(elem) + self._messages.setItemWidget(elem, item) + self._messages.repaint() + # ----------------------------------------------------------------------------------------------------------------- # Work with friends (remove, set alias, get public key) # ----------------------------------------------------------------------------------------------------------------- @@ -713,9 +721,13 @@ class Profile(Contact, Singleton): self._file_transfers[(friend_number, file_number)] = rt self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) else: - self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) + self.create_file_transfer_item(file_name + ' ' + str(size), friend_number, file_number) + #self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) # TODO: show info about incoming transfer + def cancel_transfer(self, friend_number, file_number): + self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) + def incoming_avatar(self, friend_number, file_number, size): """ Friend changed avatar From 42ca5d05975f9adce783ebffa49cbb77ac75f0bb Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 19 Mar 2016 14:41:01 +0300 Subject: [PATCH 21/32] ui update - signal added --- src/file_transfers.py | 13 +++++++++++-- src/list_items.py | 4 ++++ src/profile.py | 16 +++++++++++++--- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/file_transfers.py b/src/file_transfers.py index ec00773..40fd843 100644 --- a/src/file_transfers.py +++ b/src/file_transfers.py @@ -4,6 +4,7 @@ from os import remove from time import time from tox import Tox import profile +from PySide import QtCore TOX_FILE_TRANSFER_STATE = { @@ -14,8 +15,11 @@ TOX_FILE_TRANSFER_STATE = { } -class FileTransfer(object): +class FileTransfer(QtCore.QObject): + signal = QtCore.Signal() + def __init__(self, path, tox, friend_number, file_number=None): + QtCore.QObject.__init__(self) self._path = path self._tox = tox self._friend_number = friend_number @@ -26,6 +30,9 @@ class FileTransfer(object): def set_tox(self, tox): self._tox = tox + def set_event_handler(self, handler): + self.signal.connect(handler) + def get_file_number(self): return self._file_number @@ -51,7 +58,7 @@ class SendTransfer(FileTransfer): kind, getsize(path) if path else 0, file_id, - basename(path) if path else '') + basename(path).encode('utf-8') if path else '') if path is not None: self._file = open(path, 'rb') @@ -63,6 +70,7 @@ class SendTransfer(FileTransfer): else: self._file.close() self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] + self.signal.emit() class SendAvatar(SendTransfer): @@ -76,6 +84,7 @@ class SendAvatar(SendTransfer): class ReceiveTransfer(FileTransfer): + # TODO: remove file on cancel and close file def __init__(self, path, tox, friend_number, file_number): super(ReceiveTransfer, self).__init__(path, tox, friend_number, file_number) self._file = open(self._path, 'wb') diff --git a/src/list_items.py b/src/list_items.py index 5072484..e8f652f 100644 --- a/src/list_items.py +++ b/src/list_items.py @@ -157,6 +157,7 @@ class StatusCircle(QtGui.QWidget): class FileTransferItem(QtGui.QListWidget): + # TODO: accept button def __init__(self, file_name, time, user, friend_number, file_number, parent=None): QtGui.QListWidget.__init__(self, parent) self.name = QtGui.QLabel(self) @@ -190,3 +191,6 @@ class FileTransferItem(QtGui.QListWidget): profile = Profile.get_instance() profile.cancel_transfer(friend_number, file_number) self.name.setText('Cancelled') + + def update(self): + self.name.setText('Finished') diff --git a/src/profile.py b/src/profile.py index a1a9d29..eb27585 100644 --- a/src/profile.py +++ b/src/profile.py @@ -587,6 +587,7 @@ class Profile(Contact, Singleton): self._messages.addItem(elem) self._messages.setItemWidget(elem, item) self._messages.repaint() + return item # ----------------------------------------------------------------------------------------------------------------- # Work with friends (remove, set alias, get public key) @@ -717,9 +718,7 @@ class Profile(Contact, Singleton): friend = self.get_friend_by_number(friend_number) if settings['allow_auto_accept'] and friend.tox_id in settings['auto_accept_from_friends']: path = settings['auto_accept_path'] or curr_directory() - rt = ReceiveTransfer(path + '/' + file_name, self._tox, friend_number, file_number) - self._file_transfers[(friend_number, file_number)] = rt - self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) + self.accept_transfer(path + '/' + file_name, friend_number, file_number) else: self.create_file_transfer_item(file_name + ' ' + str(size), friend_number, file_number) #self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) @@ -727,6 +726,14 @@ class Profile(Contact, Singleton): def cancel_transfer(self, friend_number, file_number): self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) + if (friend_number, file_number) in self._file_transfers: + del self._file_transfers[(friend_number, file_number)] + + def accept_transfer(self, path, friend_number, file_number): + rt = ReceiveTransfer(path + '/' + path, self._tox, friend_number, file_number) + self._file_transfers[(friend_number, file_number)] = rt + self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) + # bind rt with widget def incoming_avatar(self, friend_number, file_number, size): """ @@ -760,6 +767,9 @@ class Profile(Contact, Singleton): friend_number = self.get_active_number() st = SendTransfer(path, self._tox, friend_number) self._file_transfers[(friend_number, st.get_file_number())] = st + item = self.create_file_transfer_item('Out file', friend_number, st.get_file_number()) + st.set_event_handler(item.update) + # bind st with widget def outgoing_chunk(self, friend_number, file_number, position, size): transfer = self._file_transfers[(friend_number, file_number)] From 01803a6376aaab02d8a8561f2258a88c9dd39b7b Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 19 Mar 2016 20:58:42 +0300 Subject: [PATCH 22/32] file transfer item #2. data binding using signals. some other updates --- src/file_transfers.py | 25 +++++++++++++--- src/list_items.py | 66 ++++++++++++++++++++++++++++++++++--------- src/profile.py | 39 +++++++++++++------------ 3 files changed, 92 insertions(+), 38 deletions(-) diff --git a/src/file_transfers.py b/src/file_transfers.py index 40fd843..252afb8 100644 --- a/src/file_transfers.py +++ b/src/file_transfers.py @@ -15,8 +15,11 @@ TOX_FILE_TRANSFER_STATE = { } +class Signal(QtCore.QObject): + signal = QtCore.Signal(int) + + class FileTransfer(QtCore.QObject): - signal = QtCore.Signal() def __init__(self, path, tox, friend_number, file_number=None): QtCore.QObject.__init__(self) @@ -26,12 +29,13 @@ class FileTransfer(QtCore.QObject): self.state = TOX_FILE_TRANSFER_STATE['RUNNING'] self._file_number = file_number self._creation_time = time() + self._signal = Signal() def set_tox(self, tox): self._tox = tox def set_event_handler(self, handler): - self.signal.connect(handler) + self._signal.signal.connect(handler) def get_file_number(self): return self._file_number @@ -39,6 +43,11 @@ class FileTransfer(QtCore.QObject): def get_friend_number(self): return self._friend_number + def cancel(self): + self.send_control(TOX_FILE_CONTROL['CANCEL']) + self._file.close() + self._signal.signal.emit(1) # other signal params - status and %? + def send_control(self, control): if self._tox.file_control(self._friend_number, self._file_number, control): self.state = control @@ -52,6 +61,7 @@ class FileTransfer(QtCore.QObject): class SendTransfer(FileTransfer): + def __init__(self, path, tox, friend_number, kind=TOX_FILE_KIND['DATA'], file_id=None): super(SendTransfer, self).__init__(path, tox, friend_number) self._file_number = tox.file_send(friend_number, @@ -70,10 +80,11 @@ class SendTransfer(FileTransfer): else: self._file.close() self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] - self.signal.emit() + self._signal.signal.emit(position) class SendAvatar(SendTransfer): + def __init__(self, path, tox, friend_number): if path is None: super(SendAvatar, self).__init__(path, tox, friend_number, TOX_FILE_KIND['AVATAR']) @@ -84,13 +95,17 @@ class SendAvatar(SendTransfer): class ReceiveTransfer(FileTransfer): - # TODO: remove file on cancel and close file + def __init__(self, path, tox, friend_number, file_number): super(ReceiveTransfer, self).__init__(path, tox, friend_number, file_number) self._file = open(self._path, 'wb') self._file.truncate(0) self._size = 0 + def cancel(self): + super(ReceiveTransfer, self).cancel() + remove(self._path) + def write_chunk(self, position, data): if data is not None: data = ''.join(chr(x) for x in data) @@ -105,9 +120,11 @@ class ReceiveTransfer(FileTransfer): else: self._file.close() self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] + self._signal.signal.emit(position) class ReceiveAvatar(ReceiveTransfer): + def __init__(self, tox, friend_number, file_number, has_size=True): path = profile.ProfileHelper.get_path() + '/avatars/{}.png'.format(tox.friend_get_public_key(friend_number)) super(ReceiveAvatar, self).__init__(path, tox, friend_number, file_number) diff --git a/src/list_items.py b/src/list_items.py index e8f652f..47ad6c7 100644 --- a/src/list_items.py +++ b/src/list_items.py @@ -1,5 +1,7 @@ from toxcore_enums_and_consts import * from PySide import QtGui, QtCore +import profile +from util import curr_directory class MessageEdit(QtGui.QPlainTextEdit): @@ -157,11 +159,13 @@ class StatusCircle(QtGui.QWidget): class FileTransferItem(QtGui.QListWidget): - # TODO: accept button - def __init__(self, file_name, time, user, friend_number, file_number, parent=None): + def __init__(self, file_name, size, time, user, friend_number, file_number, is_incoming_transfer, parent=None): QtGui.QListWidget.__init__(self, parent) + self.resize(QtCore.QSize(600, 50)) + self.setStyleSheet('QListWidget { background-color: green; }') + self.name = QtGui.QLabel(self) - self.name.setGeometry(QtCore.QRect(0, 2, 95, 20)) + self.name.setGeometry(QtCore.QRect(0, 15, 95, 20)) self.name.setTextFormat(QtCore.Qt.PlainText) font = QtGui.QFont() font.setFamily("Times New Roman") @@ -169,28 +173,62 @@ class FileTransferItem(QtGui.QListWidget): font.setBold(True) self.name.setFont(font) self.name.setObjectName("name") - self.name.setText(file_name) + self.name.setText(user) + self.name.setStyleSheet('QLabel { color: black; }') self.time = QtGui.QLabel(self) - self.time.setGeometry(QtCore.QRect(500, 0, 50, 25)) - font = QtGui.QFont() - font.setFamily("Times New Roman") + self.time.setGeometry(QtCore.QRect(550, 0, 50, 50)) font.setPointSize(10) font.setBold(False) self.time.setFont(font) self.time.setObjectName("time") self.time.setText(time) + self.time.setStyleSheet('QLabel { color: black; }') self.cancel = QtGui.QPushButton(self) - self.cancel.setGeometry(QtCore.QRect(100, 2, 200, 20)) + self.cancel.setGeometry(QtCore.QRect(500, 0, 50, 50)) self.cancel.setText("Cancel") - self.cancel.clicked.connect(lambda: self.click(friend_number, file_number)) + self.cancel.clicked.connect(lambda: self.cancel_transfer(friend_number, file_number)) - def click(self, friend_number, file_number): - from profile import Profile - profile = Profile.get_instance() - profile.cancel_transfer(friend_number, file_number) + self.accept = QtGui.QPushButton(self) + self.accept.setGeometry(QtCore.QRect(450, 0, 50, 50)) + self.accept.setText("Accept") + self.accept.clicked.connect(lambda: self.accept_transfer(friend_number, file_number)) + self.accept.setVisible(is_incoming_transfer) + + self.pb = QtGui.QProgressBar(self) + self.pb.setGeometry(QtCore.QRect(100, 15, 100, 20)) + self.pb.setValue(0) + + self.file_name = QtGui.QLabel(self) + self.file_name.setGeometry(QtCore.QRect(210, 0, 230, 50)) + font.setPointSize(12) + self.file_name.setFont(font) + self.file_name.setObjectName("time") + size /= 1024 + if not size: + size = '<1' + self.file_name.setText('{}KB {}'.format(size, file_name)) + self.file_name.setStyleSheet('QLabel { color: black; }') + self.saved_name = file_name + + def cancel_transfer(self, friend_number, file_number): + pr = profile.Profile.get_instance() + pr.cancel_transfer(friend_number, file_number) self.name.setText('Cancelled') + self.setStyleSheet('QListWidget { background-color: red; }') + self.cancel.setVisible(False) + self.accept.setVisible(False) - def update(self): + def accept_transfer(self, friend_number, file_number): + directory = QtGui.QFileDialog.getExistingDirectory() + if directory: + pr = profile.Profile.get_instance() + pr.accept_transfer(self, directory + '/' + self.saved_name, friend_number, file_number) + self.accept.setVisible(False) + + @QtCore.Slot(str) + def update(self, data): + # TODO: add params - status and % self.name.setText('Finished') + print data diff --git a/src/profile.py b/src/profile.py index eb27585..be8a81a 100644 --- a/src/profile.py +++ b/src/profile.py @@ -168,13 +168,13 @@ class Contact(object): f.write(avatar) self.load_avatar() - def get_avatar_hash(self): - avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) - if not os.path.isfile(avatar_path): # load default image - return 0 - with open(avatar_path, 'rb') as fl: - data = fl.read() - return Tox.hash(data) + # def get_avatar_hash(self): + # avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) + # if not os.path.isfile(avatar_path): # load default image + # return 0 + # with open(avatar_path, 'rb') as fl: + # data = fl.read() + # return Tox.hash(data) class Friend(Contact): @@ -575,15 +575,16 @@ class Profile(Contact, Singleton): def create_message_item(self, text, time, name, message_type): item = MessageItem(text, time, name, message_type, self._messages) elem = QtGui.QListWidgetItem(self._messages) - elem.setSizeHint(QtCore.QSize(500, item.getHeight())) + elem.setSizeHint(QtCore.QSize(600, item.getHeight())) self._messages.addItem(elem) self._messages.setItemWidget(elem, item) self._messages.repaint() - def create_file_transfer_item(self, file_name, friend_number, file_number): - item = FileTransferItem(file_name, curr_time(), '', friend_number, file_number) + def create_file_transfer_item(self, file_name, size, friend_number, file_number, is_incoming_transfer): + friend = self.get_friend_by_number(friend_number) + item = FileTransferItem(file_name, size, curr_time(), friend.name, friend_number, file_number, is_incoming_transfer) elem = QtGui.QListWidgetItem(self._messages) - elem.setSizeHint(QtCore.QSize(500, 100)) + elem.setSizeHint(QtCore.QSize(600, 50)) self._messages.addItem(elem) self._messages.setItemWidget(elem, item) self._messages.repaint() @@ -720,20 +721,19 @@ class Profile(Contact, Singleton): path = settings['auto_accept_path'] or curr_directory() self.accept_transfer(path + '/' + file_name, friend_number, file_number) else: - self.create_file_transfer_item(file_name + ' ' + str(size), friend_number, file_number) - #self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) - # TODO: show info about incoming transfer + self.create_file_transfer_item(file_name, size, friend_number, file_number, True) def cancel_transfer(self, friend_number, file_number): - self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) if (friend_number, file_number) in self._file_transfers: + tr = self._file_transfers[(friend_number, file_number)] + tr.cancel() del self._file_transfers[(friend_number, file_number)] - def accept_transfer(self, path, friend_number, file_number): - rt = ReceiveTransfer(path + '/' + path, self._tox, friend_number, file_number) + def accept_transfer(self, item, path, friend_number, file_number): + rt = ReceiveTransfer(path, self._tox, friend_number, file_number) self._file_transfers[(friend_number, file_number)] = rt self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) - # bind rt with widget + rt.set_event_handler(item.update) def incoming_avatar(self, friend_number, file_number, size): """ @@ -767,9 +767,8 @@ class Profile(Contact, Singleton): friend_number = self.get_active_number() st = SendTransfer(path, self._tox, friend_number) self._file_transfers[(friend_number, st.get_file_number())] = st - item = self.create_file_transfer_item('Out file', friend_number, st.get_file_number()) + item = self.create_file_transfer_item('Out file', friend_number, st.get_file_number(), False) st.set_event_handler(item.update) - # bind st with widget def outgoing_chunk(self, friend_number, file_number, position, size): transfer = self._file_transfers[(friend_number, file_number)] From 6a2bc1f65092307b50e3da760991a5a168f85f24 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Sat, 19 Mar 2016 23:36:54 +0300 Subject: [PATCH 23/32] file transfer item update #3. bug fixes --- src/callbacks.py | 1 + src/file_transfers.py | 12 +++++++----- src/list_items.py | 15 ++++++++++++--- src/profile.py | 24 +++++++++++++----------- 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/callbacks.py b/src/callbacks.py index 496b72d..d69e614 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -177,6 +177,7 @@ def file_chunk_request(tox, friend_number, file_number, position, size, user_dat def file_recv_control(tox, friend_number, file_number, file_control, user_data): + # TODO: process pass # ----------------------------------------------------------------------------------------------------------------- diff --git a/src/file_transfers.py b/src/file_transfers.py index 252afb8..7389722 100644 --- a/src/file_transfers.py +++ b/src/file_transfers.py @@ -16,7 +16,7 @@ TOX_FILE_TRANSFER_STATE = { class Signal(QtCore.QObject): - signal = QtCore.Signal(int) + signal = QtCore.Signal(str) class FileTransfer(QtCore.QObject): @@ -46,7 +46,7 @@ class FileTransfer(QtCore.QObject): def cancel(self): self.send_control(TOX_FILE_CONTROL['CANCEL']) self._file.close() - self._signal.signal.emit(1) # other signal params - status and %? + self._signal.signal.emit('{} {}'.format(self.state, 0)) def send_control(self, control): if self._tox.file_control(self._friend_number, self._file_number, control): @@ -76,11 +76,12 @@ class SendTransfer(FileTransfer): if size: self._file.seek(position) data = self._file.read(size) - return self._tox.file_send_chunk(self._friend_number, self._file_number, position, data) + self._tox.file_send_chunk(self._friend_number, self._file_number, position, data) + self._signal.signal.emit('{} {}'.format(self.state, 0)) else: self._file.close() self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] - self._signal.signal.emit(position) + self._signal.signal.emit('{} {}'.format(self.state, 100)) class SendAvatar(SendTransfer): @@ -117,10 +118,11 @@ class ReceiveTransfer(FileTransfer): self._file.flush() if position + len(data) > self._size: self._size = position + len(data) + self._signal.signal.emit('{} {}'.format(self.state, 0)) else: self._file.close() self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] - self._signal.signal.emit(position) + self._signal.signal.emit('{} {}'.format(self.state, 100)) class ReceiveAvatar(ReceiveTransfer): diff --git a/src/list_items.py b/src/list_items.py index 47ad6c7..ae5724e 100644 --- a/src/list_items.py +++ b/src/list_items.py @@ -1,6 +1,7 @@ from toxcore_enums_and_consts import * from PySide import QtGui, QtCore import profile +from file_transfers import TOX_FILE_TRANSFER_STATE from util import curr_directory @@ -215,10 +216,10 @@ class FileTransferItem(QtGui.QListWidget): def cancel_transfer(self, friend_number, file_number): pr = profile.Profile.get_instance() pr.cancel_transfer(friend_number, file_number) - self.name.setText('Cancelled') self.setStyleSheet('QListWidget { background-color: red; }') self.cancel.setVisible(False) self.accept.setVisible(False) + self.pb.setVisible(False) def accept_transfer(self, friend_number, file_number): directory = QtGui.QFileDialog.getExistingDirectory() @@ -229,6 +230,14 @@ class FileTransferItem(QtGui.QListWidget): @QtCore.Slot(str) def update(self, data): - # TODO: add params - status and % - self.name.setText('Finished') + arr = data.split() + self.pb.setValue(int(arr[1])) + if int(arr[0]) == TOX_FILE_TRANSFER_STATE['CANCELED']: + self.setStyleSheet('QListWidget { background-color: red; }') + self.cancel.setVisible(False) + self.accept.setVisible(False) + self.pb.setVisible(False) + elif int(arr[0]) == TOX_FILE_TRANSFER_STATE['FINISHED']: + self.pb.setVisible(False) + self.cancel.setVisible(False) print data diff --git a/src/profile.py b/src/profile.py index be8a81a..72a1511 100644 --- a/src/profile.py +++ b/src/profile.py @@ -749,12 +749,13 @@ class Profile(Contact, Singleton): self.get_friend_by_number(friend_number).load_avatar() def incoming_chunk(self, friend_number, file_number, position, data): - transfer = self._file_transfers[(friend_number, file_number)] - transfer.write_chunk(position, data) - if transfer.state: - if type(transfer) is ReceiveAvatar: - self.get_friend_by_number(friend_number).load_avatar() - del self._file_transfers[(friend_number, file_number)] + if (friend_number, file_number) in self._file_transfers: + transfer = self._file_transfers[(friend_number, file_number)] + transfer.write_chunk(position, data) + if transfer.state: + if type(transfer) is ReceiveAvatar: + self.get_friend_by_number(friend_number).load_avatar() + del self._file_transfers[(friend_number, file_number)] def send_avatar(self, friend_number): avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) @@ -767,14 +768,15 @@ class Profile(Contact, Singleton): friend_number = self.get_active_number() st = SendTransfer(path, self._tox, friend_number) self._file_transfers[(friend_number, st.get_file_number())] = st - item = self.create_file_transfer_item('Out file', friend_number, st.get_file_number(), False) + item = self.create_file_transfer_item(os.path.basename(path), os.path.getsize(path), friend_number, st.get_file_number(), False) st.set_event_handler(item.update) def outgoing_chunk(self, friend_number, file_number, position, size): - transfer = self._file_transfers[(friend_number, file_number)] - transfer.send_chunk(position, size) - if transfer.state: - del self._file_transfers[(friend_number, file_number)] + if (friend_number, file_number) in self._file_transfers: + transfer = self._file_transfers[(friend_number, file_number)] + transfer.send_chunk(position, size) + if transfer.state: + del self._file_transfers[(friend_number, file_number)] def reset_avatar(self): super(Profile, self).reset_avatar() From e920e8e021c774bb864b4e030fc1fdc5654b7f58 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Mon, 21 Mar 2016 12:12:49 +0300 Subject: [PATCH 24/32] bug fixes --- src/file_transfers.py | 3 +++ src/list_items.py | 2 +- src/profile.py | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/file_transfers.py b/src/file_transfers.py index 7389722..d2a554f 100644 --- a/src/file_transfers.py +++ b/src/file_transfers.py @@ -46,6 +46,7 @@ class FileTransfer(QtCore.QObject): def cancel(self): self.send_control(TOX_FILE_CONTROL['CANCEL']) self._file.close() + print id(self._signal) self._signal.signal.emit('{} {}'.format(self.state, 0)) def send_control(self, control): @@ -76,8 +77,10 @@ class SendTransfer(FileTransfer): if size: self._file.seek(position) data = self._file.read(size) + # TODO: fix bug with wrong data for file_send_chunk self._tox.file_send_chunk(self._friend_number, self._file_number, position, data) self._signal.signal.emit('{} {}'.format(self.state, 0)) + #print self._friend_number, self._file_number, position, data else: self._file.close() self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] diff --git a/src/list_items.py b/src/list_items.py index ae5724e..7f18af1 100644 --- a/src/list_items.py +++ b/src/list_items.py @@ -209,7 +209,7 @@ class FileTransferItem(QtGui.QListWidget): size /= 1024 if not size: size = '<1' - self.file_name.setText('{}KB {}'.format(size, file_name)) + self.file_name.setText(u'{}KB {}'.format(size, file_name)) self.file_name.setStyleSheet('QLabel { color: black; }') self.saved_name = file_name diff --git a/src/profile.py b/src/profile.py index 72a1511..68b55fe 100644 --- a/src/profile.py +++ b/src/profile.py @@ -719,9 +719,9 @@ class Profile(Contact, Singleton): friend = self.get_friend_by_number(friend_number) if settings['allow_auto_accept'] and friend.tox_id in settings['auto_accept_from_friends']: path = settings['auto_accept_path'] or curr_directory() - self.accept_transfer(path + '/' + file_name, friend_number, file_number) + self.accept_transfer(path + '/' + file_name.decode('utf-8'), friend_number, file_number) else: - self.create_file_transfer_item(file_name, size, friend_number, file_number, True) + self.create_file_transfer_item(file_name.decode('utf-8'), size, friend_number, file_number, True) def cancel_transfer(self, friend_number, file_number): if (friend_number, file_number) in self._file_transfers: From 75225e5b345df5b609830d300f589e2c6c45995b Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Mon, 21 Mar 2016 15:04:53 +0300 Subject: [PATCH 25/32] temporary threading fix. ui short update --- src/callbacks.py | 8 ++------ src/list_items.py | 14 +++++++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/callbacks.py b/src/callbacks.py index d69e614..f97d664 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -161,15 +161,11 @@ def tox_file_recv(window, tray): def file_recv_chunk(tox, friend_number, file_number, position, chunk, length, user_data): - invoke_in_main_thread(Profile.get_instance().incoming_chunk, - friend_number, - file_number, - position, - chunk[:length] if length else None) + Profile.get_instance().incoming_chunk(friend_number, file_number, position, chunk[:length] if length else None) def file_chunk_request(tox, friend_number, file_number, position, size, user_data): - invoke_in_main_thread(Profile.get_instance().outgoing_chunk, + Profile.get_instance().outgoing_chunk( friend_number, file_number, position, diff --git a/src/list_items.py b/src/list_items.py index 7f18af1..446bb09 100644 --- a/src/list_items.py +++ b/src/list_items.py @@ -206,10 +206,14 @@ class FileTransferItem(QtGui.QListWidget): font.setPointSize(12) self.file_name.setFont(font) self.file_name.setObjectName("time") - size /= 1024 - if not size: - size = '<1' - self.file_name.setText(u'{}KB {}'.format(size, file_name)) + file_size = size / 1024 + if not file_size: + file_size = '<1KB' + elif file_size >= 1024: + file_size = '{}MB'.format(file_size / 1024) + else: + file_size = '{}KB'.format(file_size) + self.file_name.setText(u'{} {}'.format(file_size, file_name)) self.file_name.setStyleSheet('QLabel { color: black; }') self.saved_name = file_name @@ -240,4 +244,4 @@ class FileTransferItem(QtGui.QListWidget): elif int(arr[0]) == TOX_FILE_TRANSFER_STATE['FINISHED']: self.pb.setVisible(False) self.cancel.setVisible(False) - print data + From 61d4a28d6bd832554d343e3c6d8c3ccd52cc1a11 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Mon, 21 Mar 2016 17:37:56 +0300 Subject: [PATCH 26/32] accept and decline images --- src/callbacks.py | 8 ++++++-- src/images/accept.png | Bin 0 -> 469 bytes src/images/decline.png | Bin 0 -> 1185 bytes src/list_items.py | 10 ++++++++-- 4 files changed, 14 insertions(+), 4 deletions(-) create mode 100755 src/images/accept.png create mode 100755 src/images/decline.png diff --git a/src/callbacks.py b/src/callbacks.py index f97d664..d69e614 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -161,11 +161,15 @@ def tox_file_recv(window, tray): def file_recv_chunk(tox, friend_number, file_number, position, chunk, length, user_data): - Profile.get_instance().incoming_chunk(friend_number, file_number, position, chunk[:length] if length else None) + invoke_in_main_thread(Profile.get_instance().incoming_chunk, + friend_number, + file_number, + position, + chunk[:length] if length else None) def file_chunk_request(tox, friend_number, file_number, position, size, user_data): - Profile.get_instance().outgoing_chunk( + invoke_in_main_thread(Profile.get_instance().outgoing_chunk, friend_number, file_number, position, diff --git a/src/images/accept.png b/src/images/accept.png new file mode 100755 index 0000000000000000000000000000000000000000..5612f2693f46c3bb960c093ec6176e68a7b7aa73 GIT binary patch literal 469 zcmV;`0V@89P)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ-TS-JgRCwC#nnAL|FbG2}CilNV&&uC*(do4QAmP}>+d3{U0xTziyN?sbHO3ft zK(+p-y97E&F~I8_3Xm;=$jmPR@#5>GiYys+12rhuS1N*C6q$j*&LF5zri;BnlwPZP z7Y?isrXnkCvVZ%3+W=xJ4%-OgV?4GQ#Bp8AnII5a4los&J*I*T5b|ZV>JCn>0R{*P zVUTS$#wxZ1%<_j13>ZXra(0bmU^3~1|AHV11655#6v@CSoHa73rH1ir92Yq_4|E|9 z%musHS%FEtmO53a&RXeyy|Ou|ZM;{VajZaguQ;ehUi2UU?xOXdUGh#gKDCY|4peLy zC~BlS7$kmoRoB9bv@}v$Md#Aa`^tR;h03{lLxft1cZ#UKQaI|373&=$R)fftcm?rQ z0qmAqa%E>sqqiar^1~okq_A!(fVHfu#O>=8`_00000 LNkvXXu0mjfo+!6c literal 0 HcmV?d00001 diff --git a/src/images/decline.png b/src/images/decline.png new file mode 100755 index 0000000000000000000000000000000000000000..dfdf85d2964eb980261e89bddae912511e6da67f GIT binary patch literal 1185 zcmV;S1YY}zP)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ=CrLy>RCwCd+FOWKRUF6h&+I8QO;dDANs%79=@3$8N*AIiBOJRJ)k9Yz3Mvrl zrKqqAK1dSSOApaS2nnG`ZJP3_LQNyfii$v(FoL|KgIY=D^sxVjPSd&U%Q@rm2j^w) zz4lq(^Iz+KTc>MKOLP)?u>cq1Je+~a=#FQO;Q;pF2kgK$e1)TBC(uj7`!oOu*(uO2jo91*;^^Bj!HXQ`L{v0C(`+IbG)Dz zXJL0l>AtABXJi;VJ{-kRd}iQDJd_h+3!cGnb%SnU+{mlpSe|1GZq%b;h3*1l`zm`i z8Q}uoEby|f5)h*4Q3PTj_Q!wPK^4u2woofl`*!LN>FM!z^^(S)q89ITt*nNq-t5)d zEmp_qsu1_ZudwboLJZ*gMi5y`-63gj_%UhMaI|zJqV{6^(#o2B73PhSbv8sUwj6)A zqxrBatg}dZEHK<+fRCi@)g{ui% Date: Mon, 21 Mar 2016 20:19:13 +0300 Subject: [PATCH 27/32] progress bar in file transfers --- src/file_transfers.py | 76 ++++++++++++++++++++++--------------------- src/list_items.py | 11 +++---- src/profile.py | 4 +-- 3 files changed, 46 insertions(+), 45 deletions(-) diff --git a/src/file_transfers.py b/src/file_transfers.py index d2a554f..491979b 100644 --- a/src/file_transfers.py +++ b/src/file_transfers.py @@ -15,13 +15,13 @@ TOX_FILE_TRANSFER_STATE = { } -class Signal(QtCore.QObject): - signal = QtCore.Signal(str) +class StateSignal(QtCore.QObject): + signal = QtCore.Signal(int, float) class FileTransfer(QtCore.QObject): - def __init__(self, path, tox, friend_number, file_number=None): + def __init__(self, path, tox, friend_number, size, file_number=None): QtCore.QObject.__init__(self) self._path = path self._tox = tox @@ -29,13 +29,15 @@ class FileTransfer(QtCore.QObject): self.state = TOX_FILE_TRANSFER_STATE['RUNNING'] self._file_number = file_number self._creation_time = time() - self._signal = Signal() + self._size = size + self._done = 0 + self._state_changed = StateSignal() def set_tox(self, tox): self._tox = tox - def set_event_handler(self, handler): - self._signal.signal.connect(handler) + def set_state_changed_handler(self, handler): + self._state_changed.signal.connect(handler) def get_file_number(self): return self._file_number @@ -46,95 +48,95 @@ class FileTransfer(QtCore.QObject): def cancel(self): self.send_control(TOX_FILE_CONTROL['CANCEL']) self._file.close() - print id(self._signal) - self._signal.signal.emit('{} {}'.format(self.state, 0)) + self._state_changed.signal.emit(self.state, self._done / self._size) def send_control(self, control): if self._tox.file_control(self._friend_number, self._file_number, control): self.state = control + self._state_changed.signal.emit(self.state, self._done / self._size) def get_file_id(self): return self._tox.file_get_file_id(self._friend_number, self._file_number) def file_seek(self): - # TODO implement + # TODO implement or not implement pass class SendTransfer(FileTransfer): def __init__(self, path, tox, friend_number, kind=TOX_FILE_KIND['DATA'], file_id=None): - super(SendTransfer, self).__init__(path, tox, friend_number) - self._file_number = tox.file_send(friend_number, - kind, - getsize(path) if path else 0, - file_id, - basename(path).encode('utf-8') if path else '') if path is not None: self._file = open(path, 'rb') + size = getsize(path) + else: + size = 0 + super(SendTransfer, self).__init__(path, tox, friend_number, size) + self._file_number = tox.file_send(friend_number, kind, size, file_id, + basename(path).encode('utf-8') if path else '') def send_chunk(self, position, size): if size: self._file.seek(position) data = self._file.read(size) - # TODO: fix bug with wrong data for file_send_chunk self._tox.file_send_chunk(self._friend_number, self._file_number, position, data) - self._signal.signal.emit('{} {}'.format(self.state, 0)) - #print self._friend_number, self._file_number, position, data + self._done += size + self._state_changed.signal.emit(self.state, self._done / self._size) else: self._file.close() self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] - self._signal.signal.emit('{} {}'.format(self.state, 100)) + self._state_changed.signal.emit(self.state, self._done / self._size) class SendAvatar(SendTransfer): def __init__(self, path, tox, friend_number): if path is None: - super(SendAvatar, self).__init__(path, tox, friend_number, TOX_FILE_KIND['AVATAR']) + hash = None else: with open(path, 'rb') as fl: hash = Tox.hash(fl.read()) - super(self.__class__, self).__init__(path, tox, friend_number, TOX_FILE_KIND['AVATAR'], hash) + super(SendAvatar, self).__init__(path, tox, friend_number, TOX_FILE_KIND['AVATAR'], hash) class ReceiveTransfer(FileTransfer): - def __init__(self, path, tox, friend_number, file_number): - super(ReceiveTransfer, self).__init__(path, tox, friend_number, file_number) + def __init__(self, path, tox, friend_number, size, file_number): + super(ReceiveTransfer, self).__init__(path, tox, friend_number, size, file_number) self._file = open(self._path, 'wb') self._file.truncate(0) - self._size = 0 + self._file_size = 0 def cancel(self): super(ReceiveTransfer, self).cancel() remove(self._path) def write_chunk(self, position, data): - if data is not None: + if data is None: + self._file.close() + self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] + self._state_changed.signal.emit(self.state, self._done / self._size) + else: data = ''.join(chr(x) for x in data) - if self._size < position: + if self._file_size < position: self._file.seek(0, 2) - self._file.write('\0' * (position - self._size)) + self._file.write('\0' * (position - self._file_size)) self._file.seek(position) self._file.write(data) self._file.flush() - if position + len(data) > self._size: - self._size = position + len(data) - self._signal.signal.emit('{} {}'.format(self.state, 0)) - else: - self._file.close() - self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] - self._signal.signal.emit('{} {}'.format(self.state, 100)) + if position + len(data) > self._file_size: + self._file_size = position + len(data) + self._done += len(data) + self._state_changed.signal.emit(self.state, self._done / self._size) class ReceiveAvatar(ReceiveTransfer): - def __init__(self, tox, friend_number, file_number, has_size=True): + def __init__(self, tox, friend_number, size, file_number): path = profile.ProfileHelper.get_path() + '/avatars/{}.png'.format(tox.friend_get_public_key(friend_number)) - super(ReceiveAvatar, self).__init__(path, tox, friend_number, file_number) + super(ReceiveAvatar, self).__init__(path, tox, friend_number, size, file_number) if exists(path): - if not has_size: + if not size: remove(path) self.send_control(TOX_FILE_CONTROL['CANCEL']) self.state = TOX_FILE_TRANSFER_STATE['CANCELED'] diff --git a/src/list_items.py b/src/list_items.py index 7b4f36d..43a46ab 100644 --- a/src/list_items.py +++ b/src/list_items.py @@ -238,16 +238,15 @@ class FileTransferItem(QtGui.QListWidget): pr.accept_transfer(self, directory + '/' + self.saved_name, friend_number, file_number) self.accept.setVisible(False) - @QtCore.Slot(str) - def update(self, data): - arr = data.split() - self.pb.setValue(int(arr[1])) - if int(arr[0]) == TOX_FILE_TRANSFER_STATE['CANCELED']: + @QtCore.Slot(int, float) + def update(self, state, progress): + self.pb.setValue(int(progress * 100)) + if state == TOX_FILE_TRANSFER_STATE['CANCELED']: self.setStyleSheet('QListWidget { background-color: red; }') self.cancel.setVisible(False) self.accept.setVisible(False) self.pb.setVisible(False) - elif int(arr[0]) == TOX_FILE_TRANSFER_STATE['FINISHED']: + elif state == TOX_FILE_TRANSFER_STATE['FINISHED']: self.pb.setVisible(False) self.cancel.setVisible(False) diff --git a/src/profile.py b/src/profile.py index 68b55fe..58801b3 100644 --- a/src/profile.py +++ b/src/profile.py @@ -733,7 +733,7 @@ class Profile(Contact, Singleton): rt = ReceiveTransfer(path, self._tox, friend_number, file_number) self._file_transfers[(friend_number, file_number)] = rt self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) - rt.set_event_handler(item.update) + rt.set_state_changed_handler(item.update) def incoming_avatar(self, friend_number, file_number, size): """ @@ -769,7 +769,7 @@ class Profile(Contact, Singleton): st = SendTransfer(path, self._tox, friend_number) self._file_transfers[(friend_number, st.get_file_number())] = st item = self.create_file_transfer_item(os.path.basename(path), os.path.getsize(path), friend_number, st.get_file_number(), False) - st.set_event_handler(item.update) + st.set_state_changed_handler(item.update) def outgoing_chunk(self, friend_number, file_number, position, size): if (friend_number, file_number) in self._file_transfers: From bfbc82a9a3731c1b91ca3195f190187df69bf611 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Mon, 21 Mar 2016 21:53:02 +0300 Subject: [PATCH 28/32] progressbar is now work --- src/file_transfers.py | 20 ++++++++++++-------- src/list_items.py | 8 +++++--- src/profile.py | 6 +++--- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/file_transfers.py b/src/file_transfers.py index 491979b..78de97d 100644 --- a/src/file_transfers.py +++ b/src/file_transfers.py @@ -29,7 +29,7 @@ class FileTransfer(QtCore.QObject): self.state = TOX_FILE_TRANSFER_STATE['RUNNING'] self._file_number = file_number self._creation_time = time() - self._size = size + self._size = float(size) self._done = 0 self._state_changed = StateSignal() @@ -53,7 +53,7 @@ class FileTransfer(QtCore.QObject): def send_control(self, control): if self._tox.file_control(self._friend_number, self._file_number, control): self.state = control - self._state_changed.signal.emit(self.state, self._done / self._size) + self._state_changed.signal.emit(self.state, self._done / self._size if self._size else 0) def get_file_id(self): return self._tox.file_get_file_id(self._friend_number, self._file_number) @@ -79,13 +79,16 @@ class SendTransfer(FileTransfer): if size: self._file.seek(position) data = self._file.read(size) - self._tox.file_send_chunk(self._friend_number, self._file_number, position, data) + try: + self._tox.file_send_chunk(self._friend_number, self._file_number, position, data) + except: + print self._friend_number, self._file_number, position, data self._done += size self._state_changed.signal.emit(self.state, self._done / self._size) else: self._file.close() self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] - self._state_changed.signal.emit(self.state, self._done / self._size) + self._state_changed.signal.emit(self.state, 1) class SendAvatar(SendTransfer): @@ -115,7 +118,7 @@ class ReceiveTransfer(FileTransfer): if data is None: self._file.close() self.state = TOX_FILE_TRANSFER_STATE['FINISHED'] - self._state_changed.signal.emit(self.state, self._done / self._size) + self._state_changed.signal.emit(self.state, self._done / self._size if self._size else 0) else: data = ''.join(chr(x) for x in data) if self._file_size < position: @@ -124,9 +127,10 @@ class ReceiveTransfer(FileTransfer): self._file.seek(position) self._file.write(data) self._file.flush() - if position + len(data) > self._file_size: - self._file_size = position + len(data) - self._done += len(data) + l = len(data) + if position + l > self._file_size: + self._file_size = position + l + self._done += l self._state_changed.signal.emit(self.state, self._done / self._size) diff --git a/src/list_items.py b/src/list_items.py index 43a46ab..4108358 100644 --- a/src/list_items.py +++ b/src/list_items.py @@ -200,7 +200,7 @@ class FileTransferItem(QtGui.QListWidget): icon = QtGui.QIcon(pixmap) self.accept.setIcon(icon) self.accept.setIconSize(QtCore.QSize(50, 50)) - self.accept.clicked.connect(lambda: self.accept_transfer(friend_number, file_number)) + self.accept.clicked.connect(lambda: self.accept_transfer(friend_number, file_number, size)) self.accept.setVisible(is_incoming_transfer) self.pb = QtGui.QProgressBar(self) @@ -231,16 +231,18 @@ class FileTransferItem(QtGui.QListWidget): self.accept.setVisible(False) self.pb.setVisible(False) - def accept_transfer(self, friend_number, file_number): + def accept_transfer(self, friend_number, file_number, size): directory = QtGui.QFileDialog.getExistingDirectory() if directory: pr = profile.Profile.get_instance() - pr.accept_transfer(self, directory + '/' + self.saved_name, friend_number, file_number) + pr.accept_transfer(self, directory + '/' + self.saved_name, friend_number, file_number, size) self.accept.setVisible(False) @QtCore.Slot(int, float) def update(self, state, progress): self.pb.setValue(int(progress * 100)) + #self.pb.repaint() + print state, progress if state == TOX_FILE_TRANSFER_STATE['CANCELED']: self.setStyleSheet('QListWidget { background-color: red; }') self.cancel.setVisible(False) diff --git a/src/profile.py b/src/profile.py index 58801b3..38ae0c4 100644 --- a/src/profile.py +++ b/src/profile.py @@ -729,8 +729,8 @@ class Profile(Contact, Singleton): tr.cancel() del self._file_transfers[(friend_number, file_number)] - def accept_transfer(self, item, path, friend_number, file_number): - rt = ReceiveTransfer(path, self._tox, friend_number, file_number) + def accept_transfer(self, item, path, friend_number, file_number, size): + rt = ReceiveTransfer(path, self._tox, friend_number, size, file_number) self._file_transfers[(friend_number, file_number)] = rt self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) rt.set_state_changed_handler(item.update) @@ -742,7 +742,7 @@ class Profile(Contact, Singleton): :param file_number: file number :param size: size of avatar or 0 (default avatar) """ - ra = ReceiveAvatar(self._tox, friend_number, file_number, size) + ra = ReceiveAvatar(self._tox, friend_number, size, file_number) if ra.state != TOX_FILE_TRANSFER_STATE['CANCELED']: self._file_transfers[(friend_number, file_number)] = ra else: From 1897fa762a00d493fc92aabffd8d66bf2e22a5a7 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Mon, 21 Mar 2016 23:51:29 +0300 Subject: [PATCH 29/32] sending fix --- src/callbacks.py | 2 +- src/file_transfers.py | 5 +---- src/list_items.py | 2 -- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/callbacks.py b/src/callbacks.py index d69e614..31f480a 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -169,7 +169,7 @@ def file_recv_chunk(tox, friend_number, file_number, position, chunk, length, us def file_chunk_request(tox, friend_number, file_number, position, size, user_data): - invoke_in_main_thread(Profile.get_instance().outgoing_chunk, + Profile.get_instance().outgoing_chunk( friend_number, file_number, position, diff --git a/src/file_transfers.py b/src/file_transfers.py index 78de97d..91412f2 100644 --- a/src/file_transfers.py +++ b/src/file_transfers.py @@ -79,10 +79,7 @@ class SendTransfer(FileTransfer): if size: self._file.seek(position) data = self._file.read(size) - try: - self._tox.file_send_chunk(self._friend_number, self._file_number, position, data) - except: - print self._friend_number, self._file_number, position, data + self._tox.file_send_chunk(self._friend_number, self._file_number, position, data) self._done += size self._state_changed.signal.emit(self.state, self._done / self._size) else: diff --git a/src/list_items.py b/src/list_items.py index 4108358..75fbfbf 100644 --- a/src/list_items.py +++ b/src/list_items.py @@ -241,8 +241,6 @@ class FileTransferItem(QtGui.QListWidget): @QtCore.Slot(int, float) def update(self, state, progress): self.pb.setValue(int(progress * 100)) - #self.pb.repaint() - print state, progress if state == TOX_FILE_TRANSFER_STATE['CANCELED']: self.setStyleSheet('QListWidget { background-color: red; }') self.cancel.setVisible(False) From 9d405b6c125872a0e50ef9a91aa06e3e7f85c7b2 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Tue, 22 Mar 2016 12:55:49 +0300 Subject: [PATCH 30/32] file transfer ui fix - too long data --- src/list_items.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/list_items.py b/src/list_items.py index 75fbfbf..c1929e5 100644 --- a/src/list_items.py +++ b/src/list_items.py @@ -174,7 +174,7 @@ class FileTransferItem(QtGui.QListWidget): font.setBold(True) self.name.setFont(font) self.name.setObjectName("name") - self.name.setText(user) + self.name.setText(user if len(user) <= 14 else user[:11] + '...') self.name.setStyleSheet('QLabel { color: black; }') self.time = QtGui.QLabel(self) @@ -219,7 +219,8 @@ class FileTransferItem(QtGui.QListWidget): file_size = '{}MB'.format(file_size / 1024) else: file_size = '{}KB'.format(file_size) - self.file_name.setText(u'{} {}'.format(file_size, file_name)) + file_data = u'{} {}'.format(file_size, file_name) + self.file_name.setText(file_data if len(file_data) <= 27 else file_data[:24] + '...') self.file_name.setStyleSheet('QLabel { color: black; }') self.saved_name = file_name From 046a091ed89fc66dfec2cb552e3afac7ceef78a1 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Tue, 22 Mar 2016 15:04:21 +0300 Subject: [PATCH 31/32] created file transfer item for autoaccept --- src/list_items.py | 4 ++-- src/profile.py | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/list_items.py b/src/list_items.py index c1929e5..e93ddc2 100644 --- a/src/list_items.py +++ b/src/list_items.py @@ -160,7 +160,7 @@ class StatusCircle(QtGui.QWidget): class FileTransferItem(QtGui.QListWidget): - def __init__(self, file_name, size, time, user, friend_number, file_number, is_incoming_transfer, parent=None): + def __init__(self, file_name, size, time, user, friend_number, file_number, show_accept, parent=None): QtGui.QListWidget.__init__(self, parent) self.resize(QtCore.QSize(600, 50)) self.setStyleSheet('QListWidget { background-color: green; }') @@ -201,7 +201,7 @@ class FileTransferItem(QtGui.QListWidget): self.accept.setIcon(icon) self.accept.setIconSize(QtCore.QSize(50, 50)) self.accept.clicked.connect(lambda: self.accept_transfer(friend_number, file_number, size)) - self.accept.setVisible(is_incoming_transfer) + self.accept.setVisible(show_accept) self.pb = QtGui.QProgressBar(self) self.pb.setGeometry(QtCore.QRect(100, 15, 100, 20)) diff --git a/src/profile.py b/src/profile.py index 38ae0c4..47995ba 100644 --- a/src/profile.py +++ b/src/profile.py @@ -493,7 +493,7 @@ class Profile(Contact, Singleton): self._messages.scrollToBottom() self._friends[self._active_friend].append_message((message.decode('utf-8'), MESSAGE_OWNER['FRIEND'], - time.time(), + int(time.time()), message_type)) else: friend = filter(lambda x: x.number == friend_num, self._friends)[0] @@ -580,9 +580,9 @@ class Profile(Contact, Singleton): self._messages.setItemWidget(elem, item) self._messages.repaint() - def create_file_transfer_item(self, file_name, size, friend_number, file_number, is_incoming_transfer): + def create_file_transfer_item(self, file_name, size, friend_number, file_number, show_accept): friend = self.get_friend_by_number(friend_number) - item = FileTransferItem(file_name, size, curr_time(), friend.name, friend_number, file_number, is_incoming_transfer) + item = FileTransferItem(file_name, size, curr_time(), friend.name, friend_number, file_number, show_accept) elem = QtGui.QListWidgetItem(self._messages) elem.setSizeHint(QtCore.QSize(600, 50)) self._messages.addItem(elem) @@ -720,8 +720,10 @@ class Profile(Contact, Singleton): if settings['allow_auto_accept'] and friend.tox_id in settings['auto_accept_from_friends']: path = settings['auto_accept_path'] or curr_directory() self.accept_transfer(path + '/' + file_name.decode('utf-8'), friend_number, file_number) + item = self.create_file_transfer_item(file_name.decode('utf-8'), size, friend_number, file_number, False) else: - self.create_file_transfer_item(file_name.decode('utf-8'), size, friend_number, file_number, True) + item = self.create_file_transfer_item(file_name.decode('utf-8'), size, friend_number, file_number, True) + friend.append_file_transfer((item, MESSAGE_OWNER['FRIEND'], int(time.time()), 2)) def cancel_transfer(self, friend_number, file_number): if (friend_number, file_number) in self._file_transfers: From 6ffa15b4d78e53512ad22c8cdaaca39083d8263f Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Tue, 22 Mar 2016 15:17:46 +0300 Subject: [PATCH 32/32] screenshot button todo --- src/mainscreen.py | 6 ++++++ src/profile.py | 5 ++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/mainscreen.py b/src/mainscreen.py index 9eda4ec..3196900 100644 --- a/src/mainscreen.py +++ b/src/mainscreen.py @@ -103,6 +103,7 @@ class MainWindow(QtGui.QMainWindow): self.sendMessageButton.setObjectName("sendMessageButton") self.sendMessageButton.clicked.connect(self.send_message) self.fileTransferButton.clicked.connect(self.send_file) + self.screenshotButton.clicked.connect(self.send_screenshot) self.screenshotButton.setText(QtGui.QApplication.translate("Form", "Screenshot", None, QtGui.QApplication.UnicodeUTF8)) self.fileTransferButton.setText(QtGui.QApplication.translate("Form", "File", None, QtGui.QApplication.UnicodeUTF8)) self.sendMessageButton.setText(QtGui.QApplication.translate("Form", "Send", None, QtGui.QApplication.UnicodeUTF8)) @@ -282,6 +283,11 @@ class MainWindow(QtGui.QMainWindow): if name[0]: self.profile.send_file(name[0]) + def send_screenshot(self): + # TODO: add screenshots support + if self.profile.is_active_online(): # active friend exists and online + pass + # ----------------------------------------------------------------------------------------------------------------- # Functions which called when user open context menu in friends list # ----------------------------------------------------------------------------------------------------------------- diff --git a/src/profile.py b/src/profile.py index 47995ba..bf23540 100644 --- a/src/profile.py +++ b/src/profile.py @@ -720,10 +720,9 @@ class Profile(Contact, Singleton): if settings['allow_auto_accept'] and friend.tox_id in settings['auto_accept_from_friends']: path = settings['auto_accept_path'] or curr_directory() self.accept_transfer(path + '/' + file_name.decode('utf-8'), friend_number, file_number) - item = self.create_file_transfer_item(file_name.decode('utf-8'), size, friend_number, file_number, False) + self.create_file_transfer_item(file_name.decode('utf-8'), size, friend_number, file_number, False) else: - item = self.create_file_transfer_item(file_name.decode('utf-8'), size, friend_number, file_number, True) - friend.append_file_transfer((item, MESSAGE_OWNER['FRIEND'], int(time.time()), 2)) + self.create_file_transfer_item(file_name.decode('utf-8'), size, friend_number, file_number, True) def cancel_transfer(self, friend_number, file_number): if (friend_number, file_number) in self._file_transfers: