Add the ability to install plugins while Picard is running.

This commit is contained in:
Michael Wiencek
2011-06-25 20:24:13 -05:00
parent 6fa4d20c78
commit f2db1b33ce
5 changed files with 294 additions and 211 deletions

View File

@@ -20,15 +20,27 @@
from PyQt4 import QtCore
import imp
import os.path
import shutil
import picard.plugins
import traceback
_suffixes = [s[0] for s in imp.get_suffixes()]
_package_entries = ["__init__.py", "__init__.pyc", "__init__.pyo"]
def plugin_name_from_module(module):
name = module.__name__
if name.startswith("picard.plugins"):
return name[15:]
def plugin_name_from_path(path):
path = os.path.normpath(path)
file = os.path.basename(path)
if os.path.isdir(path):
for entry in _package_entries:
if os.path.isfile(os.path.join(path, entry)):
return file
else:
if file in _package_entries:
return None
name, ext = os.path.splitext(file)
if ext in _suffixes:
return name
return None
@@ -41,6 +53,10 @@ class ExtensionPoint(QtCore.QObject):
def register(self, module, item):
if module.startswith("picard.plugins"):
module = module[15:]
for i, (module_, item_) in enumerate(self.__items):
if module == module_:
self.__items[i] = (module, item)
return
else:
module = None
self.__items.append((module, item))
@@ -56,15 +72,23 @@ class PluginWrapper(object):
def __init__(self, module, plugindir):
self.module = module
self.compatible = False
self.dir = plugindir
def __get_name(self):
try:
return self.module.PLUGIN_NAME
except AttributeError:
return self.module.__name__
return self.module_name
name = property(__get_name)
def __get_module_name(self):
name = self.module.__name__
if name.startswith("picard.plugins"):
name = name[15:]
return name
module_name = property(__get_module_name)
def __get_author(self):
try:
return self.module.PLUGIN_AUTHOR
@@ -104,57 +128,67 @@ class PluginManager(QtCore.QObject):
QtCore.QObject.__init__(self)
self.plugins = []
def load(self, plugindir):
def load_plugindir(self, plugindir):
if not os.path.isdir(plugindir):
self.log.debug("Plugin directory %r doesn't exist", plugindir)
return
names = set()
suffixes = [s[0] for s in imp.get_suffixes()]
package_entries = ["__init__.py", "__init__.pyc", "__init__.pyo"]
for name in os.listdir(plugindir):
if name in package_entries:
continue
path = os.path.join(plugindir, name)
if os.path.isdir(path):
for entry in package_entries:
if os.path.isfile(os.path.join(path, entry)):
break
else:
continue
else:
name, suffix = os.path.splitext(name)
if suffix not in suffixes:
continue
if hasattr(picard.plugins, name):
self.log.debug("Plugin %r already loaded!", name)
else:
for path in [os.path.join(plugindir, file) for file in os.listdir(plugindir)]:
name = plugin_name_from_path(path)
if name:
names.add(name)
for name in names:
self.log.debug("Loading plugin %r", name)
info = imp.find_module(name, [plugindir])
try:
plugin_module = imp.load_module('picard.plugins.' + name, *info)
plugin = PluginWrapper(plugin_module, plugindir)
for version in list(plugin.api_versions):
found = False
for api_version in picard.api_versions:
if api_version.startswith(version):
setattr(picard.plugins, name, plugin_module)
self.load_plugin(name, plugindir)
def load_plugin(self, name, plugindir):
self.log.debug("Loading plugin %r", name)
info = imp.find_module(name, [plugindir])
plugin = None
try:
plugin_module = imp.load_module("picard.plugins." + name, *info)
plugin = PluginWrapper(plugin_module, plugindir)
for version in list(plugin.api_versions):
for api_version in picard.api_versions:
if api_version.startswith(version):
plugin.compatible = True
setattr(picard.plugins, name, plugin_module)
for i, p in enumerate(self.plugins):
if name == p.module_name:
self.plugins[i] = plugin
break
else:
self.plugins.append(plugin)
found = True
break
if found:
break
else:
self.log.info("Plugin '%s' from '%s' is not compatible "
"with this version of Picard." %
(plugin.name, plugin.file))
except:
self.log.error(traceback.format_exc())
if info[0] is not None:
info[0].close()
continue
break
else:
self.log.info("Plugin '%s' from '%s' is not compatible"
" with this version of Picard." % (plugin.name, plugin.file))
except:
self.log.error(traceback.format_exc())
if info[0] is not None:
info[0].close()
return plugin
def install_plugin(self, path, dest):
plugin_name = plugin_name_from_path(path)
plugin_dir = self.tagger.user_plugin_dir
if plugin_name:
try:
dest_exists = os.path.exists(dest)
same_file = os.path.samefile(path, dest) if dest_exists else False
if os.path.isfile(path) and not (dest_exists and same_file):
shutil.copy(path, dest)
elif os.path.isdir(path) and not same_file:
if dest_exists:
shutil.rmtree(dest)
shutil.copytree(path, dest)
plugin = self.load_plugin(plugin_name, plugin_dir)
if plugin is not None:
self.emit(QtCore.SIGNAL("plugin_installed"), plugin, False)
except OSError, IOError:
self.tagger.log.debug("Unable to copy %s to plugin folder %s" % (path, plugin_dir))
def enabled(self, name):
return True

View File

@@ -185,14 +185,14 @@ class Tagger(QtGui.QApplication):
# Load plugins
self.pluginmanager = PluginManager()
user_plugin_dir = os.path.join(self.userdir, "plugins")
if not os.path.exists(user_plugin_dir):
os.makedirs(user_plugin_dir)
self.pluginmanager.load(user_plugin_dir)
self.user_plugin_dir = os.path.join(self.userdir, "plugins")
if not os.path.exists(self.user_plugin_dir):
os.makedirs(self.user_plugin_dir)
self.pluginmanager.load_plugindir(self.user_plugin_dir)
if hasattr(sys, "frozen"):
self.pluginmanager.load(os.path.join(os.path.dirname(sys.argv[0]), "plugins"))
self.pluginmanager.load_plugindir(os.path.join(os.path.dirname(sys.argv[0]), "plugins"))
else:
self.pluginmanager.load(os.path.join(os.path.dirname(__file__), "plugins"))
self.pluginmanager.load_plugindir(os.path.join(os.path.dirname(__file__), "plugins"))
self.puidmanager = PUIDManager()

View File

@@ -22,7 +22,6 @@ import os.path
import sys
from PyQt4 import QtCore, QtGui
from picard.config import TextOption
from picard.plugin import plugin_name_from_module
from picard.ui.options import OptionsPage, register_options_page
from picard.ui.ui_options_plugins import Ui_PluginsOptionsPage
@@ -47,40 +46,65 @@ class PluginsOptionsPage(OptionsPage):
super(PluginsOptionsPage, self).__init__(parent)
self.ui = Ui_PluginsOptionsPage()
self.ui.setupUi(self)
self.items = {}
self.connect(self.ui.plugins, QtCore.SIGNAL("itemSelectionChanged()"), self.change_details)
self.user_plugin_dir = os.path.join(self.tagger.userdir, "plugins")
self.ui.plugins.__class__.mimeTypes = self.mimeTypes
self.ui.plugins.__class__.dropEvent = self.dropEvent
if sys.platform == "win32":
self.loader="file:///%s"
self.loader="file:///%s"
else:
self.loader="file://%s"
self.connect(self.ui.install_plugin, QtCore.SIGNAL("clicked()"), self.open_plugins)
self.connect(self.ui.folder_open, QtCore.SIGNAL("clicked()"), self.open_plugin_dir)
self.connect(self.ui.plugin_download, QtCore.SIGNAL("clicked()"), self.open_plugin_site)
self.connect(self.tagger.pluginmanager, QtCore.SIGNAL("plugin_installed"), self.plugin_installed)
def load(self):
plugins = sorted(self.tagger.pluginmanager.plugins, cmp=cmp_plugins)
enabled_plugins = self.config.setting["enabled_plugins"].split()
self.items = {}
firstitem = None
for plugin in plugins:
item = QtGui.QTreeWidgetItem(self.ui.plugins)
item.setText(0, plugin.name)
if plugin_name_from_module(plugin.module) in enabled_plugins:
item.setCheckState(0, QtCore.Qt.Checked)
else:
item.setCheckState(0, QtCore.Qt.Unchecked)
item.setText(1, plugin.version)
item.setText(2, plugin.author)
enabled = plugin.module_name in enabled_plugins
item = self.add_plugin_item(plugin, enabled=enabled)
if not firstitem:
firstitem = item
self.items[item] = plugin
self.ui.plugins.header().resizeSections(QtGui.QHeaderView.ResizeToContents)
self.ui.plugins.setCurrentItem(firstitem)
def plugin_installed(self, plugin):
if not plugin.compatible:
msgbox = QtGui.QMessageBox(self)
msgbox.setText(u"The plugin %s is not compatible with this version of Picard." % plugin.name)
msgbox.setStandardButtons(QtGui.QMessageBox.Ok)
msgbox.setDefaultButton(QtGui.QMessageBox.Ok)
msgbox.exec_()
return
for i, p in self.items.items():
if plugin.module_name == p.module_name:
enabled = i.checkState(0) == QtCore.Qt.Checked
self.add_plugin_item(plugin, enabled=enabled, item=i)
break
else:
self.add_plugin_item(plugin)
def add_plugin_item(self, plugin, enabled=False, item=None):
if item is None:
item = QtGui.QTreeWidgetItem(self.ui.plugins)
item.setText(0, plugin.name)
if enabled:
item.setCheckState(0, QtCore.Qt.Checked)
else:
item.setCheckState(0, QtCore.Qt.Unchecked)
item.setText(1, plugin.version)
item.setText(2, plugin.author)
self.ui.plugins.header().resizeSections(QtGui.QHeaderView.ResizeToContents)
self.items[item] = plugin
return item
def save(self):
enabled_plugins = []
for item, plugin in self.items.iteritems():
if item.checkState(0) == QtCore.Qt.Checked:
enabled_plugins.append(plugin_name_from_module(plugin.module))
enabled_plugins.append(plugin.module_name)
self.config.setting["enabled_plugins"] = " ".join(enabled_plugins)
def change_details(self):
@@ -99,11 +123,38 @@ class PluginsOptionsPage(OptionsPage):
text.append("<b>" + _("File") + "</b>: " + plugin.file[len(plugin.dir)+1:])
self.ui.details.setText("<p>%s</p>" % "<br/>\n".join(text))
def open_plugins(self):
files = QtGui.QFileDialog.getOpenFileNames(self, "", "/", "Picard plugin (*.py *.pyc)")
if files:
files = map(unicode, files)
for path in files:
self.install_plugin(path)
def install_plugin(self, path):
file = os.path.basename(path)
dest = os.path.join(self.tagger.user_plugin_dir, file)
if os.path.exists(dest):
msgbox = QtGui.QMessageBox(self)
msgbox.setText("A plugin named %s is already installed." % file)
msgbox.setInformativeText("Do you want to overwrite the existing plugin?")
msgbox.setStandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
msgbox.setDefaultButton(QtGui.QMessageBox.No)
if msgbox.exec_() == QtGui.QMessageBox.No:
return
self.tagger.pluginmanager.install_plugin(path, dest)
def open_plugin_dir(self):
QtGui.QDesktopServices.openUrl(QtCore.QUrl(self.loader % self.user_plugin_dir, QtCore.QUrl.TolerantMode))
QtGui.QDesktopServices.openUrl(QtCore.QUrl(self.loader % self.tagger.user_plugin_dir, QtCore.QUrl.TolerantMode))
def open_plugin_site(self):
QtGui.QDesktopServices.openUrl(QtCore.QUrl("http://musicbrainz.org/doc/Picard_Plugins", QtCore.QUrl.TolerantMode))
def mimeTypes(self):
return ["text/uri-list"]
def dropEvent(self, event):
for path in [os.path.normpath(unicode(u.toLocalFile())) for u in event.mimeData().urls()]:
self.install_plugin(path)
register_options_page(PluginsOptionsPage)

View File

@@ -1,126 +1,117 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'ui\options_plugins.ui'
#
# Created: Thu Oct 22 00:04:48 2009
# by: PyQt4 UI code generator 4.6
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
class Ui_PluginsOptionsPage(object):
def setupUi(self, PluginsOptionsPage):
PluginsOptionsPage.setObjectName("PluginsOptionsPage")
PluginsOptionsPage.resize(406, 297)
self.vboxlayout = QtGui.QVBoxLayout(PluginsOptionsPage)
self.vboxlayout.setSpacing(6)
self.vboxlayout.setMargin(9)
self.vboxlayout.setObjectName("vboxlayout")
self.splitter = QtGui.QSplitter(PluginsOptionsPage)
self.splitter.setOrientation(QtCore.Qt.Vertical)
self.splitter.setHandleWidth(2)
self.splitter.setObjectName("splitter")
self.groupBox_2 = QtGui.QGroupBox(self.splitter)
self.groupBox_2.setObjectName("groupBox_2")
self.vboxlayout1 = QtGui.QVBoxLayout(self.groupBox_2)
self.vboxlayout1.setSpacing(2)
self.vboxlayout1.setMargin(9)
self.vboxlayout1.setObjectName("vboxlayout1")
self.plugins = QtGui.QTreeWidget(self.groupBox_2)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.plugins.sizePolicy().hasHeightForWidth())
self.plugins.setSizePolicy(sizePolicy)
self.plugins.setRootIsDecorated(False)
self.plugins.setObjectName("plugins")
self.vboxlayout1.addWidget(self.plugins)
self.horizontalLayout = QtGui.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.folder_open = QtGui.QPushButton(self.groupBox_2)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.folder_open.sizePolicy().hasHeightForWidth())
self.folder_open.setSizePolicy(sizePolicy)
self.folder_open.setObjectName("folder_open")
self.horizontalLayout.addWidget(self.folder_open)
self.plugin_download = QtGui.QPushButton(self.groupBox_2)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.plugin_download.sizePolicy().hasHeightForWidth())
self.plugin_download.setSizePolicy(sizePolicy)
self.plugin_download.setObjectName("plugin_download")
self.horizontalLayout.addWidget(self.plugin_download)
self.vboxlayout1.addLayout(self.horizontalLayout)
self.groupBox = QtGui.QGroupBox(self.splitter)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.groupBox.sizePolicy().hasHeightForWidth())
self.groupBox.setSizePolicy(sizePolicy)
self.groupBox.setObjectName("groupBox")
self.vboxlayout2 = QtGui.QVBoxLayout(self.groupBox)
self.vboxlayout2.setSpacing(0)
self.vboxlayout2.setMargin(9)
self.vboxlayout2.setObjectName("vboxlayout2")
self.scrollArea = QtGui.QScrollArea(self.groupBox)
self.scrollArea.setEnabled(True)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.scrollArea.sizePolicy().hasHeightForWidth())
self.scrollArea.setSizePolicy(sizePolicy)
self.scrollArea.setFrameShape(QtGui.QFrame.HLine)
self.scrollArea.setFrameShadow(QtGui.QFrame.Plain)
self.scrollArea.setLineWidth(0)
self.scrollArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
self.scrollArea.setObjectName("scrollArea")
self.scrollAreaWidgetContents = QtGui.QWidget(self.scrollArea)
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 368, 69))
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.scrollAreaWidgetContents.sizePolicy().hasHeightForWidth())
self.scrollAreaWidgetContents.setSizePolicy(sizePolicy)
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.verticalLayout = QtGui.QVBoxLayout(self.scrollAreaWidgetContents)
self.verticalLayout.setSizeConstraint(QtGui.QLayout.SetNoConstraint)
self.verticalLayout.setContentsMargins(0, 0, 6, 0)
self.verticalLayout.setObjectName("verticalLayout")
self.details = QtGui.QLabel(self.scrollAreaWidgetContents)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.details.sizePolicy().hasHeightForWidth())
self.details.setSizePolicy(sizePolicy)
self.details.setMinimumSize(QtCore.QSize(0, 0))
self.details.setFrameShape(QtGui.QFrame.Box)
self.details.setLineWidth(0)
self.details.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
self.details.setWordWrap(True)
self.details.setIndent(0)
self.details.setOpenExternalLinks(True)
self.details.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
self.details.setObjectName("details")
self.verticalLayout.addWidget(self.details)
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.vboxlayout2.addWidget(self.scrollArea)
self.vboxlayout.addWidget(self.splitter)
self.retranslateUi(PluginsOptionsPage)
QtCore.QMetaObject.connectSlotsByName(PluginsOptionsPage)
def retranslateUi(self, PluginsOptionsPage):
self.groupBox_2.setTitle(_("Plugins"))
self.plugins.headerItem().setText(0, _("Name"))
self.plugins.headerItem().setText(1, _("Version"))
self.plugins.headerItem().setText(2, _("Author"))
self.folder_open.setText(_("Open plugin folder"))
self.plugin_download.setText(_("Download plugins"))
self.groupBox.setTitle(_("Details"))
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'ui/options_plugins.ui'
#
# Created: Mon Jun 20 19:52:58 2011
# by: PyQt4 UI code generator 4.8.4
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
class Ui_PluginsOptionsPage(object):
def setupUi(self, PluginsOptionsPage):
PluginsOptionsPage.setObjectName("PluginsOptionsPage")
PluginsOptionsPage.resize(513, 312)
self.vboxlayout = QtGui.QVBoxLayout(PluginsOptionsPage)
self.vboxlayout.setObjectName("vboxlayout")
self.splitter = QtGui.QSplitter(PluginsOptionsPage)
self.splitter.setOrientation(QtCore.Qt.Vertical)
self.splitter.setHandleWidth(2)
self.splitter.setObjectName("splitter")
self.groupBox_2 = QtGui.QGroupBox(self.splitter)
self.groupBox_2.setObjectName("groupBox_2")
self.vboxlayout1 = QtGui.QVBoxLayout(self.groupBox_2)
self.vboxlayout1.setSpacing(2)
self.vboxlayout1.setObjectName("vboxlayout1")
self.plugins = QtGui.QTreeWidget(self.groupBox_2)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Expanding)
sizePolicy.setHeightForWidth(self.plugins.sizePolicy().hasHeightForWidth())
self.plugins.setSizePolicy(sizePolicy)
self.plugins.setAcceptDrops(True)
self.plugins.setDragDropMode(QtGui.QAbstractItemView.DropOnly)
self.plugins.setRootIsDecorated(False)
self.plugins.setObjectName("plugins")
self.vboxlayout1.addWidget(self.plugins)
self.horizontalLayout = QtGui.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.install_plugin = QtGui.QPushButton(self.groupBox_2)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
sizePolicy.setHeightForWidth(self.install_plugin.sizePolicy().hasHeightForWidth())
self.install_plugin.setSizePolicy(sizePolicy)
self.install_plugin.setObjectName("install_plugin")
self.horizontalLayout.addWidget(self.install_plugin)
self.folder_open = QtGui.QPushButton(self.groupBox_2)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
sizePolicy.setHeightForWidth(self.folder_open.sizePolicy().hasHeightForWidth())
self.folder_open.setSizePolicy(sizePolicy)
self.folder_open.setObjectName("folder_open")
self.horizontalLayout.addWidget(self.folder_open)
self.plugin_download = QtGui.QPushButton(self.groupBox_2)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
sizePolicy.setHeightForWidth(self.plugin_download.sizePolicy().hasHeightForWidth())
self.plugin_download.setSizePolicy(sizePolicy)
self.plugin_download.setObjectName("plugin_download")
self.horizontalLayout.addWidget(self.plugin_download)
self.vboxlayout1.addLayout(self.horizontalLayout)
self.groupBox = QtGui.QGroupBox(self.splitter)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
sizePolicy.setHeightForWidth(self.groupBox.sizePolicy().hasHeightForWidth())
self.groupBox.setSizePolicy(sizePolicy)
self.groupBox.setObjectName("groupBox")
self.vboxlayout2 = QtGui.QVBoxLayout(self.groupBox)
self.vboxlayout2.setSpacing(0)
self.vboxlayout2.setObjectName("vboxlayout2")
self.scrollArea = QtGui.QScrollArea(self.groupBox)
self.scrollArea.setEnabled(True)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
sizePolicy.setHeightForWidth(self.scrollArea.sizePolicy().hasHeightForWidth())
self.scrollArea.setSizePolicy(sizePolicy)
self.scrollArea.setFrameShape(QtGui.QFrame.HLine)
self.scrollArea.setFrameShadow(QtGui.QFrame.Plain)
self.scrollArea.setLineWidth(0)
self.scrollArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
self.scrollArea.setObjectName("scrollArea")
self.scrollAreaWidgetContents = QtGui.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 459, 76))
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
sizePolicy.setHeightForWidth(self.scrollAreaWidgetContents.sizePolicy().hasHeightForWidth())
self.scrollAreaWidgetContents.setSizePolicy(sizePolicy)
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.verticalLayout = QtGui.QVBoxLayout(self.scrollAreaWidgetContents)
self.verticalLayout.setSizeConstraint(QtGui.QLayout.SetNoConstraint)
self.verticalLayout.setContentsMargins(0, 0, 6, 0)
self.verticalLayout.setObjectName("verticalLayout")
self.details = QtGui.QLabel(self.scrollAreaWidgetContents)
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
sizePolicy.setHeightForWidth(self.details.sizePolicy().hasHeightForWidth())
self.details.setSizePolicy(sizePolicy)
self.details.setFrameShape(QtGui.QFrame.Box)
self.details.setLineWidth(0)
self.details.setText("")
self.details.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
self.details.setWordWrap(True)
self.details.setIndent(0)
self.details.setOpenExternalLinks(True)
self.details.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
self.details.setObjectName("details")
self.verticalLayout.addWidget(self.details)
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.vboxlayout2.addWidget(self.scrollArea)
self.vboxlayout.addWidget(self.splitter)
self.retranslateUi(PluginsOptionsPage)
QtCore.QMetaObject.connectSlotsByName(PluginsOptionsPage)
def retranslateUi(self, PluginsOptionsPage):
self.groupBox_2.setTitle(_("Plugins"))
self.plugins.headerItem().setText(0, _("Name"))
self.plugins.headerItem().setText(1, _("Version"))
self.plugins.headerItem().setText(2, _("Author"))
self.install_plugin.setText(_(u"Install plugin…"))
self.folder_open.setText(_("Open plugin folder"))
self.plugin_download.setText(_("Download plugins"))
self.groupBox.setTitle(_("Details"))

View File

@@ -6,17 +6,11 @@
<rect>
<x>0</x>
<y>0</y>
<width>406</width>
<height>297</height>
<width>513</width>
<height>312</height>
</rect>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<number>9</number>
</property>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
@@ -33,9 +27,6 @@
<property name="spacing">
<number>2</number>
</property>
<property name="margin">
<number>9</number>
</property>
<item>
<widget class="QTreeWidget" name="plugins">
<property name="sizePolicy">
@@ -44,6 +35,12 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DropOnly</enum>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
@@ -66,6 +63,19 @@
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="install_plugin">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Install plugin…</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="folder_open">
<property name="sizePolicy">
@@ -110,9 +120,6 @@
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>9</number>
</property>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="enabled">
@@ -147,8 +154,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>368</width>
<height>69</height>
<width>459</width>
<height>76</height>
</rect>
</property>
<property name="sizePolicy">