Commit cc8c1f66 authored by Danial Behzadi's avatar Danial Behzadi
Browse files

updated user interface

parent 963edf47
#!/usr/bin/python3
# Released under GPLv3+ License
# Danial Behzadi<dani.behzi@ubuntu.com>, 2020
import gi
import os
import re
gi.require_versions({'Notify': '0.7'})
from gi.repository import Gio, GLib, Notify
from subprocess import PIPE, Popen
from . import config
from . import functions
from . import ui
command = "tractor "
_ = config._
app_name = config.app_name
def add(name, function, app):
action = Gio.SimpleAction.new(name, None)
action.connect('activate', function, app)
app.add_action(action)
def do_startup(app):
add('preferences', on_preferences, app)
add('about', on_about, app)
add('quit', on_quit, app)
add('connect', on_connect, app)
add('new_id', on_new_id, app)
add('set_proxy', on_set_proxy, app)
add('save', on_save, app)
def on_preferences(action, params, app):
if not app.prefs:
prefs_window = ui.get('PreferencesWindow')
prefs_window.set_transient_for(app.window)
app.prefs = prefs_window
app.prefs.connect('delete-event', functions.hide_window)
response = app.prefs.show()
def on_about(action, param, app):
if not app.about:
about_dialog = ui.get('AboutDialog')
about_dialog.set_transient_for(app.window)
app.about = about_dialog
response = app.about.run()
if -6 < response < -3:
app.about.hide()
def on_quit(action, param, app):
app.quit()
def on_connect(action, param, app):
button = ui.get('ConnectButton')
button.set_sensitive(False)
menu_button = ui.get('ActionMenuButton')
menu_button.set_sensitive(False)
main_image = ui.get('MainImage')
main_image.hide()
app.spinner = ui.get('Spinner')
app.spinner.start()
app.spinner.show()
main_label = ui.get('MainLabel')
if functions.is_running():
text_stopping = _("Disconnecting…")
main_label.set_label(text_stopping)
action = 'stop'
else:
text_starting = _("Connecting…")
main_label.set_label(text_starting)
action = 'start'
app.task = Popen(command + action, stdout=PIPE, shell=True, preexec_fn=os.setsid)
app.io_id = GLib.io_add_watch(app.task.stdout, GLib.IO_IN, functions.set_progress)
GLib.io_add_watch(app.task.stdout, GLib.IO_HUP, functions.thread_finished, button, app)
def on_new_id(action, param, app):
if functions.is_running():
newid = Popen(command + 'newid', stdout=PIPE, shell=True)
newid.wait()
notif = Notify.Notification.new(app_name, _("You have a new identity!"))
else:
notif = Notify.Notification.new(app_name, _("Tractor is not running!"))
notif.set_timeout(Notify.EXPIRES_DEFAULT)
notif.show()
def on_set_proxy(action, param, app):
checkbox = ui.get('SetProxyItem')
if checkbox.get_active():
action = 'set'
else:
action = 'unset'
task = Popen(command + action, stdout=PIPE, shell=True)
task.wait()
def on_save(action, param, app):
textview = ui.get('TextO4Bridge')
buff = textview.get_buffer()
text = buff.get_text(buff.get_start_iter(), buff.get_end_iter(), 0)
regex = re.compile(r'^( )*([Bb][Rr][Ii][Dd][Gg][Ee])?( )*', re.MULTILINE)
get_bridges = Popen(command + "bridgesfile", stdout=PIPE, shell=True)
bridges = get_bridges.stdout.read()
bridges_file = bridges.decode("utf-8").strip('\n')
if text == regex.sub('Bridge ', text):
with open(bridges_file, 'w') as file:
file.write(text)
else:
dialog = ui.get('O4ErrorDialog')
response = dialog.run()
if -6 < response < -3:
dialog.hide()
......@@ -4,9 +4,10 @@
import gi
gi.require_versions({'Gdk': '3.0', 'Gtk': '3.0'})
from gi.repository import Gdk, Gio, Gtk
from gi.repository import Gdk, Gtk
from sys import argv
from . import ui
from . import actions
class AppWindow(Gtk.ApplicationWindow):#TODO: Use Handy.ApplicationWindow
def __init__(self, *args, **kwargs):
......@@ -41,18 +42,7 @@ class Application(Gtk.Application):
def do_startup(self):
Gtk.Application.do_startup(self)
action = Gio.SimpleAction.new('preferences', None)
action.connect('activate', self.on_preferences)
self.add_action(action)
action = Gio.SimpleAction.new('about', None)
action.connect('activate', self.on_about)
self.add_action(action)
action = Gio.SimpleAction.new("quit", None)
action.connect('activate', self.on_quit)
self.add_action(action)
actions.do_startup(self)
def do_activate(self):
if not self.window:
......@@ -60,30 +50,6 @@ class Application(Gtk.Application):
self.window = window
self.window.present()
def on_preferences(self, action, param):
if not self.prefs:
prefs_window = ui.get('PreferencesWindow')
prefs_window.set_transient_for(self.window)
self.prefs = prefs_window
self.prefs.connect('delete-event', self.hide_window)
response = self.prefs.show()
def hide_window(self, window, data):
window.hide()
return True
def on_about(self, action, param):
if not self.about:
about_dialog = ui.get('AboutDialog')
about_dialog.set_transient_for(self.window)
self.about = about_dialog
response = self.about.run()
if -6 < response < -3:
self.about.hide()
def on_quit(self, action, param):
self.quit()
def main():
ui.initialize_builder()
......
......@@ -19,7 +19,7 @@ gettext.textdomain('messages')
_ = gettext.gettext
app_name = _("Carburetor")
app_comment = _("Settings for Tractor")
version = "3.0" #TODO: get from changelog
version = "3.1" #TODO: get from changelog
GLib.set_application_name(app_name)
Notify.init(app_name)
......
#!/usr/bin/python3
# Released under GPLv3+ License
# Danial Behzadi<dani.behzi@ubuntu.com>, 2020
import gi
import re
gi.require_versions({'Notify': '0.7'})
from gi.repository import Gio, GLib, Gtk, Notify
from distutils.util import strtobool
from subprocess import PIPE, Popen
from . import config
from . import ui
command = "tractor "
dconf = config.dconf
_ = config._
app_name = config.app_name
def is_running():
check = Popen(command + 'isrunning', stdout=PIPE, shell=True)
output = strtobool(check.stdout.read().decode('utf-8').strip('\n'))
return output
def set_progress (stdout, condition):
try:
line = stdout.readline().decode('utf-8')
label = ui.get('MainLabel')
valid = re.compile(r'.*\[notice\] ')
if 'notice' in line:
notice = valid.sub('', line)[:-5]
label.set_label(notice)
except ValueError:
pass
return True
def thread_finished (stdout, condition, button, app):
GLib.source_remove(app.io_id)
stdout.close()
app.spinner.stop()
app.spinner.hide()
set_run_status(app)
button.set_sensitive(True)
notify(app)
return False
def notify(app):
notif = Notify.Notification.new(app_name, app.notification)
notif.set_timeout(Notify.EXPIRES_DEFAULT)
notif.show()
def set_to_stopped(app):
image = ui.get('MainImage')
image.set_from_icon_name('process-stop', Gtk.IconSize.DIALOG)
image.show()
label = ui.get('MainLabel')
label.set_label(_("Stopped"))
button = ui.get('ConnectButton')
text_start = _("_Connect")
button.set_label(text_start)
button = ui.get('ActionMenuButton')
button.set_sensitive(False)
if app:
app.notification = _("Tractor is stopped")
def set_to_running(app):
image = ui.get('MainImage')
image.set_from_icon_name('security-high', Gtk.IconSize.DIALOG)
image.show()
label = ui.get('MainLabel')
label.set_label(_("Running"))
button = ui.get('ConnectButton')
text_stop = _("_Disconnect")
button.set_label(text_stop)
button = ui.get('ActionMenuButton')
button.set_sensitive(True)
if app:
app.notification = _("Tractor is running")
def set_run_status(app):
if is_running():
set_to_running(app)
else:
set_to_stopped(app)
def hide_window(window, data):
window.hide()
return True
def is_proxy_set():
proxy = Gio.Settings.new('org.gnome.system.proxy')
socks = Gio.Settings.new('org.gnome.system.proxy.socks')
if dconf.get_boolean('accept-connection'):
ip = '0.0.0.0'
else:
ip = '127.0.0.1'
proxy_mode = proxy.get_string('mode')
current_port = socks.get_int('port')
tractor_port = dconf.get_int('socks-port')
current_ip = socks.get_string('host')
if proxy_mode=='manual' and current_port==tractor_port and current_ip==ip:
return True
else:
return False
def set_checkbox():
checkbox = ui.get('SetProxyItem')
if is_proxy_set():
checkbox.props.active = True
else:
checkbox.props.active = False
......@@ -3,13 +3,11 @@
# Danial Behzadi<dani.behzi@ubuntu.com>, 2020
import gi
import os
import re
from distutils.util import strtobool
gi.require_versions({'Notify': '0.7'})
from gi.repository import Gio, GLib, Gtk, Notify
from gi.repository import Gio, Gtk
from subprocess import PIPE, Popen
from . import actions
from . import config
from . import functions
from . import ui
command = "tractor "
......@@ -20,136 +18,11 @@ version = config.version
class Handler:
def is_running(self):
check = Popen(command + 'isrunning', stdout=PIPE, shell=True)
output = strtobool(check.stdout.read().decode('utf-8').strip('\n'))
return output
def is_proxy_set(self):
proxy = Gio.Settings.new('org.gnome.system.proxy')
socks = Gio.Settings.new('org.gnome.system.proxy.socks')
if dconf.get_boolean('accept-connection'):
ip = '0.0.0.0'
else:
ip = '127.0.0.1'
proxy_mode = proxy.get_string('mode')
current_port = socks.get_int('port')
tractor_port = dconf.get_int('socks-port')
current_ip = socks.get_string('host')
if proxy_mode=='manual' and current_port==tractor_port and current_ip==ip:
return True
else:
return False
def set_to_stopped(self):
image = ui.get('MainImage')
image.set_from_icon_name('process-stop', Gtk.IconSize.DIALOG)
label = ui.get('MainLabel')
label.set_label(_("Stopped"))
button = ui.get('StartButton')
text_start = _("Start")
button.set_tooltip_text(text_start)
icon = Gtk.Image()
icon.set_from_icon_name('media-playback-start', Gtk.IconSize.MENU)
button.props.image = icon
self.notification = _("Tractor is stopped")
def set_to_running(self):
image = ui.get('MainImage')
image.set_from_icon_name('security-high', Gtk.IconSize.DIALOG)
label = ui.get('MainLabel')
label.set_label(_("Running"))
button = ui.get('StartButton')
text_stop = _("Stop")
button.set_tooltip_text(text_stop)
icon = Gtk.Image()
icon.set_from_icon_name('process-stop', Gtk.IconSize.MENU)
button.props.image = icon
self.notification = _("Tractor is running")
def set_run_status(self):
if self.is_running():
self.set_to_running()
else:
self.set_to_stopped()
def notify(self):
notif = Notify.Notification.new(app_name, self.notification)
notif.set_timeout(Notify.EXPIRES_DEFAULT)
notif.show()
def on_MainBox_realize(self, box):
self.set_run_status()
def on_StartButton_clicked(self, button):
button.set_sensitive(False)
button.spinner = Gtk.Spinner()
button.props.image = button.spinner
button.spinner.start()
if self.is_running():
text_stopping = _("stopping…")
button.set_tooltip_text(text_stopping)
action = 'stop'
else:
text_starting = _("starting…")
button.set_tooltip_text(text_starting)
action = 'start'
self.task = Popen(command + action, stdout=PIPE, shell=True, preexec_fn=os.setsid)
self.io_id = GLib.io_add_watch(self.task.stdout, GLib.IO_IN, self.set_progress)
GLib.io_add_watch(self.task.stdout, GLib.IO_HUP, self.thread_finished, button)
def set_progress (self, stdout, condition):
try:
line = stdout.readline().decode('utf-8')
label = ui.get('MainLabel')
valid = re.compile(r'.*\[notice\] ')
if 'notice' in line:
notice = valid.sub('', line)[:-5]
label.set_label(notice)
except ValueError:
pass
return True
def thread_finished (self, stdout, condition, button):
GLib.source_remove(self.io_id)
stdout.close()
button.spinner.stop()
self.set_run_status()
button.set_sensitive(True)
self.notify()
return False
def on_SetButton_realize(self, button):
self.SetButton_set_label(button)
def on_SetButton_toggled(self, button):
if self.is_proxy_set():
action = 'unset'
else:
action = 'set'
task = Popen(command + action, stdout=PIPE, shell=True)
task.wait()
self.SetButton_set_label(button)
def SetButton_set_label(self, button):
#TODO now the button will always start as not active and toggles regardless of state, by uncommenting the set-active lines, if the proxy was set and you run the app, it toggles from inactive to active and toggles the button in a loop! I should think for a solution, but I should harry up and go out now. So I'll postpone it :D
if self.is_proxy_set():
text_set_button = _("Unset Proxy")
#button.set_active(True)
else:
text_set_button = _("Set Proxy")
#button.set_active(False)
button.set_tooltip_text(text_set_button)
def on_NewButton_clicked(self, button):
if self.is_running():
newid = Popen(command + 'newid', stdout=PIPE, shell=True)
newid.wait()
notif = Notify.Notification.new(app_name, _("You have a new identity!"))
else:
notif = Notify.Notification.new(app_name, _("Tractor is not running!"))
notif.set_timeout(Notify.EXPIRES_DEFAULT)
notif.show()
functions.set_run_status(None)
def on_SetProxyItem_realize(self, item):
functions.set_checkbox()
def on_ActionExitNode_realize(self, combo):
nodes = {_("Austria"): "au",
......@@ -215,22 +88,8 @@ class Handler:
text = file.read()
buff.set_text(str(text))
def on_ButtonO4Bridge_clicked(self, button):
textview = ui.get('TextO4Bridge')
buff = textview.get_buffer()
text = buff.get_text(buff.get_start_iter(), buff.get_end_iter(), 0)
regex = re.compile(r'^( )*([Bb][Rr][Ii][Dd][Gg][Ee])?( )*', re.MULTILINE)
get_bridges = Popen(command + "bridgesfile", stdout=PIPE, shell=True)
bridges = get_bridges.stdout.read()
bridges_file = bridges.decode("utf-8").strip('\n')
if text == regex.sub('Bridge ', text):
with open(bridges_file, 'w') as file:
file.write(text)
else:
dialog = ui.get('O4ErrorDialog')
response = dialog.run()
if -6 < response < -3:
dialog.hide()
def on_ButtonO4Bridge_clicked(self, button): #TODO: Move this to actions
actions.on_save(button, None, self)
def on_ActionBridgePlugin_realize(self, chooser):
def on_file_set(chooser):
......@@ -247,3 +106,4 @@ class Handler:
def on_AboutDialog_destroy(self, dialog):
dialog.hide()
......@@ -29,6 +29,36 @@ Author: Danial Behzadi <dani.behzi@ubuntu.com>
<!-- interface-description Settings for Tractor -->
<!-- interface-copyright 2019 - 2020 -->
<!-- interface-authors Danial Behzadi <dani.behzi@ubuntu.com> -->
<object class="GtkMenu" id="ActionMenu">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="NewIDItem">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="action-name">app.new_id</property>
<property name="label" translatable="yes">_New ID</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="SetProxyItem">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="action-name">app.set_proxy</property>
<property name="label" translatable="yes">_Set proxy on system</property>
<property name="use-underline">True</property>
<signal name="realize" handler="on_SetProxyItem_realize" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="CheckConnectionItem">
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Check connection</property>
<property name="use-underline">True</property>
</object>
</child>
</object>
<object class="GtkScrolledWindow" id="MainContainer">
<property name="visible">True</property>
<property name="can-focus">True</property>
......@@ -51,11 +81,32 @@ Author: Danial Behzadi <dani.behzi@ubuntu.com>
<property name="orientation">vertical</property>
<signal name="realize" handler="on_MainBox_realize" swapped="no"/>
<child>
<object class="GtkImage" id="MainImage">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">security-low</property>
<property name="icon_size">6</property>
<child>
<object class="GtkImage" id="MainImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">process-stop</property>
<property name="icon_size">6</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSpinner" id="Spinner">
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
......@@ -68,6 +119,8 @@ Author: Danial Behzadi <dani.behzi@ubuntu.com>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Stopped</property>
<property name="wrap">True</property>
<property name="ellipsize">middle</property>
</object>
<packing>
<property name="expand">False</property>
......@@ -118,21 +171,6 @@ Author: Danial Behzadi <dani.behzi@ubuntu.com>
<property name="can-focus">False</property>
<property name="icon-name">open-menu-symbolic</property>
</object>
<object class="GtkImage" id="NewIcon">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">view-refresh-symbolic</property>
</object>
<object class="GtkImage" id="SetIcon">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">network-transmit-receive-symbolic</property>
</object>
<object class="GtkImage" id="StartIcon">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">media-playback-start</property>
</object>
<object class="HdyHeaderBar" id="HeaderBar">
<property name="visible">True</property>
<property name="can-focus">False</property>
......@@ -140,39 +178,54 @@ Author: Danial Behzadi <dani.behzi@ubuntu.com>
<property name="subtitle" translatable="yes">Settings for Tractor</property>
<property name="show-close-button">True</property>
<child>
<object class="GtkButton" id="StartButton">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="image">StartIcon</property>
<signal name="clicked" handler="on_StartButton_clicked" swapped="no"/>
</object>
</child>
<child>
<object class="GtkToggleButton" id="SetButton">
<object class="GtkButtonBox" id="ActionButtonBox">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="image">SetIcon</property>
<signal name="realize" handler="on_SetButton_realize" swapped="no"/>
<signal name="toggled" handler="on_SetButton_toggled" swapped="no"/>
<property name="can-focus">False</property>
<property name="layout-style">expand</property>