2to3 conversion
This commit is contained in:
parent
1597fe4d04
commit
5ef3ee1179
28
FAHControl
28
FAHControl
@ -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)
|
||||
|
73
SConstruct
73
SConstruct
@ -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])
|
||||
|
151
ez_setup.py
151
ez_setup.py
@ -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:])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
198
fah/Client.py
198
fah/Client.py
@ -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':
|
||||
|
@ -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 != ([], [], [])
|
||||
|
@ -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
55
fah/Icon.py
55
fah/Icon.py
@ -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]
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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):
|
||||
|
@ -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 *
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
|
@ -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()
|
||||
|
@ -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 *
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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():
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user