mirror of
https://github.com/schrodinger/pymol-open-source.git
synced 2026-06-03 19:54:24 +08:00
416 lines
15 KiB
Python
416 lines
15 KiB
Python
from pymol.gui import get_qtwindow as getPyMOLWindow
|
|
from pymol.plugins import installation, repository
|
|
|
|
from pymol.Qt import QtGui, QtCore
|
|
from pymol.Qt import QtWidgets
|
|
|
|
from . import pref_get
|
|
|
|
Qt = QtCore.Qt
|
|
|
|
def confirm_network_access():
|
|
'''
|
|
Popup dialog with network access notification (only once per session)
|
|
'''
|
|
self = confirm_network_access
|
|
if self.ok < 0:
|
|
check = QtWidgets.QMessageBox.information(None, 'Info',
|
|
'Network download has been disabled, sorry!')
|
|
return False
|
|
if self.ok > 0:
|
|
return True
|
|
if QtWidgets.QMessageBox.question(None, 'Confirm',
|
|
'PyMOL will now download executable code from the internet!'
|
|
' Proceed?') == QtWidgets.QMessageBox.StandardButton.Yes:
|
|
self.ok = 1
|
|
else:
|
|
self.ok = 0
|
|
|
|
return self.ok
|
|
|
|
# valid values: 1=never ask, 0=ask once per session, -1=network access disabled
|
|
confirm_network_access.ok = pref_get('network_access_ok', 0)
|
|
|
|
class PluginManager(QtCore.QObject):
|
|
|
|
def __init__(self, cmd):
|
|
|
|
super(PluginManager, self).__init__(None)
|
|
|
|
window = getPyMOLWindow()
|
|
|
|
self.form = window.load_form('pluginmanager')
|
|
self.form.b_wiki.pressed.connect(self.fetchplugin)
|
|
self.form.e_wiki.returnPressed.connect(self.fetchplugin)
|
|
self.form.b_local.pressed.connect(self.installplugin)
|
|
self.form.b_startup_all.pressed.connect(self.startup_all)
|
|
self.form.b_startup_none.pressed.connect(self.startup_none)
|
|
self.form.e_filter.textChanged.connect(self.filter_plugins)
|
|
self.form.c_loaded.stateChanged.connect(self.filter_plugins)
|
|
self.form.c_startup.stateChanged.connect(self.filter_plugins)
|
|
self.form.l_repositories.itemClicked.connect(self.repo_changed)
|
|
self.form.bb_path_add.pressed.connect(self.add_path)
|
|
self.form.bb_path_up.pressed.connect(self.move_path_up)
|
|
self.form.bb_path_down.pressed.connect(self.move_path_down)
|
|
self.form.bb_path_remove.pressed.connect(self.remove_path)
|
|
self.form.b_add_repo.pressed.connect(self.add_repository)
|
|
self.form.b_remove_repo.pressed.connect(self.remove_repository)
|
|
self.reload_plugins()
|
|
self.form.l_repo_plugins.setSelectionMode(
|
|
QtWidgets.QAbstractItemView.SelectionMode.ExtendedSelection)
|
|
self.form.b_install.pressed.connect(self.install_repo_plugins)
|
|
self.form.b_info.pressed.connect(self.info_repo_plugin)
|
|
self.populate_repositories()
|
|
self.populate_startup_paths()
|
|
self.populate_preferences()
|
|
self.form._dialog.show()
|
|
|
|
def populate_repositories(self):
|
|
repos = [
|
|
'http://pldserver1.biochem.queensu.ca/~rlc/work/pymol/',
|
|
'https://github.com/Pymol-Scripts/Pymol-script-repo',
|
|
# for testing
|
|
'http://www.thomas-holder.de/projects/pymol/repository/'
|
|
]
|
|
for rep in repos:
|
|
self.form.l_repositories.addItem(rep)
|
|
|
|
def populate_startup_paths(self):
|
|
from . import get_startup_path
|
|
items = get_startup_path(True)
|
|
self.form.slb_path.addItems(items)
|
|
|
|
def update_startup_paths(self):
|
|
from . import set_startup_path
|
|
items = []
|
|
for index in range(self.form.slb_path.count()):
|
|
items.append(self.form.slb_path.item(index).text())
|
|
set_startup_path(items)
|
|
|
|
def populate_preferences(self):
|
|
from . import preferences
|
|
|
|
self.t_preferences_keys = list(preferences)
|
|
|
|
w = self.form.t_preferences
|
|
w.horizontalHeader().setStretchLastSection(True)
|
|
w.setRowCount(len(self.t_preferences_keys))
|
|
|
|
for row, key in enumerate(self.t_preferences_keys):
|
|
item = QtWidgets.QTableWidgetItem(key)
|
|
item.setFlags(Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEnabled)
|
|
w.setItem(row, 0, item)
|
|
|
|
item = QtWidgets.QTableWidgetItem()
|
|
value = preferences[key]
|
|
if isinstance(value, bool):
|
|
item.setCheckState(Qt.CheckState.Checked if value else Qt.CheckState.Unchecked)
|
|
else:
|
|
item.setFlags(item.flags() & ~Qt.ItemFlag.ItemIsUserCheckable)
|
|
item.setText(str(value))
|
|
w.setItem(row, 1, item)
|
|
|
|
w.itemChanged.connect(self.update_preferences_item)
|
|
|
|
def update_preferences_item(self, item):
|
|
from . import pref_get, pref_set
|
|
key = self.t_preferences_keys[item.row()]
|
|
|
|
if item.flags() & Qt.ItemFlag.ItemIsUserCheckable:
|
|
value = item.checkState() == Qt.CheckState.Checked
|
|
else:
|
|
value = item.data(0)
|
|
|
|
if hasattr(value, 'toString'): # PyQt4
|
|
value = value.toString()
|
|
|
|
try:
|
|
value = type(pref_get(key, ''))(value)
|
|
except BaseException as e:
|
|
print(e)
|
|
|
|
pref_set(key, value)
|
|
|
|
def install_repo_plugins(self):
|
|
from .installation import installPluginFromFile, get_plugdir
|
|
items = self.form.l_repo_plugins.selectedItems()
|
|
if len(items) == 0:
|
|
return
|
|
plugdir = get_plugdir(None)
|
|
if not plugdir:
|
|
return
|
|
import sys, tempfile, shutil, os
|
|
from .legacysupport import tkMessageBox
|
|
from .repository import guess
|
|
tmpdir = tempfile.mkdtemp()
|
|
try:
|
|
for item in items:
|
|
name = item.text()
|
|
filename = self.repo_r.copy(name, tmpdir)
|
|
installPluginFromFile(filename, None, plugdir)
|
|
except:
|
|
err = str(sys.exc_info()[1])
|
|
tkMessageBox.showinfo('Error', 'Could not install plugin ' + name + '\n\n' + err)
|
|
finally:
|
|
shutil.rmtree(tmpdir)
|
|
self.reload_plugins()
|
|
|
|
def info_repo_plugin(self):
|
|
from . import PluginInfo
|
|
from .installation import get_name_and_ext, extract_zipfile, zip_extensions
|
|
from .legacysupport import tkMessageBox
|
|
items = self.form.l_repo_plugins.selectedItems()
|
|
if len(items) == 0:
|
|
return
|
|
import tempfile, shutil, os
|
|
tmpdir = tempfile.mkdtemp()
|
|
tmpdirs = [tmpdir]
|
|
try:
|
|
name = items[0].text()
|
|
filename = self.repo_r.copy(name, tmpdir)
|
|
name, ext = get_name_and_ext(filename)
|
|
if ext in zip_extensions:
|
|
tmpdir, dirnames = extract_zipfile(filename, ext)
|
|
tmpdirs.append(tmpdir)
|
|
name = dirnames[-1]
|
|
filename = os.path.join(os.path.join(tmpdir, *dirnames), '__init__.py')
|
|
info = PluginInfo(name, filename)
|
|
self.show_plugin_info_dialog(info)
|
|
except:
|
|
tkMessageBox.showinfo('Error', 'Could not get plugin info')
|
|
finally:
|
|
for tmpdir in tmpdirs:
|
|
shutil.rmtree(tmpdir)
|
|
|
|
def repo_changed(self, item):
|
|
from .repository import guess
|
|
url = item.text()
|
|
self.repo_r = guess(url)
|
|
plist = self.repo_r.list()
|
|
self.form.l_repo_plugins.clear()
|
|
for p in plist:
|
|
self.form.l_repo_plugins.addItem(p)
|
|
|
|
def add_repository(self):
|
|
repo, result = QtWidgets.QInputDialog.getText(None,
|
|
'Add repository', 'Enter repository URL')
|
|
if result and repo.startswith('http'):
|
|
self.form.l_repositories.addItem(repo)
|
|
|
|
def remove_repository(self):
|
|
items = self.form.l_repositories.selectedItems()
|
|
if len(items) == 0:
|
|
return
|
|
row = self.form.l_repositories.row(items[0])
|
|
self.form.l_repositories.takeItem(row)
|
|
items = self.form.l_repositories.selectedItems()
|
|
if len(items) > 0:
|
|
self.repo_changed(items[0])
|
|
|
|
def show(self):
|
|
self.form._dialog.show()
|
|
self.form._dialog.raise_()
|
|
|
|
def w_startup_changed(self, state):
|
|
item = self.sender().parent()._form
|
|
info = self.plugin_info[item]
|
|
info.autoload = state
|
|
|
|
def reload_plugins(self, setting=None):
|
|
from . import plugins
|
|
window = getPyMOLWindow()
|
|
|
|
self.clear_plugins()
|
|
|
|
def add_plugin_item(info):
|
|
item = window.load_form('pluginitem', QtWidgets.QFrame())
|
|
item._widget = item._dialog
|
|
item._widget.setFrameStyle(QtWidgets.QFrame.Shadow.Sunken)
|
|
item._widget.setFrameShape(QtWidgets.QFrame.Shape.Panel)
|
|
|
|
item.w_title.setText(info.name)
|
|
item.w_version.setText(info.get_version())
|
|
item.w_startup.setChecked(info.autoload)
|
|
item.w_startup.stateChanged.connect(self.w_startup_changed)
|
|
item.w_enable.pressed.connect(info.load)
|
|
item.w_enable.pressed.connect(self.disable_load_button)
|
|
item.w_uninstall.pressed.connect(info.uninstall)
|
|
item.w_uninstall.pressed.connect(self.reload_plugins)
|
|
if info.loaded:
|
|
item.w_enable.setEnabled(False)
|
|
if hasattr(info.module, 'settings_dialog'):
|
|
item.w_settings.setVisible(True)
|
|
else:
|
|
item.w_settings.setVisible(False)
|
|
item.w_info.pressed.connect(self.show_info)
|
|
if info.loadtime:
|
|
item.w_loadtime.setText("Took %.3f seconds to load" % info.loadtime)
|
|
else:
|
|
item.w_loadtime.setText("Not loaded")
|
|
|
|
item._widget._form = item
|
|
self.plugin_info[item] = info
|
|
|
|
self.form.f_installed_layout.addWidget(item._widget)
|
|
|
|
for info in sorted(plugins.values(), key=lambda i: i.name.lower()):
|
|
try:
|
|
add_plugin_item(info)
|
|
except Exception as e:
|
|
print(e)
|
|
|
|
self.filter_plugins()
|
|
self.form.f_installed_layout.addStretch()
|
|
|
|
def clear_plugins(self):
|
|
self.plugin_info = {}
|
|
|
|
layout = self.form.f_installed_layout
|
|
while layout.count():
|
|
child = layout.takeAt(0)
|
|
if child.widget() is not None:
|
|
child.widget().deleteLater()
|
|
|
|
def filter_plugins(self):
|
|
filter_loaded = self.form.c_loaded.isChecked()
|
|
filter_startup = self.form.c_startup.isChecked()
|
|
filter_name = self.form.e_filter.text().lower()
|
|
|
|
for (item, info) in self.plugin_info.items():
|
|
if filter_name not in info.name.lower():
|
|
vis = False
|
|
elif filter_loaded and not info.loaded:
|
|
vis = False
|
|
elif filter_startup and not info.autoload:
|
|
vis = False
|
|
else:
|
|
vis = True
|
|
|
|
item._widget.setVisible(vis)
|
|
|
|
def startup_all(self):
|
|
for item, info in self.plugin_info.items():
|
|
item.w_startup.setChecked(True)
|
|
info.autoload = True
|
|
|
|
def startup_none(self):
|
|
for item, info in self.plugin_info.items():
|
|
item.w_startup.setChecked(False)
|
|
info.autoload = False
|
|
|
|
def installplugin(self):
|
|
from .legacysupport import installPlugin
|
|
installPlugin(self)
|
|
self.reload_plugins()
|
|
|
|
def fetchplugin(self):
|
|
if not confirm_network_access():
|
|
return
|
|
from .installation import installPluginFromFile
|
|
from .repository import fetchscript
|
|
from pymol import CmdException
|
|
url = self.form.e_wiki.text()
|
|
if not len(url):
|
|
return
|
|
import tempfile, shutil
|
|
tmpdir = tempfile.mkdtemp()
|
|
try:
|
|
filename = fetchscript(url, tmpdir, False)
|
|
except BaseException as e:
|
|
QtWidgets.QMessageBox.critical(None, 'Error',
|
|
'Fetching Plugin failed.\n' + str(e))
|
|
return
|
|
|
|
if filename:
|
|
installPluginFromFile(filename)
|
|
shutil.rmtree(tmpdir)
|
|
self.reload_plugins()
|
|
|
|
def show_info(self):
|
|
item = self.sender().parent()._form
|
|
info = self.plugin_info[item]
|
|
self.show_plugin_info_dialog(info)
|
|
|
|
def disable_load_button(self):
|
|
item = self.sender().parent()._form
|
|
item.w_enable.setEnabled(False)
|
|
|
|
def show_plugin_info_dialog(self, info):
|
|
dialog = QtWidgets.QDialog(None)
|
|
dialog.setWindowTitle('Plugin Information')
|
|
layout = QtWidgets.QVBoxLayout()
|
|
table = QtWidgets.QTableWidget(0, 2)
|
|
table.verticalHeader().hide()
|
|
table.horizontalHeader().hide()
|
|
table.horizontalHeader().setStretchLastSection(True)
|
|
layout.addWidget(table)
|
|
dialog.setLayout(layout)
|
|
|
|
def add_line(label, text):
|
|
row = table.rowCount()
|
|
table.insertRow(table.rowCount())
|
|
table_item = QtWidgets.QTableWidgetItem(label)
|
|
table_item.setFlags(table_item.flags() &
|
|
~(Qt.ItemFlag.ItemIsEditable))
|
|
table.setItem(row, 0, table_item)
|
|
table_item = QtWidgets.QTableWidgetItem(text)
|
|
table_item.setFlags(table_item.flags() &
|
|
~(Qt.ItemFlag.ItemIsEditable))
|
|
table.setItem(row, 1, table_item)
|
|
|
|
add_line('Name', info.name)
|
|
if not info.is_temporary:
|
|
add_line('Python Module Name', info.mod_name)
|
|
add_line('Filename', info.filename)
|
|
|
|
metadata = info.get_metadata()
|
|
for label, value in metadata.items():
|
|
add_line(label, value)
|
|
|
|
if not info.is_temporary:
|
|
if info.loaded:
|
|
add_line('commands', ', '.join(info.commands))
|
|
docstring = info.get_docstring() or 'No documentation available.'
|
|
|
|
browser = QtWidgets.QTextBrowser()
|
|
browser.setPlainText(docstring)
|
|
layout.addWidget(browser)
|
|
|
|
table.resizeColumnsToContents()
|
|
|
|
dialog.resize(600, dialog.height())
|
|
dialog.exec()
|
|
|
|
def add_path(self):
|
|
from .installation import get_default_user_plugin_path as userpath
|
|
d = QtWidgets.QFileDialog.getExistingDirectory(None,
|
|
'Add plugin directory', userpath())
|
|
if len(d) > 0:
|
|
self.form.slb_path.addItem(d)
|
|
self.update_startup_paths()
|
|
|
|
def move_path_up(self):
|
|
items = self.form.slb_path.selectedItems()
|
|
if len(items) > 0:
|
|
row = self.form.slb_path.row(items[0])
|
|
if row > 0:
|
|
item = self.form.slb_path.takeItem(row)
|
|
self.form.slb_path.insertItem(row-1, item)
|
|
self.update_startup_paths()
|
|
|
|
def move_path_down(self):
|
|
items = self.form.slb_path.selectedItems()
|
|
if len(items) > 0:
|
|
row = self.form.slb_path.row(items[0])
|
|
if row < self.form.slb_path.rowCount() - 1:
|
|
item = self.form.slb_path.takeItem(row)
|
|
self.form.slb_path.insertItem(row+1, item)
|
|
self.update_startup_paths()
|
|
|
|
def remove_path(self):
|
|
items = self.form.slb_path.selectedItems()
|
|
if len(items) > 0:
|
|
row = self.form.slb_path.row(items[0])
|
|
self.form.slb_path.takeItem(row)
|
|
self.update_startup_paths()
|