source: orange/Orange/orng/addOnPack.py @ 10581:94a831b04ec3

Revision 10581:94a831b04ec3, 11.4 KB checked in by markotoplak, 2 years ago (diff)

Moved some other scripts from misc to utils and Orange imports and canvas not works, although not systematically tested.

Line 
1from PyQt4.QtCore import *
2from PyQt4.QtGui import *
3
4import os, sys, uuid
5
6import OWGUI
7
8import Orange.utils.addons
9
10
11class AddOnPackDlg(QWizard):
12    def __init__(self, app, parent=None, flags=0):
13        #QMainWindow.__init__(self, parent)
14        QWizard.__init__(self)
15        width, height = 600, 500
16        self.resize(width, height)
17
18        desktop = app.desktop()
19        deskh = desktop.screenGeometry(desktop.primaryScreen()).height()
20        deskw = desktop.screenGeometry(desktop.primaryScreen()).width()
21        h = max(0, deskh / 2 - height / 2)  # if the window is too small, resize the window to desktop size
22        w = max(0, deskw / 2 - width / 2)
23        self.move(w, h)
24       
25        self.setWindowTitle("Orange Add-on Packaging Wizard")
26 
27        self.init_addon_selection_page()
28        self.init_metadata_page()
29        self.init_optional_metadata_page()
30        self.initDestinationPage()
31
32    def init_addon_selection_page(self):
33        self.addon_selection_page = page = QWizardPage()
34        page.setTitle("Add-on Selection")
35        page.setLayout(QVBoxLayout())
36        self.addPage( page )
37        import os
38
39        p = OWGUI.widgetBox(page, "Select a registered add-on to pack", orientation="horizontal")
40        self.aolist = OWGUI.listBox(p, self, callback=self.list_selection_callback)
41        self.registered_addons = Orange.utils.addons.registered_addons
42        for ao in self.registered_addons:
43            self.aolist.addItem(ao.name)
44        pbtn = OWGUI.widgetBox(p, orientation="vertical")
45        btn_custom_location = OWGUI.button(pbtn, self, "Custom Location...", callback=self.select_custom_location)
46        pbtn.layout().addStretch(1)
47       
48        self.directory = ""
49        self.e_directory = OWGUI.lineEdit(page, self, "directory", label="Add-on directory: ", callback=self.directory_change_callback, callbackOnType=True)
50        page.isComplete = lambda page: os.path.isdir(os.path.join(self.directory, "widgets"))
51   
52    def list_selection_callback(self):
53        index = self.aolist.currentIndex()
54        item = self.registered_addons[index.row()] if index else None
55        if item:
56            self.e_directory.setText(item.directory)
57        else:
58            self.e_directory.setText("")
59   
60    def select_custom_location(self):
61        import os
62        dir = str(QFileDialog.getExistingDirectory(self, "Select the folder that contains the add-on:", self.e_directory.text()))
63        if dir != "":
64            if os.path.split(dir)[1] == "widgets":     # register a dir above the dir that contains the widget folder
65                dir = os.path.split(dir)[0]
66            self.e_directory.setText(dir)
67            self.aolist.setCurrentItem(None)
68               
69    def directory_change_callback(self):
70        self.addon_selection_page.emit(SIGNAL("completeChanged()"))
71       
72    def new_textedit(self, label, parent):
73        vpanel = OWGUI.widgetBox(parent, orientation="vertical")
74        l = QLabel(label)
75        e = QTextEdit()
76        e.setTabChangesFocus(True)
77        vpanel.layout().addWidget(l)
78        vpanel.layout().addWidget(e)
79        return e
80
81    def init_metadata_page(self):
82        self.metaDataPage = page = QWizardPage()
83        page.setTitle("Add-on Information")
84        page.setLayout(QVBoxLayout())
85        self.addPage( page )
86        page.initializePage = self.load_metadata
87
88        p = OWGUI.widgetBox(page, "Enter the following information about your add-on", orientation="vertical")
89        import os
90        self.e_id = eId = OWGUI.lineEdit(p, self, None, "Globally unique ID:")
91        eId.setReadOnly(True)
92        eId.setFocusPolicy(Qt.NoFocus)
93
94        h = OWGUI.widgetBox(p, orientation="horizontal")
95        self.name = ""
96        self.preferred_dir_touched = True
97        self.e_name = e_name = OWGUI.lineEdit(h, self, "name", "Name:", callback=self.name_change_callback, callbackOnType=True)
98        self.e_version = eVersion = OWGUI.lineEdit(h, self, None, "Version:")
99        def nonEmpty(editor):
100            return bool(str(editor.text() if hasattr(editor, "text") else editor.toPlainText()).strip()) 
101        e_name.isComplete = lambda: nonEmpty(e_name) 
102        def versionIsComplete():
103            try:
104                map(int, str(e_version.text()).strip().split("."))
105                return bool(str(e_version.text()).strip())
106            except:
107                return False
108        eVersion.isComplete = versionIsComplete
109
110        self.e_description = eDescription = self.new_textedit("Description:", p)
111        eDescription.isComplete = lambda: nonEmpty(eDescription)
112       
113        def evalPage():
114            page.emit(SIGNAL("completeChanged()"))
115        for nonOptional in [e_name, eVersion, eDescription]:
116            QObject.connect(nonOptional, SIGNAL("textChanged(const QString &)"), evalPage)
117            QObject.connect(nonOptional, SIGNAL("textChanged()"), evalPage)
118        page.isComplete = lambda page: e_name.isComplete() and eVersion.isComplete() and eDescription.isComplete()       
119
120    def init_optional_metadata_page(self):
121        self.optionalMetaDataPage = page = QWizardPage()
122        page.setTitle("Optional Add-on Information")
123        page.setLayout(QVBoxLayout())
124        self.addPage( page )
125
126        p = OWGUI.widgetBox(page, "Optionally, enter the following information about your add-on", orientation="vertical")
127        self.preferredDir = ""
128        self.e_preferreddir = ePreferredDir = OWGUI.lineEdit(p, self, "preferredDir", "Preferred directory name (within add-ons directory; optional):", callback=self.preferred_dir_change_callback, callbackOnType=True)
129        self.e_homepage = eHomePage = OWGUI.lineEdit(p, self, None, "Add-on webpage (optional):")
130        self.preferred_dir_touched = False
131       
132        h = OWGUI.widgetBox(p, orientation="horizontal")
133        self.e_tags = self.new_textedit("Tags (one per line, optional):", h)
134        self.e_aorganizations = self.new_textedit("Contributing organizations (one per line, optional):", h)
135        h = OWGUI.widgetBox(p, orientation="horizontal")
136        self.e_aauthors = self.new_textedit("Authors (one per line, optional):", h)
137        self.e_acontributors = self.new_textedit("Contributors (one per line, optional):", h)
138        for noWrapEdit in [self.e_tags, self.e_aauthors, self.e_acontributors, self.e_aorganizations]:
139            noWrapEdit.setLineWrapMode(QTextEdit.NoWrap)
140       
141        def formatList(control, ev):
142            entries = control.parseEntries()
143            control.setPlainText("\n".join(entries))
144            QTextEdit.focusOutEvent(control, ev)
145        for listEdit in [self.e_tags, self.e_aauthors, self.e_acontributors, self.e_aorganizations]:
146            listEdit.parseEntries = lambda control=listEdit: [entry for entry in map(lambda x: x.strip(), unicode(control.toPlainText()).split("\n")) if entry]
147            listEdit.focusOutEvent = formatList
148   
149   
150    def name_change_callback(self):
151        if not self.preferred_dir_touched:
152            name = unicode(self.e_name.text()).strip()
153            pd = name.replace(" ", "_").replace("/", "-").replace("\\", "_")
154            if str(self.e_preferreddir.text()) or (name != pd):
155                self.e_preferreddir.setText(pd)
156                self.preferred_dir_touched = False
157   
158    def preferred_dir_change_callback(self):
159        self.preferred_dir_touched = bool(str(self.e_preferreddir.text()))
160   
161    def load_metadata(self):
162        import os
163        xml = os.path.join(self.directory, "addon.xml")
164        if os.path.isfile(xml):
165            self.ao = Orange.utils.addons.OrangeAddOn(xmlfile=open(xml, 'r'))
166        else:
167            self.ao = Orange.utils.addons.OrangeAddOn()
168        denone = lambda x: x if x else "" 
169        self.e_id.setText(self.ao.id if self.ao.id else str(uuid.uuid1()))
170        self.e_name.setText(self.ao.name or os.path.split(self.directory)[1])
171        self.e_version.setText(Orange.utils.addons.suggest_version(self.ao.version_str))
172        self.e_description.setPlainText(denone(self.ao.description))
173        self.e_preferreddir.setText(denone(self.ao.preferred_directory))
174        self.preferred_dir_touched = bool(denone(self.ao.preferred_directory))
175        self.e_homepage.setText(denone(self.ao.homepage))
176        self.e_tags.setPlainText("\n".join(self.ao.tags))
177        self.e_aorganizations.setPlainText("\n".join(self.ao.author_organizations))
178        self.e_aauthors.setPlainText("\n".join(self.ao.author_creators))
179        self.e_acontributors.setPlainText("\n".join(self.ao.author_contributors))
180       
181    def initDestinationPage(self):
182        self.optionalMetaDataPage = page = QWizardPage()
183        page.setTitle("Destination")
184        page.setLayout(QVBoxLayout())
185        self.addPage( page )
186
187        self.prepare_only = False
188        chk_prepare_only = OWGUI.checkBox(page, self, "prepare_only", "Do not pack, only prepare addon.xml and arrange documentation", callback = self.callbackPrepareOnlyChange)
189           
190        p = OWGUI.widgetBox(page, "Filesystem", orientation="horizontal")
191        self.oaofilename = ""
192        self.e_oaofilename = OWGUI.lineEdit(p, self, "oaofilename")
193        button = OWGUI.button(p, self, '...', callback = self.browseDestinationFile, disabled=0)
194        button.setIcon(self.style().standardIcon(QStyle.SP_DirOpenIcon))
195        button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed)
196
197        def initOao(page):
198            if not self.oaofilename:
199                self.e_oaofilename.setText(self.directory+".oao")
200        page.initializePage = initOao
201       
202        p = OWGUI.widgetBox(page, "Repository", orientation="vertical")
203        OWGUI.label(p, self, "Uploading into repositories is not yet implemented, sorry.")
204   
205    def callbackPrepareOnlyChange(self):
206        self.e_oaofilename.setEnabled(not self.prepare_only)
207   
208    def browseDestinationFile(self):
209        filename = str(QFileDialog.getSaveFileName(self, 'Save Packed Orange Add-on', self.oaofilename, "*.oao"))
210
211        if filename:
212            self.e_oaofilename.setText(filename)
213       
214    def accept(self):
215        rao = Orange.utils.addons.OrangeRegisteredAddOn(self.ao.name, self.directory)
216        rao.prepare(self.ao.id, self.ao.name, str(self.e_version.text()), unicode(self.e_description.toPlainText()), self.e_tags.parseEntries(),
217                    self.e_aorganizations.parseEntries(), self.e_aauthors.parseEntries(), self.e_acontributors.parseEntries(), self.preferredDir,
218                    str(self.e_homepage.text()))
219       
220        if not self.prepare_only:
221            import zipfile
222            oao = zipfile.ZipFile(self.oaofilename, 'w')
223            dirs = os.walk(self.directory)
224            for (dir, subdirs, files) in dirs:
225                relDir = os.path.relpath(dir, self.directory) if hasattr(os.path, "relpath") else dir.replace(self.directory, "")
226                while relDir.startswith("/") or relDir.startswith("\\"):
227                    relDir = relDir[1:]
228                for file in files:
229                    oao.write(os.path.join(dir, file), os.path.join(relDir, file))
230            QMessageBox.information( None, "Done", 'Your add-on has been successfully packed!', QMessageBox.Ok + QMessageBox.Default)
231        else:
232            QMessageBox.information( None, "Done", 'Your add-on has been successfully prepared!', QMessageBox.Ok + QMessageBox.Default)
233           
234        QWizard.accept(self)
235
236def main(argv=None):
237    if argv == None:
238        argv = sys.argv
239
240    app = QApplication(sys.argv)
241    dlg = AddOnPackDlg(app)
242    dlg.show()
243    app.exec_()
244    app.closeAllWindows()
245
246if __name__ == "__main__":
247    sys.exit(main())
Note: See TracBrowser for help on using the repository browser.