From 78f6c310617e1ee936918484977d6766141e6e8f Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Wed, 27 Aug 2014 14:21:08 -0700 Subject: [PATCH] beginning to refactor onionshare_gui to work with multiple files (#66) --- MANIFEST.in | 1 + onionshare/strings.json | 8 +- onionshare_gui/common.py | 10 +++ onionshare_gui/drop_files.png | Bin 0 -> 2035 bytes onionshare_gui/file_selection.py | 145 +++++++++++++++++++++++++++++++ onionshare_gui/onionshare_gui.py | 87 ++++++++++--------- setup/onionshare-osx.spec | 1 + setup/onionshare.nsi | 2 + 8 files changed, 214 insertions(+), 40 deletions(-) create mode 100644 onionshare_gui/common.py create mode 100644 onionshare_gui/drop_files.png create mode 100644 onionshare_gui/file_selection.py diff --git a/MANIFEST.in b/MANIFEST.in index 8af755df..d054d40d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -6,5 +6,6 @@ include onionshare/index.html include onionshare/404.html include onionshare/strings.json include onionshare_gui/logo.png +include onionshare_gui/drop_files.png include setup/onionshare.desktop include setup/onionshare80.xpm diff --git a/onionshare/strings.json b/onionshare/strings.json index 312e86bd..8207fc45 100644 --- a/onionshare/strings.json +++ b/onionshare/strings.json @@ -26,7 +26,13 @@ "help_local_only": "Do not attempt to use tor: for development only", "help_stay_open": "Keep hidden service running after download has finished", "help_debug": "Log errors to disk", - "help_filename": "List of files or folders to share" + "help_filename": "List of files or folders to share", + "gui_drag_and_drop": "Drag and drop\nfiles here", + "gui_add_files": "Add Files", + "gui_add_folder": "Add Folder", + "gui_delete": "Delete", + "gui_choose_files": "Choose files", + "gui_choose_folder": "Choose folder" }, "no": { "calculating_sha1": "Kalkulerer SHA1 sjekksum.", "connecting_ctrlport": "Kobler til Tors kontroll-port for å sette opp en gjemt tjeneste på port {0}.", diff --git a/onionshare_gui/common.py b/onionshare_gui/common.py new file mode 100644 index 00000000..4394a26d --- /dev/null +++ b/onionshare_gui/common.py @@ -0,0 +1,10 @@ +import os, inspect, platform + +def get_onionshare_gui_dir(): + if platform.system() == 'Darwin': + onionshare_gui_dir = os.path.dirname(__file__) + else: + onionshare_gui_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + return onionshare_gui_dir + +onionshare_gui_dir = get_onionshare_gui_dir() diff --git a/onionshare_gui/drop_files.png b/onionshare_gui/drop_files.png new file mode 100644 index 0000000000000000000000000000000000000000..1e2ea7d6f1749949ab5787dc830c745ae2902f27 GIT binary patch literal 2035 zcmZ`)dpHwn7{_uacS0_?-%2i_j0%f26SZYHZev(1#!93^Va!moOC%<_)uc^wM8si7 z5h`j(8gg=Ii^F8paaQO2asD~q^Stlxd4J#c{+{oT_d@XZ^H!A8krNXWQv~~XofIMM zyU9q2JWw1vD*`Db#M?`Rq9gR|$P|HWl21^In3%lUcM})8adVf5l*WOfAn9ot2}uJ- z%HxD{B1#VpL*P6UF&NZYoG26%^GrrX;!x3ssc2lBp*I-nAH5GHElLdpdmV*cnB-N2 zCaQU>#@dN+pD@$zf5_^5KK*I*AN~#b*d!psUw}A?~22@ zpsZ*h&1(|X!<~Fne?%(|aIcF2_-P8pGV`sJjIcE7Av#&WHaA&xhX~!c8wuA@T8tQ@ zy@Nzjzo0TyEW|9hBLO)iVZHu`>RjwZlO|Jqw?=5A6f;Iheo8;I{9rzEsT=#U@9w0` z!z#qFXX?wu`3T)snf$OuSEu8IjUZ;c-ne|{bEUNVa4_8Gjm`O<44DH+_~mf8lZ7L( z8@O!NH@5577icb_#tK?COJF;yscDBF1_0&0{$6}pjZ3NFHy^I{v)N>orIoaETiCZ$ zHvUT7yEBR|R}TQcCVv!jF}cFazrxI9e{ztX;*lIEI3NBH`I6vH-zwWZDjjF56V9cwCF(kypG`}?zWHWDoo z$q7jXE)za`I1c7|2_{;fiz5IjdZ|X88=MVdJ>LS^=k|KVF9ez0D!O z0sM==Byx@6b?1*x$-h}}_AQ%70W_AypR%zPh%X7_DYt&1D$CmqXxxT< zyPW~}BKNHp`f>W%){dXw*9lc4V;^3=N-C^|#zP8E))HpyJ^lI>)CNDE>hhd=>u8Wn zW1unl6*TrMYPt?f6UZ*8c>KYt0ER|Bx^~Ql1NSJO-F-=zkT%>Fmd zUw3RvaYivCxp* zR6P$1iV8N!Mf9t0eej5TG+?NqIXVYqTlH!){ADkxs0ox=0@Xo*u717gLbtN)C6bz; zk#$dTVDWzSqKf))BaZi0m9Wxv$#fCbd!RM2-Tv|hOq~HRGd5*?-&A$7T;Jr%%=6A3|9b@`Did?{`9Up$hpS z@*k(7y{T<%r|_+ilrR6U2vYWki0JMAss1Nl^ru<;{sr%&l@c0xoF~%juZwD<^)D@6SuK1!Xh~fHScKo{$#qC!2ias{uA=VL z^(I5Jn`-gANbscL4gI1Z81Q)Z37c|s-NiO^5by9VW-b}jD9=@Io0}*- z#Eck42V#dV3BFvzg(NL$dU_CZ32;cj$%aYhMgFLZ3Z z4sG;+)cJet%fLRlgUO1skLKRHjizLi(rXqC+#e*LS!I^2%%;S!fJ5oG{ma>*T&n3@ zb?GOBLiU)BwYcti@r>r1aZs$4`cik;0@W*SJ;4QI74~wp1;NYh!lJws`4R>Rwarq# zmiT~|Jt;&pP1Ytv`hHeW$vfPo8KdxElf1R!W8bO= thresh: + b /= thresh + u += 1 + return '{0} {1}'.format(round(b, 1), units[u]) + +class FileSelection(QtGui.QVBoxLayout): + def __init__(self): + super(FileSelection, self).__init__() + + # file list + self.file_list = FileList() + self.file_list.currentItemChanged.connect(self.update) + self.file_list.files_dropped.connect(self.update) + + # buttons + self.add_files_button = QtGui.QPushButton(strings._('gui_add_files')) + self.add_files_button.clicked.connect(self.add_files) + self.add_dir_button = QtGui.QPushButton(strings._('gui_add_folder')) + self.add_dir_button.clicked.connect(self.add_dir) + self.delete_button = QtGui.QPushButton(strings._('gui_delete')) + self.delete_button.clicked.connect(self.delete_file) + button_layout = QtGui.QHBoxLayout() + button_layout.addWidget(self.add_files_button) + button_layout.addWidget(self.add_dir_button) + button_layout.addWidget(self.delete_button) + + # add the widgets + self.addWidget(self.file_list) + self.addLayout(button_layout) + + self.update() + + def update(self): + # delete button should be disabled if item isn't selected + current_item = self.file_list.currentItem() + if not current_item: + self.delete_button.setEnabled(False) + else: + self.delete_button.setEnabled(True) + + # update the file list + self.file_list.update() + + def add_files(self): + filenames = QtGui.QFileDialog.getOpenFileNames(caption=strings._('gui_choose_files'), options=QtGui.QFileDialog.ReadOnly) + if filenames: + for filename in filenames: + self.file_list.add_file(str(filename)) + self.update() + + def add_dir(self): + filename = QtGui.QFileDialog.getExistingDirectory(caption=strings._('gui_choose_folder'), options=QtGui.QFileDialog.ReadOnly) + if filename: + self.file_list.add_file(str(filename)) + self.update() + + def delete_file(self): + current_row = self.file_list.currentRow() + self.file_list.filenames.pop(current_row) + self.file_list.takeItem(current_row) + self.update() + diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 35780c57..84d02626 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -2,20 +2,17 @@ from __future__ import division import os, sys, subprocess, inspect, platform, argparse, threading, time, math, inspect, platform from PyQt4 import QtCore, QtGui -def get_onionshare_gui_dir(): - if platform.system() == 'Darwin': - onionshare_gui_dir = os.path.dirname(__file__) - else: - onionshare_gui_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) - return onionshare_gui_dir +import common try: import onionshare except ImportError: - sys.path.append(os.path.abspath(__file__+"/..")) + sys.path.append(os.path.abspath(common.onionshare_gui_dir+"/..")) import onionshare from onionshare import strings, helpers, web +from file_selection import FileSelection + class Application(QtGui.QApplication): def __init__(self): platform = helpers.get_platform() @@ -24,10 +21,28 @@ class Application(QtGui.QApplication): QtGui.QApplication.__init__(self, sys.argv) class OnionShareGui(QtGui.QWidget): - def __init__(self, app, filename, basename): + def __init__(self, app, filenames=None): super(OnionShareGui, self).__init__() self.app = app + self.filenames = filenames + self.setWindowTitle('OnionShare') + self.setWindowIcon(window_icon) + + def start_send(self): + # file selection + file_selection = FileSelection() + if self.filenames: + for filename in self.filenames: + file_selection.file_list.add_file(filename) + + # main layout + self.layout = QtGui.QVBoxLayout() + self.layout.addLayout(file_selection) + self.setLayout(self.layout) + self.show() + +""" # initialize ui self.init_ui(filename, basename) # check for requests every 1000ms @@ -64,7 +79,7 @@ class OnionShareGui(QtGui.QWidget): # logo self.logoLabel = QtGui.QLabel(self.widget) - self.logo = QtGui.QPixmap("{0}/static/logo.png".format(get_onionshare_gui_dir())) + self.logo = QtGui.QPixmap("{0}/static/logo.png".format(common.onionshare_gui_dir)) self.logoLabel.setPixmap(self.logo) self.header.addWidget(self.logoLabel) @@ -268,6 +283,7 @@ class OnionShareGui(QtGui.QWidget): else: web.set_stay_open(True) return +""" def alert(msg, icon=QtGui.QMessageBox.NoIcon): dialog = QtGui.QMessageBox() @@ -277,24 +293,6 @@ def alert(msg, icon=QtGui.QMessageBox.NoIcon): dialog.setIcon(icon) dialog.exec_() -def select_file(filename=None): - # get filename, either from argument or file chooser dialog - if not filename: - filename = QtGui.QFileDialog.getOpenFileName(caption=strings._('choose_file'), options=QtGui.QFileDialog.ReadOnly) - if not filename: - return False, False - - filename = str(unicode(filename).encode("utf-8")) - - # validate filename - if not os.path.isfile(filename): - alert(strings._("not_a_file").format(filename), QtGui.QMessageBox.Warning) - return False, False - - filename = os.path.abspath(filename) - basename = os.path.basename(filename) - return filename, basename - def main(): strings.load_strings() @@ -307,23 +305,37 @@ def main(): parser.add_argument('--local-only', action='store_true', dest='local_only', help=strings._("help_local_only")) parser.add_argument('--stay-open', action='store_true', dest='stay_open', help=strings._("help_stay_open")) parser.add_argument('--debug', action='store_true', dest='debug', help=strings._("help_debug")) - parser.add_argument('filename', nargs='?', help=strings._("help_filename")) + parser.add_argument('--filenames', metavar='filenames', nargs='+', help=strings._('help_filename')) args = parser.parse_args() - filename = args.filename + filenames = args.filenames + if filenames: + for i in range(len(filenames)): + filenames[i] = os.path.abspath(filenames[i]) + local_only = bool(args.local_only) stay_open = bool(args.stay_open) debug = bool(args.debug) - web.set_stay_open(stay_open) + # validation + if filenames: + valid = True + for filename in filenames: + if not os.path.exists(filename): + alert(strings._("not_a_file").format(filename)) + valid = False + if not valid: + sys.exit() # create the onionshare icon global window_icon - window_icon = QtGui.QIcon("{0}/static/logo.png".format(get_onionshare_gui_dir())) + window_icon = QtGui.QIcon("{0}/static/logo.png".format(common.onionshare_gui_dir)) # start the onionshare app - try: - app = onionshare.OnionShare(debug, local_only, stay_open) + web.set_stay_open(stay_open) + app = onionshare.OnionShare(debug, local_only, stay_open) + + """try: app.start_hidden_service(gui=True) except onionshare.NoTor as e: alert(e.args[0], QtGui.QMessageBox.Warning) @@ -331,11 +343,7 @@ def main(): except onionshare.TailsError as e: alert(e.args[0], QtGui.QMessageBox.Warning) sys.exit() - - # select file to share - filename, basename = select_file(filename) - if not filename: - return + """ # clean up when app quits def shutdown(): @@ -343,7 +351,8 @@ def main(): qtapp.connect(qtapp, QtCore.SIGNAL("aboutToQuit()"), shutdown) # launch the gui - gui = OnionShareGui(app, filename, basename) + gui = OnionShareGui(app, filenames) + gui.start_send() # all done sys.exit(qtapp.exec_()) diff --git a/setup/onionshare-osx.spec b/setup/onionshare-osx.spec index d0ee7631..8ba11b71 100644 --- a/setup/onionshare-osx.spec +++ b/setup/onionshare-osx.spec @@ -9,6 +9,7 @@ a.datas += [ ('onionshare/index.html', 'onionshare/index.html', 'DATA'), ('onionshare/404.html', 'onionshare/404.html', 'DATA'), ('onionshare_gui/logo.png', 'onionshare_gui/logo.png', 'DATA'), + ('onionshare_gui/drop_files.png', 'onionshare_gui/drop_files.png', 'DATA'), ] pyz = PYZ(a.pure) exe = EXE(pyz, diff --git a/setup/onionshare.nsi b/setup/onionshare.nsi index 842ac803..ceab6a48 100644 --- a/setup/onionshare.nsi +++ b/setup/onionshare.nsi @@ -59,6 +59,7 @@ Section "install" File "${BINPATH}\onionshare_gui\__init__.py" File "${BINPATH}\onionshare_gui\__init__.pyc" File "${BINPATH}\onionshare_gui\logo.png" + File "${BINPATH}\onionshare_gui\drop_files.png" # dependencies SetOutPath $INSTDIR @@ -162,6 +163,7 @@ Section "uninstall" Delete "$INSTDIR\onionshare_gui\__init__.py" Delete "$INSTDIR\onionshare_gui\__init__.pyc" Delete "$INSTDIR\onionshare_gui\logo.png" + Delete "$INSTDIR\onionshare_gui\drop_files.png" Delete "$INSTDIR\onionshare_gui\onionshare_gui.py" Delete "$INSTDIR\onionshare_gui\onionshare_gui.pyc" Delete "$INSTDIR\qt4_plugins\accessible\qtaccessiblewidgets4.dll"