"Fossies" - the Fresh Open Source Software Archive

Member "ffmulticonverter-1.8.0/ffmulticonverter/presets_dlgs.py" (20 Jun 2016, 18607 Bytes) of package /linux/privat/ffmulticonverter-1.8.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "presets_dlgs.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.7.2_vs_1.8.0.

    1 # Copyright (C) 2011-2016 Ilias Stamatis <stamatis.iliass@gmail.com>
    2 #
    3 # This program is free software: you can redistribute it and/or modify
    4 # it under the terms of the GNU General Public License as published by
    5 # the Free Software Foundation, either version 3 of the License, or
    6 # (at your option) any later version.
    7 #
    8 # This program is distributed in the hope that it will be useful,
    9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
   10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   11 # GNU General Public License for more details.
   12 #
   13 # You should have received a copy of the GNU General Public License
   14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
   15 
   16 import os
   17 import re
   18 import time
   19 import xml.etree.ElementTree as etree
   20 
   21 from PyQt5.QtCore import Qt, QTimer
   22 from PyQt5.QtWidgets import (
   23         QDialog, QDialogButtonBox, QFileDialog, QLabel, QLineEdit, QListWidget,
   24         QMessageBox, QPushButton, QShortcut, QSizePolicy, QSpacerItem
   25         )
   26 
   27 from ffmulticonverter import utils
   28 from ffmulticonverter import config
   29 
   30 
   31 class ShowPresets(QDialog):
   32     def __init__(self, parent=None, choose=False):
   33         super(ShowPresets, self).__init__(parent)
   34 
   35         self.original_presets_file = utils.find_presets_file(
   36                 config.presets_file_name,
   37                 config.presets_lookup_dirs,
   38                 config.presets_lookup_virtenv
   39                 )
   40         self.current_presets_file = config.presets_file
   41 
   42         self.presQLW = QListWidget()
   43         labelQL = QLabel(self.tr('Preset label'))
   44         self.labelQLE = QLineEdit()
   45         self.labelQLE.setReadOnly(True)
   46         commandQL = QLabel(self.tr('Preset command line parameters'))
   47         self.commandQLE = QLineEdit()
   48         self.commandQLE.setReadOnly(True)
   49         extQL = QLabel(self.tr('Output file extension'))
   50         self.extQLE = QLineEdit()
   51         self.extQLE.setReadOnly(True)
   52         addQPB = QPushButton(self.tr('Add'))
   53         self.deleteQPB = QPushButton(self.tr('Delete'))
   54         self.delete_allQPB = QPushButton(self.tr('Delete all'))
   55         self.editQPB = QPushButton(self.tr('Edit'))
   56         searchQL = QLabel(self.tr('Search'))
   57         self.searchQLE = QLineEdit()
   58         okQPB = QPushButton(self.tr('OK'))
   59         okQPB.setDefault(True)
   60 
   61         spc1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
   62         spc2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
   63         spc3 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
   64 
   65         grid = utils.add_to_grid(
   66                 [self.delete_allQPB, addQPB, spc1],
   67                 [self.deleteQPB, self.editQPB, spc2]
   68                 )
   69 
   70         hlayout = utils.add_to_layout(
   71                 'h', searchQL, self.searchQLE, None, okQPB)
   72 
   73         final_layout = utils.add_to_layout(
   74                 'v', self.presQLW, labelQL, self.labelQLE, commandQL,
   75                 self.commandQLE, extQL, self.extQLE, grid, spc3, hlayout
   76                 )
   77 
   78         self.setLayout(final_layout)
   79 
   80         okQPB.clicked.connect(self.accept)
   81         self.presQLW.currentRowChanged.connect(self.show_preset)
   82         addQPB.clicked.connect(self.add_preset)
   83         self.deleteQPB.clicked.connect(self.delete_preset)
   84         self.delete_allQPB.clicked.connect(self.delete_all_presets)
   85         self.editQPB.clicked.connect(self.edit_preset)
   86         self.searchQLE.textEdited.connect(self.search)
   87         if choose:
   88             self.presQLW.doubleClicked.connect(okQPB.click)
   89 
   90         del_shortcut = QShortcut(self)
   91         del_shortcut.setKey(Qt.Key_Delete)
   92         del_shortcut.activated.connect(self.delete_preset)
   93 
   94         self.resize(430, 480)
   95         self.setWindowTitle(self.tr('Edit Presets'))
   96 
   97         QTimer.singleShot(0, self.load_xml)
   98         QTimer.singleShot(0, self.fill_presQLW)
   99 
  100     def load_xml(self):
  101         """Load xml tree and set xml root."""
  102         if os.path.isfile(self.current_presets_file):
  103             self.tree = etree.parse(self.current_presets_file)
  104         else:
  105             self.tree = etree.parse(self.original_presets_file)
  106             if not os.path.exists(config.config_dir):
  107                 os.makedirs(config.config_dir)
  108 
  109         self.root = self.tree.getroot()
  110 
  111     def set_buttons_clear_lineEdits(self):
  112         """Enable or disable button's and clear lineEdits."""
  113         enable = bool(self.presQLW)
  114         self.editQPB.setEnabled(enable)
  115         self.deleteQPB.setEnabled(enable)
  116         self.delete_allQPB.setEnabled(enable)
  117         if not enable:
  118             self.labelQLE.clear()
  119             self.commandQLE.clear()
  120             self.extQLE.clear()
  121 
  122     def fill_presQLW(self):
  123         """Clear self.presQLW and to it presets' tags."""
  124         self.presQLW.clear()
  125         for i in sorted([y.tag for y in self.root]):
  126             elem = self.root.find(i)
  127             self.presQLW.addItem(utils.XmlListItem(i, elem))
  128 
  129         self.presQLW.setCurrentRow(0)
  130         self.set_buttons_clear_lineEdits()
  131         self.searchQLE.clear()
  132 
  133     def show_preset(self):
  134         """Fill LineEdits with current xml element's values."""
  135         try:
  136             xml_elem = self.presQLW.currentItem().xml_element
  137         except AttributeError:
  138             return
  139 
  140         self.labelQLE.setText(xml_elem[0].text)
  141         self.commandQLE.setText(xml_elem[1].text)
  142         self.commandQLE.home(False)
  143         self.extQLE.setText(xml_elem[2].text)
  144 
  145     def add_preset(self):
  146         """Open AddorEditPreset() dialog and add a preset xml root."""
  147         dialog = AddorEditPreset(None, False, self)
  148         if dialog.exec_():
  149             element = etree.Element(dialog.name_text)
  150             label = etree.Element('label')
  151             label.text = dialog.label_text
  152             command = etree.Element('params')
  153             command.text = dialog.command_text
  154             ext = etree.Element('extension')
  155             ext.text = dialog.ext_text
  156             category = etree.Element('category')
  157             category.text = 'Scattered'
  158 
  159             for num, elem in enumerate([label, command, ext, category]):
  160                 element.insert(num, elem)
  161             index = sorted(
  162                     [i.tag for i in self.root] + [dialog.name_text]
  163                     ).index(dialog.name_text)
  164             self.root.insert(index, element)
  165             self.save_tree()
  166             self.fill_presQLW()
  167 
  168     def delete_preset(self):
  169         """
  170         Ask user wether he wants to delete the selected preset.
  171         If so, delete the preset from xml root.
  172         """
  173         try:
  174             xml_elem = self.presQLW.currentItem().xml_element
  175         except AttributeError:
  176             return
  177 
  178         reply = QMessageBox.question(self, 'FF Multi Converter - ' + self.tr(
  179             'Delete Preset'), self.tr('Are you sure that you want to delete '
  180             'the {0} preset?'.format(xml_elem.tag)),
  181             QMessageBox.Yes|QMessageBox.Cancel)
  182         if reply == QMessageBox.Yes:
  183             self.root.remove(xml_elem)
  184             self.save_tree()
  185             self.fill_presQLW()
  186 
  187     def delete_all_presets(self):
  188         """
  189         Ask user if he wants to delete all presets.
  190         If so, clear xml root.
  191         """
  192         reply = QMessageBox.question(self, 'FF Multi Converter - ' + self.tr(
  193             'Delete Preset'), self.tr('Are you sure that you want to delete '
  194             'all presets?'), QMessageBox.Yes|QMessageBox.Cancel)
  195         if reply == QMessageBox.Yes:
  196             self.root.clear()
  197             self.save_tree()
  198             self.fill_presQLW()
  199 
  200     def edit_preset(self):
  201         """Call the AddorEditPreset() dialog and update xml element's values."""
  202         elem = self.presQLW.currentItem().xml_element
  203         dialog = AddorEditPreset(elem, True)
  204 
  205         if dialog.exec_():
  206             elem.tag = dialog.name_text
  207             elem[0].text = dialog.label_text
  208             elem[1].text = dialog.command_text
  209             elem[2].text = dialog.ext_text
  210             self.save_tree()
  211             self.fill_presQLW()
  212 
  213     def search(self):
  214         """
  215         Search for keywords in presets data.
  216 
  217         Show a preset only if its tag, label or extension matches any of
  218         search string's tokens.
  219         """
  220         txt = self.searchQLE.text().strip().lower()
  221         if not txt:
  222             self.fill_presQLW()
  223             return
  224 
  225         self.presQLW.clear()
  226         for i in txt.split(' '):
  227             for p in sorted([y.tag for y in self.root]):
  228                 elem = self.root.find(p)
  229                 if (i.strip() and (
  230                         i in elem.tag.lower()
  231                         or i in elem[0].text.lower()
  232                         or i in elem[2].text.lower())):
  233                     self.presQLW.addItem(utils.XmlListItem(p, elem))
  234 
  235         self.presQLW.setCurrentRow(0)
  236         self.set_buttons_clear_lineEdits()
  237 
  238     def save_tree(self):
  239         """Save xml tree."""
  240         with open(self.current_presets_file, 'wb') as _file:
  241             try:
  242                 etree.ElementTree(self.root).write(_file)
  243             except:
  244                 pass
  245 
  246     def import_presets(self):
  247         """Import an xml tree."""
  248         title = 'FF Multi Converter - Import'
  249         reply = QMessageBox.question(self, title, self.tr('All current '
  250                 'presets will be deleted.\nAre you sure that you want to '
  251                 'continue?'), QMessageBox.Yes|QMessageBox.Cancel)
  252         if reply == QMessageBox.Yes:
  253             fname = QFileDialog.getOpenFileName(self, title, config.home)[0]
  254             if fname:
  255                 msg = self.tr('Successful import!')
  256                 try:
  257                     self.tree = etree.parse(fname)
  258                 except:
  259                     msg = self.tr('Import failed!')
  260                 else:
  261                     self.root = self.tree.getroot()
  262                     self.save_tree()
  263                 QMessageBox.information(self, title, msg)
  264 
  265     def export_presets(self):
  266         """Export the xml tree."""
  267         fname = QFileDialog.getSaveFileName(
  268                 self, 'FF Multi Converter - ' + self.tr('Export presets'),
  269                 config.home + '/presets-' + time.strftime("%Y-%m-%d") + '.xml'
  270                 )[0]
  271         if fname:
  272             self.load_xml()
  273             with open(fname, 'wb') as _file:
  274                 try:
  275                     etree.ElementTree(self.root).write(_file)
  276                 except:
  277                     pass
  278 
  279     def reset(self):
  280         """Import the default xml tree."""
  281         reply = QMessageBox.question(self, 'FF Multi Converter - ' + self.tr(
  282             'Delete Preset'), self.tr('Are you sure that you want to restore '
  283             'the default presets?'), QMessageBox.Yes|QMessageBox.Cancel)
  284         if reply == QMessageBox.Yes:
  285             if os.path.exists(self.current_presets_file):
  286                 os.remove(self.current_presets_file)
  287 
  288             QMessageBox.information(self, ' ',
  289                     self.tr('Default presets restored successfully.'))
  290 
  291     def synchronize(self):
  292         """
  293         Synchronize current presets with default presets.
  294 
  295         For each preset in default presets:
  296         - if not contained in current presets, add it to current presets
  297         - if has the same name with some preset in current presets but
  298           different attributes, then add this preset to current presets and
  299           add an '__OLD' suffix to matching preset's name
  300         """
  301         reply = QMessageBox.question(
  302                 self, 'FF Multi Converter - ' +
  303                 self.tr('Presets Synchronization'),
  304                 self.tr('Current presets and default presets will be merged. '
  305                 'Are you sure that you want to continue?'),
  306                 QMessageBox.Yes|QMessageBox.Cancel
  307                 )
  308         if not reply == QMessageBox.Yes:
  309             return
  310 
  311         def_tree = etree.parse(self.original_presets_file)
  312         def_root = def_tree.getroot()
  313         self.load_xml()
  314 
  315         for i in def_root:
  316             for n, y in enumerate(self.root):
  317                 if i.tag == y.tag:
  318                     if not (i[0].text == y[0].text
  319                             and i[1].text == y[1].text
  320                             and i[2].text == y[2].text):
  321                         # copy element and change its name
  322                         elem = etree.Element(y.tag)
  323                         label = etree.Element('label')
  324                         label.text = i[0].text
  325                         command = etree.Element('params')
  326                         command.text = i[1].text
  327                         ext = etree.Element('extension')
  328                         ext.text = i[2].text
  329                         elem.insert(0, label)
  330                         elem.insert(1, command)
  331                         elem.insert(2, ext)
  332 
  333                         y.tag = y.tag + config.presets_old
  334                         self.root.insert(n+1, elem)
  335                     break
  336             else:
  337                 # preset not found
  338                 index = sorted([x.tag for x in self.root] +
  339                                [i.tag]).index(i.tag)
  340                 self.root.insert(index, i)
  341 
  342         self.save_tree()
  343 
  344         QMessageBox.information(self, ' ',
  345                 self.tr(
  346                 'Synchronization completed.\nYour presets are up to date!'))
  347 
  348     def remove_old(self):
  349         """Remove those xml elements which their tags has an __OLD suffix."""
  350         reply = QMessageBox.question(self, 'FF Multi Converter - ' + self.tr(
  351             'Remove old presets'), self.tr('All presets with an __OLD suffix '
  352             'will be deleted. Are you sure that you want to continue?'),
  353             QMessageBox.Yes|QMessageBox.Cancel)
  354         if not reply == QMessageBox.Yes:
  355             return
  356 
  357         self.load_xml()
  358 
  359         for i in self.root:
  360             if i.tag.endswith(config.presets_old):
  361                 self.root.remove(i)
  362 
  363         self.save_tree()
  364 
  365         QMessageBox.information(self, ' ',
  366                 self.tr('Old presets successfully removed.'))
  367 
  368     def accept(self):
  369         """
  370         Save current xml element's values in order to be used from
  371         main program and close (accept) dialog.
  372         """
  373         self.the_command = None
  374         if self.presQLW:
  375             self.the_command = self.presQLW.currentItem()\
  376                     .xml_element[1].text
  377             self.the_extension = self.presQLW.currentItem()\
  378                     .xml_element[2].text
  379         QDialog.accept(self)
  380 
  381 
  382 class AddorEditPreset(QDialog):
  383     def __init__(self, xml_element, edit=False, parent=None):
  384         super(AddorEditPreset, self).__init__(parent)
  385 
  386         nameQL = QLabel(self.tr('Preset name (one word, A-z, 0-9)'))
  387         self.nameQLE = QLineEdit()
  388         labelQL = QLabel(self.tr('Preset label'))
  389         self.labelQLE = QLineEdit()
  390         commandQL = QLabel(self.tr('Preset command line parameters'))
  391         self.commandQLE = QLineEdit()
  392         extQL = QLabel(self.tr('Output file extension'))
  393         self.extQLE = QLineEdit()
  394         buttonBox = QDialogButtonBox(
  395                 QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
  396 
  397         final_layout = utils.add_to_layout(
  398                 'v', nameQL, self.nameQLE, labelQL, self.labelQLE,
  399                 commandQL, self.commandQLE, extQL, self.extQLE, buttonBox
  400                 )
  401 
  402         self.setLayout(final_layout)
  403 
  404         buttonBox.accepted.connect(self.accept)
  405         buttonBox.rejected.connect(self.reject)
  406 
  407         self.resize(410, 280)
  408 
  409         if edit:
  410             self.nameQLE.setText(xml_element.tag)
  411             self.labelQLE.setText(xml_element[0].text)
  412             self.commandQLE.setText(xml_element[1].text)
  413             self.commandQLE.home(False)
  414             self.extQLE.setText(xml_element[2].text)
  415 
  416             title = self.tr('Edit {0}'.format(xml_element.tag))
  417         else:
  418             title = self.tr('Add preset')
  419 
  420         self.resize(410, 280)
  421         self.setWindowTitle(title)
  422 
  423     def validate_data(self):
  424         """
  425         Extract data from GUI and check if everything is ok to continue.
  426 
  427         Check if:
  428         - Any lineEdit is empty.
  429         - Preset name and extension meet the qualifications.
  430 
  431         Return True if all tests pass, else False.
  432         """
  433         self.name_text = self.nameQLE.text().strip()
  434         self.label_text = self.labelQLE.text().strip()
  435         self.command_text = self.commandQLE.text().strip()
  436         self.ext_text = self.extQLE.text().strip()
  437 
  438         if not self.name_text:
  439             QMessageBox.warning(
  440                     self, 'Edit Preset - ' + self.tr('Error!'),
  441                     self.tr("Preset name can't be left blank.")
  442                     )
  443             self.nameQLE.setFocus()
  444             return False
  445         if not re.match('^(?![xX][mM][lL])[A-Za-z][A-Za-z0-9_.-:]*$',
  446                 self.name_text):
  447             QMessageBox.warning(
  448                     self, 'Edit Preset - ' + self.tr('Error!'),
  449                     self.tr(
  450                     'Preset name must be one word, start with a letter and '
  451                     'contain only letters, digits, underscores, hyphens, '
  452                     'colons and periods. It cannot also start with xml.')
  453                     )
  454             self.nameQLE.selectAll()
  455             self.nameQLE.setFocus()
  456             return False
  457         if not self.label_text:
  458             QMessageBox.warning(
  459                     self, 'Edit Preset - ' + self.tr('Error!'),
  460                     self.tr("Preset label can't be left blank.")
  461                     )
  462             self.labelQLE.setFocus()
  463             return False
  464         if not self.command_text:
  465             QMessageBox.warning(
  466                     self, 'Edit Preset - ' + self.tr('Error!'),
  467                     self.tr("Command label can't be left blank.")
  468                     )
  469             self.commandQLE.setFocus()
  470             return False
  471         if not self.ext_text:
  472             QMessageBox.warning(
  473                     self, 'Edit Preset - ' + self.tr('Error!'),
  474                     self.tr("Extension label can't be left blank.")
  475                     )
  476             self.extQLE.setFocus()
  477             return False
  478         if len(self.ext_text.split()) != 1 or self.ext_text[0] == '.':
  479             QMessageBox.warning(
  480                     self, 'Edit Preset - ' + self.tr('Error!'),
  481                     self.tr(
  482                     'Extension must be one word and must not start with a  dot.')
  483                     )
  484             self.extQLE.selectAll()
  485             self.extQLE.setFocus()
  486             return False
  487         return True
  488 
  489     def accept(self):
  490         if self.validate_data():
  491             QDialog.accept(self)