diff --git a/onionshare_gui/gui.py b/onionshare_gui/gui.py deleted file mode 100644 index 270678c2..00000000 --- a/onionshare_gui/gui.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python - -import Tkinter as tk, tkFont, tkFileDialog -import sys, os, time -import onionshare -from Queue import Queue, Empty -from threading import Thread - -class OnionShareGUI(object): - def __init__(self): - self.root = tk.Tk() - - # prepare GUI - self.root.title('OnionShare') - self.root.resizable(0, 0) - self.create_widgets() - self.root.grid() - - # select file - if len(sys.argv) >= 2: - self.filename = sys.argv[1] - else: - self.filename = tkFileDialog.askopenfilename(title="Choose a file to share", parent=self.root) - self.basename = os.path.basename(self.filename) - self.root.title('OnionShare - {0}'.format(self.basename)) - - # todo: start onionshare here, and display web server logs in update() method - # this might be helpful: https://stackoverflow.com/questions/375427/non-blocking-read-on-a-subprocess-pipe-in-python - - # update regularly - self.update() - - def create_widgets(self): - self.pad = 10 - sys12 = tkFont.Font(family="system", size=12) - sys20 = tkFont.Font(family="system", size=20, weight="bold") - - # url - self.url_labelframe = tk.LabelFrame(text="Send this URL to your friend") - self.url_labelframe.pack() - self.url_text = tk.Text(self.url_labelframe, width=31, height=2, font=sys20) - self.url_text.config(state=tk.DISABLED) - self.url_text.pack(padx=self.pad, pady=self.pad) - self.url_labelframe.grid(padx=self.pad, pady=self.pad) - - # logs - self.logs_labelframe = tk.LabelFrame(text="Server logs") - self.logs_labelframe.pack() - self.logs_text = tk.Text(self.logs_labelframe, width=70, height=10, font=sys12) - self.logs_text.insert(tk.INSERT, "") - self.logs_text.config(state=tk.DISABLED) - self.logs_text.pack(padx=self.pad, pady=self.pad) - self.logs_labelframe.grid(padx=self.pad, pady=self.pad) - - # quit button - self.quit_button = tk.Button(self.root, text='Quit', command=self.root.quit) - self.quit_button.grid(padx=self.pad, pady=self.pad) - - def update(self): - self.root.after(500, self.update) - - def enqueue_output(self, out, queue): - for line in iter(out.readline, b''): - queue.put(line) - out.close() - -def main(): - app = OnionShareGUI() - app.root.mainloop() - -if __name__ == '__main__': - main() diff --git a/onionshare_gui/index.html b/onionshare_gui/index.html new file mode 100644 index 00000000..e7b5b84d --- /dev/null +++ b/onionshare_gui/index.html @@ -0,0 +1,34 @@ + + + + + + + +

filename.zip

+

+ + diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py new file mode 100644 index 00000000..098a33ad --- /dev/null +++ b/onionshare_gui/onionshare_gui.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +import onionshare, webgui +import signal, os, time, json + +class Global(object): + quit = False + @classmethod + def set_quit(cls, *args, **kwargs): + cls.quit = True + +def main(): + if not webgui.select_file(): + return + + webgui.start_gtk_thread() + browser, web_recv, web_send = webgui.sync_gtk_msg(webgui.launch_browser)(quit_function=Global.set_quit) + + last_second = time.time() + uptime_seconds = 1 + clicks = 0 + while not Global.quit: + + current_time = time.time() + again = False + msg = web_recv() + if msg: + msg = json.loads(msg) + again = True + + if msg == "got-a-click": + clicks += 1 + web_send('document.getElementById("messages").innerHTML = %s' % + to_json('%d clicks so far' % clicks)) + # If you are using jQuery, you can do this instead: + # web_send('$("#messages").text(%s)' % + # to_json('%d clicks so far' % clicks)) + + if current_time - last_second >= 1.0: + web_send('document.getElementById("uptime-value").innerHTML = %s' % + json.dumps('%d' % uptime_seconds)) + # If you are using jQuery, you can do this instead: + # web_send('$("#uptime-value").text(%s)' + # % to_json('%d' % uptime_seconds)) + uptime_seconds += 1 + last_second += 1.0 + + + if again: + pass + else: + time.sleep(0.1) + +def my_quit_wrapper(fun): + signal.signal(signal.SIGINT, Global.set_quit) + def fun2(*args, **kwargs): + try: + x = fun(*args, **kwargs) # equivalent to "apply" + finally: + kill_gtk_thread() + Global.set_quit() + return x + return fun2 + +if __name__ == '__main__': + main() diff --git a/onionshare_gui/webgui.py b/onionshare_gui/webgui.py new file mode 100644 index 00000000..c412da26 --- /dev/null +++ b/onionshare_gui/webgui.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python + +import time, Queue, thread, gtk, gobject, os, sys, webkit + +def select_file(): + global filename, basename + + # was a filename passed in as an argument? + if len(sys.argv) >= 2: + filename = sys.argv[1] + basename = os.path.basename(filename) + return True + + # choose a file + canceled = False + chooser = gtk.FileChooserDialog( + title="Choose a file to share", + action=gtk.FILE_CHOOSER_ACTION_OPEN, + buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) + response = chooser.run() + if response == gtk.RESPONSE_OK: + filename = chooser.get_filename() + basename = os.path.basename(filename) + elif response == gtk.RESPONSE_CANCEL: + canceled = True + chooser.destroy() + + return not canceled + +def async_gtk_msg(fun): + def worker((function, args, kwargs)): + apply(function, args, kwargs) + + def fun2(*args, **kwargs): + gobject.idle_add(worker, (fun, args, kwargs)) + + return fun2 + +def sync_gtk_msg(fun): + class NoResult: pass + + def worker((R, function, args, kwargs)): + R.result = apply(function, args, kwargs) + + def fun2(*args, **kwargs): + class R: result = NoResult + gobject.idle_add(callable=worker, user_data=(R, fun, args, kwargs)) + while R.result is NoResult: time.sleep(0.01) + return R.result + + return fun2 + +def launch_browser(quit_function=None, echo=True): + window = gtk.Window() + browser = webkit.WebView() + + box = gtk.VBox(homogeneous=False, spacing=0) + window.add(box) + + if quit_function is not None: + # file > quit menu + file_menu = gtk.Menu() + quit_item = gtk.MenuItem('Quit') + accel_group = gtk.AccelGroup() + quit_item.add_accelerator('activate', accel_group, ord('Q'), gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE) + window.add_accel_group(accel_group) + file_menu.append(quit_item) + quit_item.connect('activate', quit_function) + quit_item.show() + menu_bar = gtk.MenuBar() + menu_bar.show() + file_item = gtk.MenuItem('File') + file_item.show() + file_item.set_submenu(file_menu) + menu_bar.append(file_item) + + box.pack_start(menu_bar, expand=False, fill=True, padding=0) + + window.connect('destroy', quit_function) + + box.pack_start(browser, expand=True, fill=True, padding=0) + + window.set_default_size(400, 400) + window.show_all() + + message_queue = Queue.Queue() + + def title_changed(title): + if title != 'null': message_queue.put(title) + + def callback_wrapper(widget, frame, title): callback(title) + browser.connect('title-changed', callback_wrapper) + + browser.open('file://'+os.getcwd()+'/index.html') + + def web_recv(): + if message_queue.empty(): + return None + else: + msg = message_queue.get() + if echo: print '>>>', msg + return msg + + def web_send(msg): + if echo: print '<<<', msg + async_gtk_msg(browser.execute_script)(msg) + + return browser, web_recv, web_send + + +def start_gtk_thread(): + # Start GTK in its own thread: + gtk.gdk.threads_init() + thread.start_new_thread(gtk.main, ()) + +def kill_gtk_thread(): + async_gtk_msg(gtk.main_quit)() + +def main(): + if not select_file(): + return + + launch_browser()