2to3 conversion

This commit is contained in:
Tux 2023-07-19 22:46:19 +02:00
parent 1597fe4d04
commit 5ef3ee1179
22 changed files with 1184 additions and 1142 deletions

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
'''
Folding@Home Client Control (FAHControl)
Copyright (C) 2010-2020 foldingathome.org
@ -36,16 +36,17 @@ def set_proc_name(name):
libc.prctl(15, byref(buff), 0, 0, 0)
if sys.platform.startswith('linux'): set_proc_name('FAHControl')
if sys.platform.startswith('linux'):
set_proc_name('FAHControl')
# If present, remove the Launch Services -psn_xxx_xxx argument
if len(sys.argv) > 1 and sys.argv[1][:4] == '-psn':
del sys.argv[1]
parser = OptionParser(usage = 'Usage: %prog [options]')
parser = OptionParser(usage='Usage: %prog [options]')
parser.add_option('--exit', help = 'Tell the running application to exit',
action = 'store_true', dest = 'exit')
parser.add_option('--exit', help='Tell the running application to exit',
action='store_true', dest='exit')
options, args = parser.parse_args()
# Tell app to exit
@ -54,24 +55,29 @@ if options.exit:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(single_app_addr)
sock.send('EXIT')
if sock.recv(1024).strip() == 'OK': print ('Ok')
if sock.recv(1024).strip() == 'OK':
print('Ok')
except Exception as e:
pass
sys.exit(0)
# Add executable path to PATH
if getattr(sys, 'frozen', False): path = os.path.dirname(sys.executable)
else: path = os.path.dirname(__file__)
if getattr(sys, 'frozen', False):
path = os.path.dirname(sys.executable)
else:
path = os.path.dirname(__file__)
path = os.path.realpath(path)
os.environ['PATH'] = path + os.pathsep + os.environ['PATH']
# Load Glade
dir = os.path.dirname(inspect.getfile(inspect.currentframe()))
if not dir: dir = '.'
if not dir:
dir = '.'
glade = dir + '/fah/FAHControl.glade'
if os.path.exists(glade): app = FAHControl(glade)
if os.path.exists(glade):
app = FAHControl(glade)
else:
from fah.FAHControl_glade import glade_data
app = FAHControl(glade_data)
@ -79,4 +85,4 @@ else:
try:
app.run()
except Exception as e:
print (e)
print(e)

View File

@ -1,8 +1,8 @@
# Setup
import os
env = Environment(ENV = os.environ)
env = Environment(ENV=os.environ)
try:
env.Tool('config', toolpath = [os.environ.get('CBANG_HOME')])
env.Tool('config', toolpath=[os.environ.get('CBANG_HOME')])
except Exception as e:
raise Exception('CBANG_HOME not set?\n' + str(e))
@ -17,13 +17,14 @@ try:
except Exception as e:
print(e)
version = '0.0.0'
env.Replace(PACKAGE_VERSION = version)
env.Replace(PACKAGE_VERSION=version)
f = open('version.txt', 'w')
f.write(version)
f.close()
if env['PLATFORM'] != 'darwin': env['package_arch'] = 'noarch'
if env['PLATFORM'] != 'darwin':
env['package_arch'] = 'noarch'
# Build
target_dir = None
@ -37,10 +38,11 @@ if env['PLATFORM'] == 'darwin':
elif env['PLATFORM'] == 'win32' or int(env.get('cross_mingw', 0)):
env['RUN_DISTUTILSOPTS'] = 'build'
target_dir = 'gui'
target_pat = '' # Not packaged here
target_pat = '' # Not packaged here
elif env.GetPackageType() == 'deb':
env['RUN_DISTUTILSOPTS'] = ['--command-packages=stdeb.command', 'bdist_deb']
env['RUN_DISTUTILSOPTS'] = ['--command-packages=stdeb.command',
'bdist_deb']
target_dir = 'deb_dist'
target_pat = 'deb_dist/fahcontrol_%s-*.deb' % version
@ -67,38 +69,39 @@ if env['PLATFORM'] == 'darwin' or env.GetPackageType() == 'rpm':
pkg = env.Packager(
'FAHControl',
version = version,
maintainer = 'Joseph Coffland <joseph@cauldrondevelopment.com>',
vendor = 'Folding@home',
url = 'https://foldingathome.org/',
license = 'LICENSE.txt',
bug_url = 'https://apps.foldingathome.org/bugs/',
summary = 'Folding@home Control',
description = \
'Control and monitor local and remote Folding@home clients',
prefix = '/usr',
version=version,
maintainer='Joseph Coffland <joseph@cauldrondevelopment.com>',
vendor='Folding@home',
url='https://foldingathome.org/',
license='LICENSE.txt',
bug_url='https://apps.foldingathome.org/bugs/',
summary='Folding@home Control',
description='Control and monitor local \
and remote Folding@home clients',
prefix='/usr',
documents = ['README.md', 'CHANGELOG.md', 'LICENSE.txt'],
desktop_menu = ['FAHControl.desktop'],
icons = ['images/FAHControl.png'],
documents=['README.md', 'CHANGELOG.md', 'LICENSE.txt'],
desktop_menu=['FAHControl.desktop'],
icons=['images/FAHControl.png'],
rpm_license = 'GPL v3+',
rpm_group = 'Applications/Internet',
rpm_requires = 'python, pygtk2',
rpm_build = 'rpm/build',
rpm_filelist = 'filelist.txt',
rpm_license='GPL v3+',
rpm_group='Applications/Internet',
rpm_requires='python, pygtk2',
rpm_build='rpm/build',
rpm_filelist='filelist.txt',
pkg_id = 'org.foldingathome.fahcontrol.pkg',
pkg_resources = 'osx/Resources',
pkg_apps = [['dist/FAHControl.app', 'Folding@home/FAHControl.app']],
pkg_scripts = 'osx/scripts',
pkg_target = '10.6',
pkg_distribution = 'osx/distribution.xml',
pkg_id='org.foldingathome.fahcontrol.pkg',
pkg_resources='osx/Resources',
pkg_apps=[['dist/FAHControl.app', 'Folding@home/FAHControl.app']],
pkg_scripts='osx/scripts',
pkg_target='10.6',
pkg_distribution='osx/distribution.xml',
)
AlwaysBuild(pkg)
env.Alias('package', pkg)
if gui is not None: Depends(pkg, gui)
if gui is not None:
Depends(pkg, gui)
else:
# Write package.txt
@ -107,10 +110,12 @@ else:
filename = str(Glob(target_pat)[0])
open(str(target[0]), 'w').write(filename)
bld = Builder(action = write_filename)
env.Append(BUILDERS = {'WriteFilename' : bld})
bld = Builder(action=write_filename)
env.Append(BUILDERS={'WriteFilename':
bld})
cmd = env.WriteFilename('package.txt', [])
AlwaysBuild(cmd)
if gui is not None: Depends(cmd, gui)
if gui is not None:
Depends(cmd, gui)
env.Alias('package', [cmd])

View File

@ -13,11 +13,14 @@ the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
from __future__ import print_function
import sys
import os
DEFAULT_VERSION = "0.6c11"
DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % \
sys.version[:3]
md5_data = {
'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
@ -64,12 +67,12 @@ md5_data = {
'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
}
import sys, os
try:
from hashlib import md5
except ImportError:
from md5 import md5
def _validate_md5(egg_name, data):
if egg_name in md5_data:
digest = md5(data).hexdigest()
@ -81,6 +84,7 @@ def _validate_md5(egg_name, data):
sys.exit(2)
return data
def use_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
download_delay=15
@ -96,25 +100,34 @@ def use_setuptools(
this routine will print a message to ``sys.stderr`` and raise SystemExit in
an attempt to abort the calling script.
"""
was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
was_imported = 'pkg_resources' in sys.modules \
or 'setuptools' in sys.modules
def do_download():
egg = download_setuptools(version, download_base, to_dir, download_delay)
egg = download_setuptools(version,
download_base,
to_dir,
download_delay)
sys.path.insert(0, egg)
import setuptools; setuptools.bootstrap_install_from = egg
import setuptools
setuptools.bootstrap_install_from = egg
try:
import pkg_resources
except ImportError:
return do_download()
return do_download()
try:
pkg_resources.require("setuptools>="+version); return
pkg_resources.require("setuptools>="+version)
return
except pkg_resources.VersionConflict as e:
if was_imported:
print (
"The required version of setuptools (>=%s) is not available, and\n"
" can't be installed while this script is running. Please install\n"
" a more recent version first, using 'easy_install -U setuptools'."
"\n\n(Currently using %r)"
% (version, e.args[0]), file=sys.stderr)
print("The required version of setuptools (>=%s) \
is not available, and\n"
" can't be installed while this script is running. \
Please install\n"
" a more recent version first, using \
'easy_install -U setuptools'."
"\n\n(Currently using %r)"
% (version, e.args[0]), file=sys.stderr)
sys.exit(2)
else:
del pkg_resources, sys.modules['pkg_resources'] # reload ok
@ -122,19 +135,26 @@ def use_setuptools(
except pkg_resources.DistributionNotFound:
return do_download()
def download_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
delay = 15
version=DEFAULT_VERSION,
download_base=DEFAULT_URL,
to_dir=os.curdir,
delay=15
):
"""Download setuptools from a specified location and return its filename
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end
with a '/'). `to_dir` is the directory where the egg will be downloaded.
`delay` is the number of seconds to pause before an actual download attempt.
`delay` is the number of seconds to pause before an actual download \
attempt.
"""
import urllib2, shutil
egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
import urllib.request
import urllib.error
import urllib.parse
import shutil
egg_name = "setuptools-%s-py%s.egg" % (version, sys.version[:3])
url = download_base + egg_name
saveto = os.path.join(to_dir, egg_name)
src = dst = None
@ -156,54 +176,26 @@ I will start the download in %d seconds.
and place it in this directory before rerunning this script.)
---------------------------------------------------------------------------""",
version, download_base, delay, url
); from time import sleep; sleep(delay)
version,
download_base,
delay, url)
from time import sleep
sleep(delay)
log.warn("Downloading %s", url)
src = urllib2.urlopen(url)
src = urllib.request.urlopen(url)
# Read/write all in one block, so we don't create a corrupt file
# if the download is interrupted.
data = _validate_md5(egg_name, src.read())
dst = open(saveto,"wb"); dst.write(data)
dst = open(saveto, "wb")
dst.write(data)
finally:
if src: src.close()
if dst: dst.close()
if src:
src.close()
if dst:
dst.close()
return os.path.realpath(saveto)
def main(argv, version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
try:
@ -212,7 +204,7 @@ def main(argv, version=DEFAULT_VERSION):
egg = None
try:
egg = download_setuptools(version, delay=0)
sys.path.insert(0,egg)
sys.path.insert(0, egg)
from setuptools.command.easy_install import main
return main(list(argv)+[egg]) # we're done here
finally:
@ -220,10 +212,11 @@ def main(argv, version=DEFAULT_VERSION):
os.unlink(egg)
else:
if setuptools.__version__ == '0.0.1':
print (
"You have an obsolete version of setuptools installed. Please\n"
" remove it from your system entirely before rerunning this script.",
file=sys.stderr)
print("You have an obsolete version of setuptools installed. \
Please\n"
" remove it from your system entirely before rerunning this \
script.",
file=sys.stderr)
sys.exit(2)
req = "setuptools>="+version
@ -236,14 +229,16 @@ def main(argv, version=DEFAULT_VERSION):
except ImportError:
from easy_install import main
main(list(argv)+[download_setuptools(delay=0)])
sys.exit(0) # try to force an exit
sys.exit(0) # try to force an exit
else:
if argv:
from setuptools.command.easy_install import main
main(argv)
else:
print ("Setuptools version %s or greater has been installed." % version)
print ('(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)')
print("Setuptools version %s or greater has been installed."
% version)
print('(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)')
def update_md5(filenames):
"""Update our built-in md5 registry"""
@ -252,37 +247,33 @@ def update_md5(filenames):
for name in filenames:
base = os.path.basename(name)
f = open(name,'rb')
f = open(name, 'rb')
md5_data[base] = md5(f.read()).hexdigest()
f.close()
data = [" %r: %r,\n" % it for it in md5_data.items()]
data = [" %r: %r,\n" % it for it in list(md5_data.items())]
data.sort()
repl = "".join(data)
import inspect
srcfile = inspect.getsourcefile(sys.modules[__name__])
f = open(srcfile, 'rb'); src = f.read(); f.close()
f = open(srcfile, 'rb')
src = f.read()
f.close()
match = re.search("\nmd5_data = {\n([^}]+)}", src)
if not match:
print ("Internal error!", file=sys.stderr)
print("Internal error!", file=sys.stderr)
sys.exit(2)
src = src[:match.start(1)] + repl + src[match.end(1):]
f = open(srcfile,'w')
f = open(srcfile, 'w')
f.write(src)
f.close()
if __name__=='__main__':
if len(sys.argv)>2 and sys.argv[1]=='--md5update':
if __name__ == '__main__':
if len(sys.argv) > 2 and sys.argv[1] == '--md5update':
update_md5(sys.argv[2:])
else:
main(sys.argv[1:])

View File

@ -1,23 +1,23 @@
################################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################
###############################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
###############################################################################
import traceback
import time
@ -40,7 +40,8 @@ debug = False
class Client:
def __init__(self, app, name, address, port, password):
if debug: print('Client.__init__()')
if debug:
print('Client.__init__()')
self.name = name
self.address = address
@ -54,12 +55,13 @@ class Client:
self.error_messages = set()
if not name: self.name = self.get_address()
if not name:
self.name = self.get_address()
# Option names
names = app.client_option_widgets.keys()
self.option_names = map(lambda name: name.replace('_', '-'), names)
self.option_names.append('power') # Folding power
names = list(app.client_option_widgets.keys())
self.option_names = [name.replace('_', '-') for name in names]
self.option_names.append('power') # Folding power
# Init commands
self.inactive_cmds = [
@ -82,45 +84,39 @@ class Client:
self.conn = Connection(self.address, self.port, self.password)
self.conn.set_init_commands(self.inactive_cmds)
# Class special functions
def __str__(self): return self.name
def __cmp__(self, other): return str.__cmp__(self.name, other.name)
def __hash__(self): return self.name.__hash__()
# Getters
@staticmethod
def make_address(address, port): return '%s:%d' % (address, port)
def get_address(self): return Client.make_address(self.address, self.port)
def get_password(self): return self.conn.password
def get_status(self):
status = self.conn.get_status()
if status == 'Online' and not self.is_updated() and self.selected:
return 'Updating'
return status
def get_selected_slot(self, app): return self.config.get_selected_slot(app)
# Setters
def set_address(self, address, port):
self.conn.address = self.address = address
self.conn.port = self.port = port
def set_password(self, password):
if self.conn.is_connected():
self.conn.queue_command('option password "%s"' % password)
self.conn.password = self.password = password
# State functions
def is_local(self):
return self.address == '127.0.0.1' and self.name == 'local'
def is_online(self): return self.conn.get_status() == 'Online'
def set_selected(self, selected):
@ -128,28 +124,25 @@ class Client:
if selected:
self.set_updated(False)
self.conn.set_init_commands(self.active_cmds)
else: self.conn.set_init_commands(self.inactive_cmds)
else:
self.conn.set_init_commands(self.inactive_cmds)
self.selected = selected
def set_updated(self, updated):
self.options_updated = updated
self.info_updated = updated
self.slots_updated = updated
self.units_updated = updated
def is_updated(self):
return self.options_updated and self.info_updated and \
self.slots_updated and self.units_updated
# Log functions
def refresh_log(self):
self.conn.queue_command('log-updates restart')
# GUI functions
def load_dialog(self, app):
app.client_entries['name'].set_text(self.name)
@ -169,48 +162,40 @@ class Client:
'address': self.get_address()}
return list(make_row(app.client_cols, keys))
def update_status_ui(self, app):
self.config.update_status_ui(app)
def reset_status_ui(self, app):
self.config.reset_status_ui(app)
# Slot control
def unpause(self, slot = ''):
def unpause(self, slot=''):
self.conn.queue_command('unpause %s' % str(slot))
def pause(self, slot = ''):
def pause(self, slot=''):
self.conn.queue_command('pause %s' % str(slot))
def finish(self, slot = ''):
def finish(self, slot=''):
self.conn.queue_command('finish %s' % str(slot))
def on_idle(self, slot = ''):
def on_idle(self, slot=''):
self.conn.queue_command('on_idle %s' % str(slot))
def always_on(self, slot = ''):
def always_on(self, slot=''):
self.conn.queue_command('always_on %s' % str(slot))
# Save functions
def save(self, db):
db.insert('clients', name = self.name, address = self.address,
port = self.port, password = self.password)
db.insert('clients', name=self.name, address=self.address,
port=self.port, password=self.password)
def save_options(self, options):
if not options: return
if not options:
return
cmd = 'options'
for name, value in options.items():
for name, value in list(options.items()):
cmd += ' ' + name
if name[-1] != '!':
cmd += "='%s'" % value.encode('string_escape')
@ -218,11 +203,11 @@ class Client:
cmd += ' %s *' % ' '.join(self.option_names)
self.conn.queue_command(cmd)
self.options_updated = False # Reload
self.options_updated = False # Reload
def save_slots(self, slots):
if not slots or slots == ([], [], []): return
if not slots or slots == ([], [], []):
return
deleted, added, modified = slots
@ -233,23 +218,25 @@ class Client:
# Modified
for id, type, options in modified:
cmd = 'slot-modify %d %s' % (id, type)
for name, value in options.items():
if name[-1] == '!': cmd += ' ' + name
else: cmd += ' %s="%s"' % (name, value)
for name, value in list(options.items()):
if name[-1] == '!':
cmd += ' ' + name
else:
cmd += ' %s="%s"' % (name, value)
self.conn.queue_command(cmd)
# Added
for type, options in added:
cmd = 'slot-add %s' % type
for name, value in options.items():
for name, value in list(options.items()):
cmd += ' %s="%s"' % (name, value)
self.conn.queue_command(cmd)
self.slots_updated = False # Reload
self.slots_updated = False # Reload
def save_config(self, options, slots):
if not options and slots == ([], [], []): return
if not options and slots == ([], [], []):
return
self.save_options(options)
self.save_slots(slots)
@ -257,14 +244,12 @@ class Client:
self.conn.queue_command('save')
self.conn.queue_command('updates reset')
def set_power(self, power):
power = power.lower().replace(' ', '_')
if power != self.power:
self.power = power
self.conn.queue_command('option power ' + power)
# Message processing
def process_options(self, app, data):
self.options_updated = True
@ -274,26 +259,26 @@ class Client:
self.config.update_user_info(app)
self.config.update_ppd(app, self.ppd)
def process_info(self, app, data):
self.info_updated = True
self.config.info = data
if self.selected: self.config.update_info(app)
if self.selected:
self.config.update_info(app)
def process_slots(self, app, data):
self.slots_updated = True
slots = []
for slot in data: slots.append(SlotConfig(**slot))
for slot in data:
slots.append(SlotConfig(**slot))
self.config.slots = slots
if self.selected: self.config.update_status_ui(app)
if self.selected:
self.config.update_status_ui(app)
def process_units(self, app, data):
self.units_updated = True
self.config.update_queue(data)
if self.selected: self.config.update_status_ui(app)
if self.selected:
self.config.update_status_ui(app)
def process_log_update(self, app, data):
# Remove color codes
@ -301,54 +286,64 @@ class Client:
self.config.log_add(app, data)
def process_log_restart(self, app, data):
self.config.log_clear(app)
self.process_log_update(app, data)
def process_ppd(self, app, ppd):
self.ppd = ppd
if self.selected: self.config.update_ppd(app, ppd)
if self.selected:
self.config.update_ppd(app, ppd)
def process_error(self, app, data):
msg = 'On client "%s" %s:%d: %s' % (
self.name, self.address, self.port, data)
# Only popup dialog once for each error
if not msg in self.error_messages:
if msg not in self.error_messages:
self.error_messages.add(msg)
app.error(msg)
else: print('ERROR: %s' % msg)
else:
print(('ERROR: %s' % msg))
app.set_status(msg)
def process_configured(self, app, configured):
if configured: return
if configured:
return
app.configure_dialog.show()
def process_message(self, app, type, data):
if debug: print('message: %s %s' % (type, data))
if debug:
print(('message: %s %s' % (type, data)))
if type == 'heartbeat': return
if type == 'ppd': self.process_ppd(app, data)
if type == 'heartbeat':
return
if type == 'ppd':
self.process_ppd(app, data)
if not self.selected: return
if not self.selected:
return
if type == 'options': self.process_options(app, data)
elif type == 'info': self.process_info(app, data)
elif type == 'slots': self.process_slots(app, data)
elif type == 'units': self.process_units(app, data)
elif type == 'log-restart': self.process_log_restart(app, data)
elif type == 'log-update': self.process_log_update(app, data)
elif type == 'error': self.process_error(app, data)
elif type == 'configured': self.process_configured(app, data)
if type == 'options':
self.process_options(app, data)
elif type == 'info':
self.process_info(app, data)
elif type == 'slots':
self.process_slots(app, data)
elif type == 'units':
self.process_units(app, data)
elif type == 'log-restart':
self.process_log_restart(app, data)
elif type == 'log-update':
self.process_log_update(app, data)
elif type == 'error':
self.process_error(app, data)
elif type == 'configured':
self.process_configured(app, data)
# Ignore other message types
def update(self, app):
prevStatus = self.get_status()
@ -385,18 +380,19 @@ class Client:
iter = list.iter_next(iter)
if not self.is_online(): self.set_updated(False)
if not self.is_online():
self.set_updated(False)
# Update client status label
if self.selected: app.update_client_status()
if self.selected:
app.update_client_status()
def reconnect(self):
self.conn.close()
def close(self):
if debug: print('Client closing')
if debug:
print('Client closing')
# Avoid broken pipe on OSX
if sys.platform == 'darwin':

View File

@ -1,23 +1,23 @@
################################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################
###############################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
###############################################################################
import sys
import gtk
@ -41,18 +41,20 @@ def get_option_mods(old_options, new_options):
changes[name + '!'] = None
# Added and modified
for name, value in new_options.items():
for name, value in list(new_options.items()):
if name not in old_options or value != old_options[name]:
changes[name] = value
return changes
def get_buffer_text(buffer):
return buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())
def get_model_column(model, iter, column):
if iter is not None: return model.get_value(iter, column)
if iter is not None:
return model.get_value(iter, column)
def get_selected_tree_column(tree, column):
@ -84,39 +86,38 @@ class ClientConfig:
self.log_filter_re = None
self.updating = False
def get(self, name):
if name in self.options: return self.options[name]
if name in self.options:
return self.options[name]
def set(self, name, value): self.options[name] = value
def have(self, name):
return name in self.options and self.options[name] is not None
def get_prcg(self, row):
return '%s (%s, %s, %s)' % (
row['project'], row['run'], row['clone'], row['gen'])
def update_power(self, app):
power = self.get('power').lower()
for i in range(len(app.folding_power_levels)):
if power == app.folding_power_levels[i].lower():
app.folding_power.set_value(i)
def update_ppd(self, app, ppd):
if ppd: s = '%d' % int(ppd)
else: s = 'Unknown'
if ppd:
s = '%d' % int(ppd)
else:
s = 'Unknown'
app.client_ppd.set_text(s)
def update_queue(self, queue):
self.queue = queue
self.queue_map = {}
for values in self.queue:
self.queue_map[values['id']] = values
def update_user_info(self, app):
# User
user = self.options['user']
@ -126,22 +127,20 @@ class ClientConfig:
team = self.options['team']
app.team_info.set_label(team)
def reset_user_info(self, app):
app.donor_info.set_label('')
app.team_info.set_label('')
def get_selected_queue_entry(self, app):
return get_selected_tree_column(app.queue_tree, 1)
def get_selected_slot(self, app):
id = get_selected_tree_column(app.slot_status_tree, 0)
if id is not None:
id = int(id)
for slot in self.slots:
if slot.id == id: return slot
if slot.id == id:
return slot
def update_queue_ui(self, app):
if not self.queue:
@ -171,19 +170,25 @@ class ClientConfig:
progress = values['percentdone']
percent = float(progress[:-1])
eta = values['eta']
if eta == '0.00 secs': eta = 'Unknown'
if eta == '0.00 secs':
eta = 'Unknown'
credit = values['creditestimate']
if float(credit) == 0: credit = 'Unknown'
if float(credit) == 0:
credit = 'Unknown'
prcg = self.get_prcg(values)
iter = app.queue_list.append([unit_id, queue_id, status, color,
progress, percent, eta, credit, prcg])
progress, percent, eta, credit,
prcg])
if queue_id == selected: selected_row = iter
if queue_id == log_filter_selected: log_filter_row = iter
if queue_id == selected:
selected_row = iter
if queue_id == log_filter_selected:
log_filter_row = iter
# Select the first item if nothing is selected
if selected_row is None: selected_row = app.queue_list.get_iter_first()
if selected_row is None:
selected_row = app.queue_list.get_iter_first()
if log_filter_row is None:
log_filter_row = app.queue_list.get_iter_first()
@ -191,7 +196,6 @@ class ClientConfig:
app.queue_tree.get_selection().select_iter(selected_row)
app.log_unit.set_active_iter(log_filter_row)
def update_work_unit_info(self, app):
if not self.queue:
self.reset_work_unit_info(app)
@ -199,15 +203,19 @@ class ClientConfig:
# Get selected queue entry
selected = self.get_selected_queue_entry(app)
if selected is None: return
if selected is None:
return
entry = self.queue_map[selected]
# Load info
for name, value in entry.items():
for name, value in list(entry.items()):
if name in app.queue_widgets:
if (name in ['basecredit', 'creditestimate', 'ppd'] and \
float(value) == 0) or value == '<invalid>' or \
value == '0.00 secs': value = 'Unknown'
if (name in ['basecredit',
'creditestimate',
'ppd'] and float(value) == 0) \
or value == '<invalid>' \
or value == '0.00 secs':
value = 'Unknown'
widget = app.queue_widgets[name]
set_widget_str_value(widget, value)
@ -229,24 +237,27 @@ class ClientConfig:
entry['project'], entry['run'], entry['clone'], entry['gen'])
set_widget_str_value(app.queue_widgets['prcg'], prcg)
def select_slot(self, app):
# Get selected slot
slot = self.get_selected_slot(app)
if slot is None: return
if slot is None:
return
# Get associated queue ID
first_id = None
first_running_id = None
for entry in self.queue:
if int(entry['slot']) == slot.id:
if first_id is None: first_id = entry['unit']
if first_id is None:
first_id = entry['unit']
if entry['state'].upper() in ['RUNNING', 'FINISHING'] and \
first_running_id is None:
first_running_id = entry['unit']
if first_running_id is not None: unit_id = first_running_id
else: unit_id = first_id
if first_running_id is not None:
unit_id = first_running_id
else:
unit_id = first_id
if unit_id is not None:
# Find unit_id in the queue list entry and select row
@ -260,15 +271,16 @@ class ClientConfig:
# Update the UI
self.update_work_unit_info(app)
else: app.queue_tree.get_selection().unselect_all()
else:
app.queue_tree.get_selection().unselect_all()
def select_queue_slot(self, app):
# Get unit ID of selected queue entry
selected = self.get_selected_queue_entry(app)
if selected is None: return
if selected is None:
return
# Get associated slot ID
# Get associated slot ID
entry = self.queue_map[selected]
slot = int(entry['slot'])
@ -284,17 +296,16 @@ class ClientConfig:
# Update the UI
self.update_work_unit_info(app)
def reset_work_unit_info(self, app):
for widget in app.queue_widgets.values():
for widget in list(app.queue_widgets.values()):
set_widget_str_value(widget, None)
def update_info(self, app):
port = app.info
# Clear
for child in port.get_children(): port.remove(child)
for child in port.get_children():
port.remove(child)
# Alignment
align = gtk.Alignment(0, 0, 1, 1)
@ -327,7 +338,8 @@ class ClientConfig:
row = 0
for name, value in category:
if not value: continue
if not value:
continue
# Name
label = gtk.Label('<b>%s</b>' % name)
@ -341,30 +353,30 @@ class ClientConfig:
label.set_relief(gtk.RELIEF_NONE)
label.set_property('can-focus', False)
else: label = gtk.Label(value)
else:
label = gtk.Label(value)
label.set_alignment(0, 0.5)
label.modify_font(app.mono_font)
table.attach(label, 1, 2, row, row + 1, yoptions = gtk.FILL)
table.attach(label, 1, 2, row, row + 1, yoptions=gtk.FILL)
row += 1
port.realize()
port.show_all()
def update_options(self, app):
used = set()
for name, widget in app.client_option_widgets.items():
for name, widget in list(app.client_option_widgets.items()):
name = name.replace('_', '-')
used.add(name)
try:
set_widget_str_value(widget, self.options[name])
except Exception as e: # Don't let one bad widget kill everything
print('WARNING: failed to set widget "%s": %s' % (name, e))
except Exception as e: # Don't let one bad widget kill everything
print(('WARNING: failed to set widget "%s": %s' % (name, e)))
# Setup passkey and password entries
app.passkey_validator.set_good()
@ -384,9 +396,12 @@ class ClientConfig:
if self.have('proxy'):
proxy = self.get('proxy')
if ':' in proxy: proxy_addr, proxy_port = proxy.split(':', 1)
else: proxy_addr, proxy_port = proxy, '8080'
set_widget_str_value(app.client_option_widgets['proxy'], proxy_addr)
if ':' in proxy:
proxy_addr, proxy_port = proxy.split(':', 1)
else:
proxy_addr, proxy_port = proxy, '8080'
set_widget_str_value(app.client_option_widgets['proxy'],
proxy_addr)
set_widget_str_value(app.proxy_port, proxy_port)
# Set core priority radio button
@ -401,19 +416,20 @@ class ClientConfig:
used.add('extra-core-args')
args = self.get('extra-core-args').split()
for arg in args: app.core_option_list.append([arg])
for arg in args:
app.core_option_list.append([arg])
# Remaining options
app.option_list.clear()
for name, value in self.options.items():
for name, value in list(self.options.items()):
if name not in used:
app.option_list.append([name, value])
def update_status_slots(self, app):
# Save selection
selected = get_selected_tree_column(app.slot_status_tree, 0)
if selected is not None: selected = selected
if selected is not None:
selected = selected
selected_row = None
log_filter_selected = get_active_combo_column(app.log_slot, 0)
log_filter_row = None
@ -434,10 +450,15 @@ class ClientConfig:
status += ':' + slot.reason
status = get_span_markup(status, color)
description = slot.description.replace('"', '')
iter = app.slot_status_list.append((id, status, color, description))
iter = app.slot_status_list.append((id,
status,
color,
description))
if id == selected: selected_row = iter
if id == log_filter_selected: log_filter_row = iter
if id == selected:
selected_row = iter
if id == log_filter_selected:
log_filter_row = iter
# Selected the first item if nothing is selected
if selected_row is None:
@ -454,25 +475,22 @@ class ClientConfig:
if log_filter_row is not None:
app.log_slot.set_active_iter(log_filter_row)
def update_slots_ui(self, app):
app.slot_list.clear()
for slot in self.slots:
slot.add_to_ui(app)
def scroll_log_to_end(self, app):
if not app.log_follow.get_active(): return
if not app.log_follow.get_active():
return
mark = app.log.get_mark('end')
app.log.move_mark(mark, app.log.get_end_iter())
app.log_view.scroll_mark_onscreen(mark)
def log_clear(self, app):
app.log.set_text('')
self.log = []
def log_filter_str(self, app):
f = []
@ -491,19 +509,17 @@ class ClientConfig:
f.append(r'FS%s' % id)
if len(f):
f = map(lambda x: '.*(^|:)%s' % x, f)
return '(^\*)|(%s):' % ''.join(f)
f = ['.*(^|:)%s' % x for x in f]
return '(^\\*)|(%s):' % ''.join(f)
return None
def log_filter(self, line):
return self.log_filter_re is None or \
self.log_filter_re.match(line) is not None
def log_add_lines(self, app, lines):
filtered = filter(self.log_filter, lines)
filtered = list(filter(self.log_filter, lines))
if len(filtered):
text = '\n'.join(filtered)
@ -511,41 +527,42 @@ class ClientConfig:
app.log.insert(app.log.get_end_iter(), text + '\n')
self.scroll_log_to_end(app)
def log_add(self, app, text):
# TODO deal with split lines
lines = []
for line in text.split('\n'):
if not line: continue
if not line:
continue
lines.append(line)
self.log.append(line)
self.log_add_lines(app, lines)
def update_log(self, app):
if self.updating: return # Don't refilter during updates
if self.updating:
return # Don't refilter during updates
# Check if filter has changed
log_filter = self.log_filter_str(app)
if log_filter == self.last_log_filter: return
if log_filter == self.last_log_filter:
return
# Update filter
self.last_log_filter = log_filter
if log_filter is not None: self.log_filter_re = re.compile(log_filter)
else: self.log_filter_re = None
if log_filter is not None:
self.log_filter_re = re.compile(log_filter)
else:
self.log_filter_re = None
# Reload log
app.log.set_text('')
self.log_add_lines(app, self.log)
def update_status_ui(self, app):
self.update_queue_ui(app)
self.update_status_slots(app)
self.update_work_unit_info(app)
app.update_client_status() # TODO this should probably be moved here
app.update_client_status() # TODO this should probably be moved here
def reset_status_ui(self, app):
self.reset_work_unit_info(app)
@ -553,25 +570,25 @@ class ClientConfig:
app.slot_status_list.clear()
app.log.set_text('')
def get_running(self):
for unit in self.queue:
if unit['state'].upper() == 'RUNNING': return True
if unit['state'].upper() == 'RUNNING':
return True
return False
def get_option_changes(self, app):
used = set()
options = {}
used.add('power') # Don't set power here
used.add('power') # Don't set power here
# Proxy options
used.add('proxy')
proxy_addr = get_widget_str_value(app.client_option_widgets['proxy'])
proxy_port = get_widget_str_value(app.proxy_port)
proxy = '%s:%s' % (proxy_addr, proxy_port)
if self.get('proxy') != proxy: options['proxy'] = proxy
if self.get('proxy') != proxy:
options['proxy'] = proxy
# Core priority radio button
used.add('core-priority')
@ -586,77 +603,90 @@ class ClientConfig:
used.add('extra-core-args')
if self.have('extra-core-args'):
old_args = self.get('extra-core-args').split()
else: old_args = []
else:
old_args = []
new_args = []
def add_arg(model, path, iter, data):
new_args.append(model.get(iter, 0)[0])
app.core_option_list.foreach(add_arg, None)
if old_args != new_args:
if new_args: options['extra-core-args'] = ' '.join(new_args)
else: options['extra-core-args!'] = None
if new_args:
options['extra-core-args'] = ' '.join(new_args)
else:
options['extra-core-args!'] = None
# Extra options
def check_option(model, path, iter, data):
name, value = model.get(iter, 0, 1)
used.add(name)
if self.get(name) != value: options[name] = value
if self.get(name) != value:
options[name] = value
app.option_list.foreach(check_option, None)
# Main options
for name, widget in app.client_option_widgets.items():
for name, widget in list(app.client_option_widgets.items()):
name = name.replace('_', '-')
if name in used: continue
if name in used:
continue
value = self.get(name)
used.add(name)
try:
value = get_widget_str_value(widget)
old_value = self.get(name)
if value == '' and old_value is None: value = None
if value == '' and old_value is None:
value = None
if value != old_value:
if value is None: options[name + '!'] = None
else: options[name] = value
if value is None:
options[name + '!'] = None
else:
options[name] = value
except Exception as e: # Don't let one bad widget kill everything
print('WARNING: failed to save widget "%s": %s' % (name, e))
except Exception as e: # Don't let one bad widget kill everything
print(('WARNING: failed to save widget "%s": %s' % (name, e)))
# Removed options
for name in self.options:
if not name in used:
if name not in used:
options[name + '!'] = None
return options
def get_slot_changes(self, app):
# Get new slots
new_slots = []
def add_slot(model, path, iter, data = None):
def add_slot(model, path, iter, data=None):
new_slots.append(model.get(iter, 2)[0].slot)
app.slot_list.foreach(add_slot)
# Get old slot IDs
old_slot_map = {}
for slot in self.slots: old_slot_map[slot.id] = slot
for slot in self.slots:
old_slot_map[slot.id] = slot
# Get new slot IDs
new_slot_ids = set()
for slot in new_slots: new_slot_ids.add(slot.id)
for slot in new_slots:
new_slot_ids.add(slot.id)
# Find deleted slot IDs
deleted = []
for id in old_slot_map:
if id not in new_slot_ids: deleted.append(id)
if id not in new_slot_ids:
deleted.append(id)
# Find added and modified slots
added = []
modified = []
for slot in new_slots:
# Added
if slot.id == -1: added.append((slot.type, slot.options))
if slot.id == -1:
added.append((slot.type, slot.options))
else:
old_slot = old_slot_map[slot.id]
options = get_option_mods(old_slot.options, slot.options)
@ -665,11 +695,9 @@ class ClientConfig:
return (deleted, added, modified)
def get_changes(self, app):
return self.get_option_changes(app), self.get_slot_changes(app)
def has_changes(self, app):
options, slots = self.get_changes(app)
return options or slots != ([], [], [])

View File

@ -1,25 +1,25 @@
#!/usr/bin/env python2
################################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################
###############################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
###############################################################################
import socket
import select
@ -41,8 +41,11 @@ WSAEWOULDBLOCK = 10035
class Connection:
def __init__(self, address = 'localhost', port = 36330, password = None,
retry_rate = 5):
def __init__(self,
address='localhost',
port=36330,
password=None,
retry_rate=5):
self.address = address
self.port = int(port)
self.password = password
@ -52,44 +55,45 @@ class Connection:
self.socket = None
self.reset()
def set_init_commands(self, commands):
self.init_commands = commands
if self.is_connected():
map(self.queue_command, self.init_commands)
list(map(self.queue_command, self.init_commands))
def get_status(self):
if self.connected: return 'Online'
#if self.socket is None: return 'Offline'
if self.connected:
return 'Online'
# if self.socket is None: return 'Offline'
return 'Connecting'
def is_connected(self):
if self.socket is None: return False
if self.connected: return True
if self.socket is None:
return False
if self.connected:
return True
rlist, wlist, xlist = select.select([], [self.socket], [self.socket], 0)
rlist, wlist, xlist = select.select([],
[self.socket],
[self.socket],
0)
if len(wlist) != 0: self.connected = True
if len(wlist) != 0:
self.connected = True
elif len(xlist) != 0:
self.fail_reason = 'refused'
self.close()
return self.connected
def can_write(self):
rlist, wlist, xlist = select.select([], [self.socket], [], 0)
return len(wlist) != 0
def can_read(self):
rlist, wlist, xlist = select.select([self.socket], [], [], 0)
return len(rlist) != 0
def reset(self):
self.close()
self.messages = []
@ -99,9 +103,9 @@ class Connection:
self.last_message = 0
self.last_connect = 0
def open(self):
if debug: print('Connection.open()')
if debug:
print('Connection.open()')
self.reset()
self.last_connect = time.time()
@ -110,45 +114,47 @@ class Connection:
self.socket.setblocking(0)
err = self.socket.connect_ex((self.address, self.port))
if err != 0 and not err in [
errno.EINPROGRESS, errno.EWOULDBLOCK, WSAEWOULDBLOCK]:
if err != 0 and err not in [
errno.EINPROGRESS, errno.EWOULDBLOCK, WSAEWOULDBLOCK]:
self.fail_reason = 'connect'
raise Exception('Connection failed: ' + errno.errorcode[err])
if self.password: self.queue_command('auth "%s"' % self.password)
map(self.queue_command, self.init_commands)
if self.password:
self.queue_command('auth "%s"' % self.password)
list(map(self.queue_command, self.init_commands))
def close(self):
if debug: print('Connection.close()')
if debug:
print('Connection.close()')
if self.socket is not None:
try:
self.socket.shutdown(socket.SHUT_RDWR)
except: pass
except socket:
pass
try:
self.socket.close()
except: pass
except socket:
pass
self.socket = None
self.connected = False
def connection_lost(self):
print('Connection lost')
self.close()
self.fail_reason = 'closed'
raise Exception('Lost connection')
def connection_error(self, err, msg):
print('Connection Error: %d: %s' % (err, msg))
print(('Connection Error: %d: %s' % (err, msg)))
self.close()
if err == errno.ECONNREFUSED: self.fail_reason = 'refused'
if err == errno.ECONNREFUSED:
self.fail_reason = 'refused'
elif err in [errno.ETIMEDOUT, errno.ENETDOWN, errno.ENETUNREACH]:
self.fail_reason = 'connect'
else: self.fail_reason = 'error'
else:
self.fail_reason = 'error'
def read_some(self):
bytesRead = 0
@ -156,26 +162,30 @@ class Connection:
while True:
buffer = self.socket.recv(10 * 1024 * 1024)
if len(buffer):
#if debug: print 'BUFFER:', buffer
# if debug: print 'BUFFER:', buffer
self.readBuf += buffer
bytesRead += len(buffer)
else:
if bytesRead: return bytesRead
if bytesRead:
return bytesRead
self.connection_lost()
return 0
except socket.error as (err, msg):
except socket.error as xxx_todo_changeme:
# Error codes for nothing to read
(err, msg) = xxx_todo_changeme.args
# Error codes for nothing to read
if err not in [errno.EAGAIN, errno.EWOULDBLOCK, WSAEWOULDBLOCK]:
if bytesRead: return bytesRead
if bytesRead:
return bytesRead
self.connection_error(err, msg)
raise
return bytesRead
def write_some(self):
if len(self.writeBuf) == 0: return 0
if len(self.writeBuf) == 0:
return 0
bytesWritten = 0
try:
@ -185,35 +195,37 @@ class Connection:
self.writeBuf = self.writeBuf[count:]
bytesWritten += count
else:
if bytesWritten: return bytesWritten
if bytesWritten:
return bytesWritten
self.connection_lost()
return 0
except socket.error as (err, msg):
except socket.error as xxx_todo_changeme1:
# Error codes for write buffer full
(err, msg) = xxx_todo_changeme1.args
# Error codes for write buffer full
if err not in [errno.EAGAIN, errno.EWOULDBLOCK, WSAEWOULDBLOCK]:
if bytesWritten: return bytesWritten
if bytesWritten:
return bytesWritten
self.connection_error(err, msg)
raise
return bytesWritten
def queue_command(self, command):
if debug: print('command: ' + command)
if debug:
print(('command: ' + command))
self.writeBuf += command + '\n'
def parse_message(self, version, type, data):
try:
msg = json.loads(data, cls = PYONDecoder)
#if debug: print 'MSG:', type, msg
msg = json.loads(data, cls=PYONDecoder)
# if debug: print 'MSG:', type, msg
self.messages.append((version, type, msg))
self.last_message = time.time()
except Exception as e:
print('ERROR parsing PyON message: %s: %s'
% (str(e), data.encode('string_escape')))
print(('ERROR parsing PyON message: %s: %s'
% (str(e), data.encode('string_escape'))))
def parse(self):
start = self.readBuf.find('\nPyON ')
@ -240,7 +252,6 @@ class Connection:
return False
def update(self):
try:
try:
@ -250,13 +261,15 @@ class Connection:
self.open()
elif self.last_connect + 60 < time.time():
self.close() # Retry connect
self.close() # Retry connect
if not self.is_connected(): return
if not self.is_connected():
return
self.write_some()
if self.read_some():
while self.parse(): continue
while self.parse():
continue
# Handle special case for OSX disconnect
except socket.error as e:
@ -264,11 +277,12 @@ class Connection:
self.fail_reason = 'refused'
self.close()
else: raise
else:
raise
except Exception as e:
print('ERROR on connection to %s:%d: %s' % (
self.address, self.port, e))
print(('ERROR on connection to %s:%d: %s' % (
self.address, self.port, e)))
# Timeout connection
if self.connected and self.last_message and \
@ -277,18 +291,17 @@ class Connection:
self.close()
if __name__ == '__main__':
init = ['updates add 0 1 $options',
'updates add 1 1 $queue-info',
'updates add 2 1 $slot-info']
conn = Connection(init_commands = init)
conn = Connection(init_commands=init)
while True:
conn.update()
for version, type, data in conn.messages:
print('PyON %d %s:\n' % (version, type), data)
print(('PyON %d %s:\n' % (version, type), data))
conn.messages = []
time.sleep(0.1)

File diff suppressed because it is too large Load Diff

View File

@ -1,23 +1,23 @@
################################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################
###############################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
###############################################################################
import gtk
@ -34,10 +34,14 @@ def get_icon(name):
global icons
if icons[name] is None:
if name == 'tiny': icons[name] = load_rgba_pixbuf(icon_24_raw, 24)
elif name == 'small': icons[name] = load_rgba_pixbuf(icon_32_raw, 32)
elif name == 'medium': icons[name] = load_rgba_pixbuf(icon_64_raw, 64)
else: icons[name] = load_rgba_pixbuf(icon_128_raw, 128)
if name == 'tiny':
icons[name] = load_rgba_pixbuf(icon_24_raw, 24)
elif name == 'small':
icons[name] = load_rgba_pixbuf(icon_32_raw, 32)
elif name == 'medium':
icons[name] = load_rgba_pixbuf(icon_64_raw, 64)
else:
icons[name] = load_rgba_pixbuf(icon_128_raw, 128)
return icons[name]
@ -60,7 +64,8 @@ def get_viewer_icon(name):
viewer_icons[name] = load_rgba_pixbuf(viewer_icon_32_raw, 32)
elif name == 'medium':
viewer_icons[name] = load_rgba_pixbuf(viewer_icon_64_raw, 64)
else: viewer_icons[name] = load_rgba_pixbuf(viewer_icon_128_raw, 128)
else:
viewer_icons[name] = load_rgba_pixbuf(viewer_icon_128_raw, 128)
return viewer_icons[name]

View File

@ -1,23 +1,23 @@
################################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################
###############################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
###############################################################################
import gtk
import gobject
@ -29,8 +29,8 @@ from fah.util import status_to_color
class SlotConfig:
def __init__(
self, id = -1, status = None, description = None, reason = None,
idle = False, options = {}, **kw):
self, id=-1, status=None, description=None, reason=None,
idle=False, options={}, **kw):
self.id = int(id)
self.status = status
@ -39,28 +39,30 @@ class SlotConfig:
self.idle = idle
self.options = options
if status is not None: self.status = status.title()
if description is None: self.description = 'cpu'
if status is not None:
self.status = status.title()
if description is None:
self.description = 'cpu'
# Type
if self.description.startswith('cpu'): self.type = 'cpu'
elif self.description.startswith('gpu'): self.type = 'gpu'
else: raise Exception('Invalid slot type "%s"' % description)
if self.description.startswith('cpu'):
self.type = 'cpu'
elif self.description.startswith('gpu'):
self.type = 'gpu'
else:
raise Exception('Invalid slot type "%s"' % description)
def add_to_ui(self, app):
wrapper = gobject.GObject()
wrapper.slot = copy.deepcopy(self)
app.slot_list.append((self.id, self.type, wrapper))
@staticmethod
def clear_dialog(app):
app.slot_option_list.clear()
app.slot_type_cpu.set_active(True)
app.slot_option_widgets['cpus'].set_value(-1)
def save_dialog(self, app):
self.options = {}
@ -68,37 +70,42 @@ class SlotConfig:
self.type = 'cpu'
cpus = str(int(app.slot_option_widgets['cpus'].get_value()))
if int(cpus): self.options['cpus'] = cpus
if int(cpus):
self.options['cpus'] = cpus
elif app.slot_type_gpu.get_active():
self.type = 'gpu'
# Extra options
def add_option(model, path, iter, data = None):
def add_option(model, path, iter, data=None):
name = model.get(iter, 0)[0]
value = model.get(iter, 1)[0]
self.options[name] = value
app.slot_option_list.foreach(add_option)
def load_dialog(self, app):
used = set()
# Type
if self.type == 'cpu': app.slot_type_cpu.set_active(True)
elif self.type == 'gpu': app.slot_type_gpu.set_active(True)
else: raise Exception('Invalid slot type "%s"' % self.type)
if self.type == 'cpu':
app.slot_type_cpu.set_active(True)
elif self.type == 'gpu':
app.slot_type_gpu.set_active(True)
else:
raise Exception('Invalid slot type "%s"' % self.type)
used.add('gpu')
# SMP
if 'cpus' in self.options: cpus = float(self.options['cpus'])
else: cpus = -1
if 'cpus' in self.options:
cpus = float(self.options['cpus'])
else:
cpus = -1
app.slot_option_widgets['cpus'].set_value(cpus)
used.add('cpus')
# Options
app.slot_option_list.clear()
for name, value in self.options.items():
if not name in used:
for name, value in list(self.options.items()):
if name not in used:
app.slot_option_list.append((name, value))

View File

@ -1,43 +1,48 @@
################################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################
###############################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
###############################################################################
import gtk
class WidgetMap(dict):
def __init__(self, widgets, suffix = None, prefix = None):
def __init__(self, widgets, suffix=None, prefix=None):
self.suffix = suffix
self.prefix = prefix
self.list = []
try:
for widget in widgets: self.find(widget)
except: self.find(widgets)
for widget in widgets:
self.find(widget)
except:
self.find(widgets)
def add(self, widget):
if widget is None: return
if widget is None:
return
name = gtk.Buildable.get_name(widget)
if self.suffix is not None: name = name[0:-len(self.suffix)]
if self.prefix is not None: name = name[len(self.prefix):]
if self.suffix is not None:
name = name[0:-len(self.suffix)]
if self.prefix is not None:
name = name[len(self.prefix):]
self[name] = widget
self.list.append(widget)
@ -45,7 +50,7 @@ class WidgetMap(dict):
name = gtk.Buildable.get_name(widget)
if (name and (self.suffix is None or name.endswith(self.suffix)) and
(self.prefix is None or name.startswith(self.prefix))):
(self.prefix is None or name.startswith(self.prefix))):
self.add(widget)
if isinstance(widget, gtk.Container):

View File

@ -1,34 +1,34 @@
################################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################
###############################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
###############################################################################
# fah
import db
import util
from . import db
from . import util
from Version import *
from Icon import *
from SlotConfig import *
from ClientConfig import *
from Connection import *
from Client import *
from WidgetMap import *
from FAHControl import *
from .Icon import *
from .SlotConfig import *
from .ClientConfig import *
from .Connection import *
from .Client import *
from .WidgetMap import *
from .FAHControl import *

View File

@ -1,49 +1,50 @@
################################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################
###############################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
###############################################################################
class Column:
def __init__(self, name, dbType, constraits, auto = False):
def __init__(self, name, dbType, constraits, auto=False):
self.name = name
self.dbType = dbType.lower()
self.constraints = constraits
self.auto = auto
def get_name(self): return self.name
def is_auto(self): return self.auto
def get_db_value(self, value):
if self.dbType == 'text': return "'%s'" % str(value).replace("'", "''")
if self.dbType == 'integer': return '%d' % value
if self.dbType == 'real': return '%f' % value
if self.dbType == 'text':
return "'%s'" % str(value).replace("'", "''")
if self.dbType == 'integer':
return '%d' % value
if self.dbType == 'real':
return '%f' % value
if self.dbType == 'boolean':
if value: return '1'
else: return '0'
if value:
return '1'
else:
return '0'
def get_sql(self):
return '"%s" %s %s' % (self.name, self.dbType, self.constraints)
def __hash__(self): return self.name.__hash__()
def __cmp__(self, other): return self.name.__cmp__(other.name)
def __str__(self): return self.name

View File

@ -1,23 +1,23 @@
################################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################
###############################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
###############################################################################
from fah.db import Column, Table
@ -43,131 +43,121 @@ class Database:
'PRIMARY KEY (name)'),
]
def __init__(self, filename):
self.filename = filename
self.conn = sqlite3.connect(filename)
self.conn.row_factory = sqlite3.Row
self.queue = {}
def get_table(self, name):
for table in self.tables:
if table.name == name: return table
if table.name == name:
return table
raise Exception('Table "%s" not found' % name)
def get_version(self):
return 6
def get_current_version(self):
return int(self.execute_one('PRAGMA user_version')[0])
def set_current_version(self, version):
self.write('PRAGMA user_version=%d' % version, True)
def set(self, name, value, commit = True, queue = False):
if queue: self.queue[name] = value
def set(self, name, value, commit=True, queue=False):
if queue:
self.queue[name] = value
else:
self.insert('config', name = name, value = value)
if commit: self.commit()
def clear(self, name, commit = True):
self.delete('config', name = name)
if commit: self.commit()
self.insert('config', name=name, value=value)
if commit:
self.commit()
def clear(self, name, commit=True):
self.delete('config', name=name)
if commit:
self.commit()
def get(self, name):
c = self.get_table('config').select(self, 'value', name = name)
c = self.get_table('config').select(self, 'value', name=name)
result = c.fetchone()
c.close()
if result: return result[0]
if result:
return result[0]
def has(self, name):
return self.get(name) != None
def default(self, name, default, commit = True):
if not self.has(name): self.set(name, default, commit)
return self.get(name) is not None
def default(self, name, default, commit=True):
if not self.has(name):
self.set(name, default, commit)
def flush_queued(self):
if len(self.queue) == 0: return
if len(self.queue) == 0:
return
for name, value in self.queue.items():
self.set(name, value, commit = False)
for name, value in list(self.queue.items()):
self.set(name, value, commit=False)
self.commit()
self.queue.clear()
def execute(self, sql):
#print 'SQL:', sql
# print 'SQL:', sql
c = self.conn.cursor()
c.execute(sql)
return c
def execute_one(self, sql):
c = self.execute(sql)
result = c.fetchone()
c.close()
return result
def write(self, sql, commit = False):
def write(self, sql, commit=False):
self.execute(sql).close()
if commit: self.commit()
if commit:
self.commit()
def commit(self):
self.conn.commit()
def rollback(self):
self.conn.rollback()
def insert(self, table, **kwargs):
self.get_table(table).insert(self, **kwargs)
def delete(self, table, **kwargs):
self.get_table(table).delete(self, **kwargs)
def select(self, table, cols = None, **kwargs):
def select(self, table, cols=None, **kwargs):
return self.get_table(table).select(self, cols, **kwargs)
def create(self):
for table in self.tables:
table.create(self)
self.commit()
def validate(self):
current = self.get_current_version()
if self.get_version() < current:
raise Exception('Configuration database "%s" version %d is newer than is supported %d'
raise Exception('Configuration database "%s" version %d is newer \
than is supported %d'
% (self.filename, current, self.get_version()))
elif self.get_version() != current:
# Create or upgrade DB
if current == 0: self.create()
if current == 0:
self.create()
else:
if current <= 2:
# Just drop and recreate the clients table
self.execute('DROP TABLE IF EXISTS clients')
for table in self.tables:
if table.name == 'clients': table.create(self)
if table.name == 'clients':
table.create(self)
if current <= 5:
self.execute('DROP TABLE IF EXISTS projects')

View File

@ -1,36 +1,36 @@
################################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################
###############################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
###############################################################################
from fah.db import Column
class Table:
def __init__(self, name, cols, constraints = ''):
def __init__(self, name, cols, constraints=''):
self.name = name
self.cols = cols
self.constraints = constraints
def where(self, **kwargs):
if len(kwargs) == 0: return ''
if len(kwargs) == 0:
return ''
sql = 'WHERE '
if len(kwargs) == 1 and 'where' in kwargs:
@ -38,38 +38,37 @@ class Table:
else:
sql +=\
' AND '.join(map(lambda i: '"%s"=\'%s\'' % i, kwargs.items()))
' AND '.join(['"%s"=\'%s\'' % i for i in list(kwargs.items())])
return sql
def create(self, db):
sql = 'CREATE TABLE IF NOT EXISTS "%s" (%s' % (
self.name, ','.join(map(Column.get_sql, self.cols)))
if self.constraints: sql += ',%s' % self.constraints
if self.constraints:
sql += ',%s' % self.constraints
sql += ')'
db.execute(sql).close()
def insert(self, db, **kwargs):
cols = filter(lambda col: col.name in kwargs, self.cols)
cols = [col for col in self.cols if col.name in kwargs]
# Error checking
if len(cols) != len(kwargs):
col_names = set(map(Column.get_name, cols))
missing = filter(lambda kw: not kw in col_names, kwargs.keys())
missing = [kw for kw in list(kwargs.keys()) if kw not in col_names]
raise Exception('Table %s does not have column(s) %s'
% (self.name, ', '.join(missing)))
sql = 'REPLACE INTO "%s" ("%s") VALUES (%s)' % (
self.name, '","'.join(map(Column.get_name, cols)),
','.join(map(lambda col: col.get_db_value(kwargs[col.name]), cols)))
','.join([col.get_db_value(kwargs[col.name]) for col in cols]))
db.execute(sql).close()
def select(self, db, cols = None, **kwargs):
def select(self, db, cols=None, **kwargs):
if cols is None:
cols = '"' + '","'.join(map(str, self.cols)) + '"'
@ -82,11 +81,9 @@ class Table:
return db.execute(sql)
def delete(self, db, **kwargs):
sql = 'DELETE FROM %s %s' % (self.name, self.where(**kwargs))
db.execute(sql).close()
def drop(self, db):
db.execute('DROP TABLE IF EXISTS ' + self.name).close()

View File

@ -1,26 +1,26 @@
################################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################
###############################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
###############################################################################
# fah.db
from Column import *
from Table import *
from Database import *
from .Column import *
from .Table import *
from .Database import *

View File

@ -69,9 +69,9 @@ class OrderedDict(dict, DictMixin):
if not self:
raise KeyError('dictionary is empty')
if last:
key = reversed(self).next()
key = next(reversed(self))
else:
key = iter(self).next()
key = next(iter(self))
value = self.pop(key)
return key, value
@ -100,7 +100,7 @@ class OrderedDict(dict, DictMixin):
def __repr__(self):
if not self:
return '%s()' % (self.__class__.__name__,)
return '%s(%r)' % (self.__class__.__name__, self.items())
return '%s(%r)' % (self.__class__.__name__, list(self.items()))
def copy(self):
return self.__class__(self)
@ -114,7 +114,7 @@ class OrderedDict(dict, DictMixin):
def __eq__(self, other):
if isinstance(other, OrderedDict):
return len(self)==len(other) and self.items() == other.items()
return len(self)==len(other) and list(self.items()) == list(other.items())
return dict.__eq__(self, other)
def __ne__(self, other):

View File

@ -29,8 +29,8 @@ NUMBER_RE = re.compile(r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?',
FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS)
BACKSLASH = {
'"': u'"', '\\': u'\\', '/': u'/',
'b': u'\b', 'f': u'\f', 'n': u'\n', 'r': u'\r', 't': u'\t',
'"': '"', '\\': '\\', '/': '/',
'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t',
}
DEFAULT_ENCODING = "utf-8"
@ -94,8 +94,8 @@ def pyon_scanstring(s, end, encoding = None, strict = True,
# Content is contains zero or more unescaped string characters
if content:
if not isinstance(content, unicode):
content = unicode(content, encoding)
if not isinstance(content, str):
content = str(content, encoding)
_append(content)
# Terminator is the end of string, a literal control character,
@ -127,7 +127,7 @@ def pyon_scanstring(s, end, encoding = None, strict = True,
# Hex escape sequence
code = s[end + 1: end + 3]
try:
char = unichr(int(code, 16))
char = chr(int(code, 16))
except:
raise ValueError(errmsg('Invalid \\escape: ' + repr(code), s, end))
@ -144,12 +144,12 @@ def pyon_scanstring(s, end, encoding = None, strict = True,
if 0xdc00 <= uni2 <= 0xdfff:
uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00))
end += 6
char = unichr(uni)
char = chr(uni)
# Append the unescaped character
_append(char)
return u''.join(chunks), end
return ''.join(chunks), end

View File

@ -22,7 +22,7 @@
import sys
import socket
import threading
import SocketServer
import socketserver
import gtk
@ -33,7 +33,7 @@ single_app_port = 32455
single_app_addr = (single_app_host, single_app_port)
class SingleAppRequestHandler(SocketServer.BaseRequestHandler):
class SingleAppRequestHandler(socketserver.BaseRequestHandler):
def handle(self):
cmd = self.request.recv(1024).strip()
@ -47,7 +47,7 @@ class SingleAppRequestHandler(SocketServer.BaseRequestHandler):
class SingleAppServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
class SingleAppServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
allow_reuse_address = True
def __init__(self):
@ -57,7 +57,7 @@ class SingleAppServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
self.ping = threading.Event()
self.exit_requested = threading.Event()
SocketServer.TCPServer.__init__(
socketserver.TCPServer.__init__(
self, single_app_addr, SingleAppRequestHandler)
thread = threading.Thread(target = self.serve_forever)

View File

@ -32,11 +32,11 @@ if sys.platform == 'darwin':
from gtkosx_application import gtkosx_application_get_resource_path \
as quartz_application_get_resource_path
from SingleApp import *
from EntryValidator import *
from PasswordValidator import *
from OrderedDict import *
from PYONDecoder import *
from .SingleApp import *
from .EntryValidator import *
from .PasswordValidator import *
from .OrderedDict import *
from .PYONDecoder import *
def parse_bool(x):
@ -125,7 +125,7 @@ def get_widget_str_value(widget):
return widget.get_active_text()
else:
print ('ERROR: unsupported widget type %s' % type(widget))
print(('ERROR: unsupported widget type %s' % type(widget)))
def set_widget_str_value(widget, value):
@ -159,7 +159,7 @@ def set_widget_str_value(widget, value):
widget.set_active(i)
return
print ('ERROR: Invalid value "%s"' % value)
print(('ERROR: Invalid value "%s"' % value))
elif isinstance(widget, gtk.ProgressBar):
widget.set_text(value)
@ -172,7 +172,7 @@ def set_widget_str_value(widget, value):
widget.set_fraction(fraction)
else:
print ('ERROR: unsupported option widget type %s' % type(widget))
print(('ERROR: unsupported option widget type %s' % type(widget)))
def set_widget_change_action(widget, action):
@ -192,7 +192,7 @@ def set_widget_change_action(widget, action):
widget.connect('rows_reordered', action)
else:
print ('ERROR: unsupported option widget type %s' % type(widget))
print(('ERROR: unsupported option widget type %s' % type(widget)))
def get_home_dir():

View File

@ -1,23 +1,23 @@
################################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################
###############################################################################
# #
# Folding@Home Client Control (FAHControl) #
# Copyright (C) 2016-2020 foldingathome.org #
# Copyright (C) 2010-2016 Stanford University #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
###############################################################################
import gtk
import gobject
@ -27,42 +27,39 @@ import pango
class WrapLabel(gtk.Label):
__gtype_name__ = 'WrapLabel'
def __init__(self, str = None):
def __init__(self, str=None):
gtk.Label.__init__(self)
self.__wrap_width = 0
self.layout = self.get_layout()
self.layout.set_wrap(pango.WRAP_WORD_CHAR)
if str != None: self.set_text(str)
if str is not None:
self.set_text(str)
self.set_alignment(0, 0)
def do_size_request(self, requisition):
layout = self.get_layout()
width, height = layout.get_pixel_size()
requisition.width = 0
requisition.height = height
def do_size_allocate(self, allocation):
gtk.Label.do_size_allocate(self, allocation)
self.__set_wrap_width(allocation.width)
def set_text(self, str):
gtk.Label.set_text(self, str)
self.__set_wrap_width(self.__wrap_width)
def set_markup(self, str):
gtk.Label.set_markup(self, str)
self.__set_wrap_width(self.__wrap_width)
def __set_wrap_width(self, width):
if width == 0: return
if width == 0:
return
layout = self.get_layout()
layout.set_width(width * pango.SCALE)
if self.__wrap_width != width:

View File

@ -48,7 +48,7 @@ if [ -d "$A2" ]; then chmod -R go-w "$A2"; fi
A3="/Applications/Folding@home/FAHControl/FAHControl.app"
F1="/Applications/Folding@home/FAHControl/.DS_Store"
D1="/Applications/Folding@home/FAHControl"
[ -d "$A3" ] && [ ! -d "$A2"] && mv "$A3" "$A2" || true
[ -d "$A3" ] && [ ! -d "$A2" ] && mv "$A3" "$A2" || true
[ -f "$F1" ] && rm -f "$F1" || true
[ -d "$D1" ] && rmdir "$D1" || true

View File

@ -4,7 +4,7 @@ import inspect
dir = os.path.dirname(inspect.getfile(inspect.currentframe()))
if dir == '': dir = '.'
print ('dir = %s' % dir)
print(('dir = %s' % dir))
os.chdir(dir)