def __init__(self, options, parent=None): """options is a list of strings. Each a comma-delimited field list. Eg. ['artist, title', 'album, genre'] """ QDialog.__init__(self, parent) connect = lambda c, signal, s: self.connect(c, SIGNAL(signal), s) self.listbox = ListBox() self.listbox.setSelectionMode(self.listbox.ExtendedSelection) buttons = ListButtons() self.listbox.addItems(options) hbox = QHBoxLayout() hbox.addWidget(self.listbox, 1) hbox.addLayout(buttons) okcancel = OKCancel() vbox = QVBoxLayout() vbox.addLayout(hbox) vbox.addLayout(okcancel) self.setLayout(vbox) connect(buttons, "add", self.addPattern) connect(buttons, "edit", self.editItem) buttons.duplicate.setVisible(False) self.connect(okcancel, SIGNAL('ok'), self.applySettings) self.connect(okcancel, SIGNAL('cancel'), self.applySettings) self.listbox.connectToListButtons(buttons) self.listbox.editButton = buttons.edit connect(self.listbox, 'itemDoubleClicked(QListWidgetItem *)', self._doubleClicked)
def __init__(self, parent=None, filename=None, clipboard=None): QDialog.__init__(self, parent) self.setWindowTitle( translate('Text File -> Tag', "Import tags from text file")) winsettings('importwin', self) grid = QGridLayout() self.label = QLabel(translate('Text File -> Tag', "Text")) grid.addWidget(self.label, 0, 0) self.label = QLabel(translate('Text File -> Tag', "Tag preview")) grid.addWidget(self.label, 0, 2) self.file = QTextEdit() grid.addWidget(self.file, 1, 0, 1, 2) self.tags = QTextEdit() grid.addWidget(self.tags, 1, 2, 1, 2) self.tags.setLineWrapMode(QTextEdit.NoWrap) hbox = QHBoxLayout() self.patterncombo = QComboBox() self.patterncombo.setEditable(True) self.patterncombo.setDuplicatesEnabled(False) okcancel = OKCancel() self.ok = okcancel.ok self.cancel = okcancel.cancel self.openfile = QPushButton( translate('Text File -> Tag', "&Select File")) getclip = QPushButton(translate('Text File -> Tag', "&Paste Clipboard")) self.connect(getclip, SIGNAL('clicked()'), self.openClipBoard) hbox.addWidget(self.openfile) hbox.addWidget(getclip) hbox.addWidget(self.patterncombo, 1) hbox.addLayout(okcancel) grid.addLayout(hbox, 3, 0, 1, 4) self.setLayout(grid) self.connect(self.openfile, SIGNAL("clicked()"), self.openFile) self.connect(self.cancel, SIGNAL("clicked()"), self.close) self.connect(self.ok, SIGNAL("clicked()"), self.emitValues) if clipboard: self.openClipBoard() return self.lastDir = HOMEDIR if filename is not None: self.openFile(filename)
def __init__(self, field=None, parent=None, field_list=None, edit=True): QDialog.__init__(self, parent) self.setWindowTitle(translate('Edit Field', 'Edit Field')) winsettings('edit_field', self) self.vbox = QVBoxLayout() label = QLabel(translate('Edit Field', "&Field")) self.tagcombo = QComboBox() self.tagcombo.setEditable(True) label.setBuddy(self.tagcombo) completer = self.tagcombo.completer() completer.setCaseSensitivity(Qt.CaseSensitive) completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.tagcombo.setCompleter(completer) self.tagcombo.addItems(field_list if field_list else gettaglist()) #Get the previous field self.__oldField = field label1 = QLabel(translate('Edit Field', "&Value")) self.value = TextEdit() self.value.setTabChangesFocus(True) label1.setBuddy(self.value) okcancel = OKCancel() okcancel.ok.setText(translate('Edit Field', 'A&dd')) if field is not None: x = self.tagcombo.findText(field[0]) if x > -1: self.tagcombo.setCurrentIndex(x) else: self.tagcombo.setEditText(field[0]) self.value.setPlainText(field[1]) if edit: okcancel.ok.setText(translate('Edit Field', 'E&dit')) map(self.vbox.addWidget, [label, self.tagcombo, label1, self.value]) self.vbox.addLayout(okcancel) self.setLayout(self.vbox) self.connect(okcancel, SIGNAL("ok"), self.ok) self.connect(okcancel, SIGNAL("cancel"), self.close) self.value.setFocus() if self.__oldField else self.tagcombo.setFocus()
def __init__(self, parent=None, row=None, files=None, preview_mode=False, artwork=True, status=None): if status is None: status = {'cover_pattern': 'folder'} self.status = status QDialog.__init__(self, parent) winsettings('extendedtags', self) self.get_fieldlist = [] self.previewMode = preview_mode add = QColor.fromRgb(255, 255, 0) edit = QColor.fromRgb(0, 255, 0) remove = QColor.fromRgb(255, 0, 0) self._colors = {ADD: QBrush(add), EDIT: QBrush(edit), REMOVE: QBrush(remove)} self.table = QTableWidget(0, 2, self) self.table.setVerticalHeader(VerticalHeader()) self.table.verticalHeader().setVisible(False) self.table.setSortingEnabled(True) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setHorizontalHeaderLabels([ translate('Extended Tags', 'Field'), translate('Extended Tags', 'Value')]) header = self.table.horizontalHeader() header.setVisible(True) header.setSortIndicatorShown (True) header.setStretchLastSection (True) header.setSortIndicator (0, Qt.AscendingOrder) self.piclabel = PicWidget(buttons = True) self.connect(self.piclabel, SIGNAL('imageChanged'), self._imageChanged) if not isinstance(self.piclabel.removepic, QAction): self.connect(self.piclabel.removepic, SIGNAL('clicked()'), self.removePic) else: self.connect(self.piclabel.removepic, SIGNAL('triggered()'), self.removePic) if row >= 0 and files: buttons = MoveButtons(files, row) self.connect(buttons, SIGNAL('indexChanged'), self._prevnext) buttons.setVisible(True) else: buttons = MoveButtons([], row) buttons.setVisible(False) self._files = files self.okcancel = OKCancel() self.okcancel.insertWidget(0, buttons) self._reset = QToolButton() self._reset.setToolTip(translate('Extended Tags', 'Resets the selected fields to their original value.')) self._reset.setIcon(get_icon('edit-undo', ':/undo.png')) self.connect(self._reset, SIGNAL('clicked()'), self.resetFields) self.listbuttons = ListButtons() self.listbuttons.layout().addWidget(self._reset) self.listbuttons.moveup.hide() self.listbuttons.movedown.hide() listframe = QFrame() listframe.setFrameStyle(QFrame.Box) hbox = QHBoxLayout() hbox.addWidget(self.table, 1) hbox.addLayout(self.listbuttons, 0) listframe.setLayout(hbox) layout = QVBoxLayout() if artwork: imageframe = QFrame() imageframe.setFrameStyle(QFrame.Box) vbox = QVBoxLayout() vbox.setMargin(0) vbox.addWidget(self.piclabel) vbox.addStretch() vbox.addStrut(0) imageframe.setLayout(vbox) hbox = QHBoxLayout() hbox.addWidget(listframe, 1) hbox.addSpacing(4) hbox.addWidget(imageframe) hbox.addStrut(1) layout.addLayout(hbox) else: layout.addWidget(listframe) layout.addLayout(self.okcancel) self.setLayout(layout) self.connect(self.okcancel, SIGNAL("cancel"), self.closeMe) self.connect(self.table, SIGNAL("itemDoubleClicked(QTableWidgetItem *)"), self.editField) self.connect(self.table, SIGNAL("itemSelectionChanged()"), self._checkListBox) self.connect(self.okcancel, SIGNAL("ok"), self.okClicked) clicked = SIGNAL('clicked()') self.connect(self.listbuttons, SIGNAL('edit'), self.editField) self.connect(self.listbuttons.add, clicked, self.addField) self.connect(self.listbuttons.remove, clicked, self.removeField) self.connect(self.listbuttons, SIGNAL('duplicate'), self.duplicate) self.setMinimumSize(450, 350) self.canceled = False self.filechanged = False if row >= 0 and files: self._prevnext(row) else: self.loadFiles(files)
class ExTags(QDialog): """A dialog that shows you the tags in a file In addition, any attached cover art is shown.""" def __init__(self, parent=None, row=None, files=None, preview_mode=False, artwork=True, status=None): if status is None: status = {'cover_pattern': 'folder'} self.status = status QDialog.__init__(self, parent) winsettings('extendedtags', self) self.get_fieldlist = [] self.previewMode = preview_mode add = QColor.fromRgb(255, 255, 0) edit = QColor.fromRgb(0, 255, 0) remove = QColor.fromRgb(255, 0, 0) self._colors = {ADD: QBrush(add), EDIT: QBrush(edit), REMOVE: QBrush(remove)} self.table = QTableWidget(0, 2, self) self.table.setVerticalHeader(VerticalHeader()) self.table.verticalHeader().setVisible(False) self.table.setSortingEnabled(True) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setHorizontalHeaderLabels([ translate('Extended Tags', 'Field'), translate('Extended Tags', 'Value')]) header = self.table.horizontalHeader() header.setVisible(True) header.setSortIndicatorShown (True) header.setStretchLastSection (True) header.setSortIndicator (0, Qt.AscendingOrder) self.piclabel = PicWidget(buttons = True) self.connect(self.piclabel, SIGNAL('imageChanged'), self._imageChanged) if not isinstance(self.piclabel.removepic, QAction): self.connect(self.piclabel.removepic, SIGNAL('clicked()'), self.removePic) else: self.connect(self.piclabel.removepic, SIGNAL('triggered()'), self.removePic) if row >= 0 and files: buttons = MoveButtons(files, row) self.connect(buttons, SIGNAL('indexChanged'), self._prevnext) buttons.setVisible(True) else: buttons = MoveButtons([], row) buttons.setVisible(False) self._files = files self.okcancel = OKCancel() self.okcancel.insertWidget(0, buttons) self._reset = QToolButton() self._reset.setToolTip(translate('Extended Tags', 'Resets the selected fields to their original value.')) self._reset.setIcon(get_icon('edit-undo', ':/undo.png')) self.connect(self._reset, SIGNAL('clicked()'), self.resetFields) self.listbuttons = ListButtons() self.listbuttons.layout().addWidget(self._reset) self.listbuttons.moveup.hide() self.listbuttons.movedown.hide() listframe = QFrame() listframe.setFrameStyle(QFrame.Box) hbox = QHBoxLayout() hbox.addWidget(self.table, 1) hbox.addLayout(self.listbuttons, 0) listframe.setLayout(hbox) layout = QVBoxLayout() if artwork: imageframe = QFrame() imageframe.setFrameStyle(QFrame.Box) vbox = QVBoxLayout() vbox.setMargin(0) vbox.addWidget(self.piclabel) vbox.addStretch() vbox.addStrut(0) imageframe.setLayout(vbox) hbox = QHBoxLayout() hbox.addWidget(listframe, 1) hbox.addSpacing(4) hbox.addWidget(imageframe) hbox.addStrut(1) layout.addLayout(hbox) else: layout.addWidget(listframe) layout.addLayout(self.okcancel) self.setLayout(layout) self.connect(self.okcancel, SIGNAL("cancel"), self.closeMe) self.connect(self.table, SIGNAL("itemDoubleClicked(QTableWidgetItem *)"), self.editField) self.connect(self.table, SIGNAL("itemSelectionChanged()"), self._checkListBox) self.connect(self.okcancel, SIGNAL("ok"), self.okClicked) clicked = SIGNAL('clicked()') self.connect(self.listbuttons, SIGNAL('edit'), self.editField) self.connect(self.listbuttons.add, clicked, self.addField) self.connect(self.listbuttons.remove, clicked, self.removeField) self.connect(self.listbuttons, SIGNAL('duplicate'), self.duplicate) self.setMinimumSize(450, 350) self.canceled = False self.filechanged = False if row >= 0 and files: self._prevnext(row) else: self.loadFiles(files) def addField(self): win = EditField(parent=self, field_list=self.get_fieldlist) win.setModal(True) win.show() self.connect(win, SIGNAL("donewithmyshit"), self.editFieldBuddy) def _checkListBox(self): if self.table.rowCount() <= 0: self.table.setEnabled(False) self.listbuttons.edit.setEnabled(False) self.listbuttons.remove.setEnabled(False) self.listbuttons.duplicate.setEnabled(False) self._reset.setEnabled(False) else: self.table.setEnabled(True) self._reset.setEnabled(True) if len(self.table.selectedIndexes()) / 2 > 1: self.listbuttons.edit.setEnabled(False) self.listbuttons.duplicate.setEnabled(False) else: self.listbuttons.edit.setEnabled(True) self.listbuttons.remove.setEnabled(True) self.listbuttons.duplicate.setEnabled(True) self.table.resizeColumnToContents(0) def closeEvent(self,event): self.piclabel.close() QDialog.closeEvent(self,event) def closeMe(self): self.canceled = True self.close() def _deletePressed(self, item): if self.table.deletePressed: self.table.deletePressed = False self.removeField() def duplicate(self): self.editField(True) def editField(self, duplicate=False): """Opens a dialog to edit the currently selected Field. If duplicate is True the Edit Field dialog will be populated with the currently selected field's values. The new field'll then be added to the field list.""" row = self.table.currentRow() if row != -1: prevtag = self.get_field(row) if duplicate is True: win = EditField(prevtag, self, self.get_fieldlist, edit=False) else: win = EditField(prevtag, self, self.get_fieldlist) win.setModal(True) win.show() #Have to check for truth, because this method is #called by the doubleclicked signal. if duplicate is True: buddy = partial(self.editFieldBuddy, duplicate=True) else: buddy = self.editFieldBuddy self.connect(win, SIGNAL("donewithmyshit"), buddy) def editFieldBuddy(self, tag, value, prevtag = None, duplicate=False): rowcount = self.table.rowCount() if prevtag is not None: if duplicate: row = rowcount self._settag(rowcount, tag, value, ADD, self.previewMode, True) else: if tag == prevtag[0]: row = self.table.currentRow() self._settag(row, tag, value, EDIT, self.previewMode, True) if row + 1 < rowcount: self.table.selectRow(row + 1) else: cur_item = self.table.item(self.table.currentRow(),0) self.resetFields([cur_item]) self.table.setCurrentItem(cur_item, QItemSelectionModel.ClearAndSelect) self.table.selectRow(self.table.row(cur_item)) self.removeField() valitem = self._settag(rowcount, tag, value, ADD, self.previewMode, True) cur_item.linked = [valitem] else: self._settag(rowcount, tag, value, ADD, self.previewMode, True) self._checkListBox() self.filechanged = True self.table.clearSelection() def get_field(self, row, status = None): getitem = self.table.item item = getitem(row, 0) tag = unicode(item.text()) try: value = unicode(getitem(row, 1).text()) except AttributeError: value = unicode(self.table.cellWidget(row, 1).currentText()) if status: return (tag, value, item.status) else: return (tag, value) def _imageChanged(self): self.filechanged = True def loadSettings(self): cparser = PuddleConfig() self.get_fieldlist = gettaglist() get = lambda k,v : cparser.get('extendedtags', k, v, True) add = QColor.fromRgb(*get('add', [255, 255, 0])) edit = QColor.fromRgb(*get('edit', [0, 255,0])) remove = QColor.fromRgb(*get('remove', [255, 0, 0])) self._colors = {ADD: QBrush(add), EDIT: QBrush(edit), REMOVE: QBrush(remove)} item = self.table.item for row in xrange(self.table.rowCount()): field_item = self.get_item(row, 0) field_item.statusColors = self._colors field_item.status = field_item.status val_item = self.get_item(row, 1) val_item.statusColors = self._colors val_item.status = val_item.status def listtotag(self): get_field = self.get_field tags = {} lowered = {} listitems = [get_field(row, True) for row in xrange(self.table.rowCount())] for field, val, status in listitems: if status != REMOVE: if val == KEEP: continue l_field = field.lower() if l_field in lowered: tags[lowered[l_field]].append(val) else: lowered[l_field] = field tags[field] = [z.strip() for z in val.split('\\') if z.strip()] else: if field.lower() not in lowered: tags[field] = [] lowered[field.lower()] = field return tags def loadFiles(self, audios): if self.filechanged: self.save() self.filechanged = False self.table.clearContents() self.table.setRowCount(0) self.piclabel.lastfilename = audios[0].filepath self.piclabel.setEnabled(False) self.piclabel.setImages(None) if len(audios) == 1: audio = audios[0] self.setWindowTitle(audios[0].filepath) self._loadsingle(audio) else: self.setWindowTitle( translate('Extended Tags', 'Different files.')) from puddlestuff.tagmodel import status k = status['table'].model().taginfo[0] common, numvalues, imagetags = commontags(audios) images = common['__image'] del(common['__image']) previews = set(audios[0].preview) italics = set(audios[0].equal_fields()) self.piclabel.currentFile = audios[0] self.piclabel.filePattern = self.status['cover_pattern'] for audio in audios[1:]: previews = previews.intersection(audio.preview) italics = italics.intersection(audio.equal_fields()) row = 0 for field, values in common.iteritems(): if field in italics: preview = UNCHANGED #field in italics => field in previews. elif field in previews: preview = BOLD else: preview = UNCHANGED if numvalues[field] != len(audios): self._settag(row, field, values, multi=True) row += 1 else: if isinstance(values, basestring): self._settag(row, field, values, None, preview) row += 1 else: for v in values: self._settag(row, field, v, None, preview) row += 1 self.piclabel.setImageTags(imagetags) if images: self.piclabel.setEnabled(True) self.piclabel.setImages(images) else: self.piclabel.setImages(None) self.piclabel.setEnabled(True) if images == 0: self.piclabel.context = 'Cover Varies' self.piclabel.removepic.setEnabled(True) self._checkListBox() def _loadsingle(self, tags): items = [] d = tags.usertags.copy() italics = tags.equal_fields() self.piclabel.currentFile = tags self.piclabel.filePattern = self.status['cover_pattern'] for key, val in sorted(d.items()): if key in italics: preview = UNCHANGED elif key in tags.preview: preview = BOLD else: preview = UNCHANGED if isinstance(val, basestring): items.append([key, val, None, preview]) else: [items.append([key, z, None, preview]) for z in val] [self._settag(i, *item) for i, item in enumerate(items)] self.piclabel.lastfilename = tags.filepath if not tags.library: self.piclabel.setImageTags(tags.IMAGETAGS) if tags.IMAGETAGS: if '__image' in tags.preview: images = tags.preview['__image'] else: images = tags.images self.piclabel.setEnabled(True) if images: self.piclabel.setImages(deepcopy(images)) else: self.piclabel.setImages(None) self._checkListBox() self.setWindowTitle(tags[PATH]) def okClicked(self): self.save() self.close() def _prevnext(self, row): if self.filechanged: self.save() self.loadFiles([self._files[row]]) self.emit(SIGNAL('rowChanged'), row) def get_item(self, row, column=None): if column is None: #Assume QModelIndex passed column = row.column() row = row.row() item = self.table.item(row, column) if item is None: item = self.table.cellWidget(row, column) return item def removeField(self): tb = self.table tb.setSortingEnabled(False) to_remove = {} rows = [] for index in self.table.selectedIndexes(): row = index.row() item = self.get_item(index) if item.status == ADD: to_remove[row] = item rows.append(row) item.status = REMOVE item.status = REMOVE [tb.removeRow(tb.row(z)) for z in to_remove.values()] tb.setSortingEnabled(True) self.filechanged = True self._checkListBox() if rows: row = max(rows) self.table.clearSelection() if row + 1 < self.table.rowCount(): self.table.selectRow(row + 1) def resetFields(self, items=None): box = self.table to_remove = {} #Stores row: item values so that only one item #gets removed per row. max_row = -1 for index in box.selectedIndexes(): row = index.row() item = self.table.item(row, index.column()) if item is None: item = self.table.cellWidget(row, index.column()) for i in item.linked: try: to_remove[box.row(i)] = i except RuntimeError: pass item.reset() if row > max_row: max_row = row if item.status == REMOVE: to_remove[row] = item self.table.clearSelection() if max_row != -1 and max_row + 1 < self.table.rowCount(): self.table.selectRow(max_row + 1) for item in to_remove.values(): self.table.removeRow(self.table.row(item)) self._checkListBox() def removePic(self): if self.piclabel.context == 'Cover Varies': self.piclabel.context = 'No Images' self.piclabel.removepic.setEnabled(False) if not isinstance(self.piclabel.removepic, QAction): self.disconnect(self.piclabel.removepic, SIGNAL('clicked()'), self.removePic) else: self.discconnect(self.piclabel.removepic, SIGNAL('triggered()'), self.removePic) self.piclabel.setImages(None) def save(self): if not self.filechanged: table = self.table for row in range(table.rowCount()): combo = table.cellWidget(row, 1) if combo is not None and combo.currentIndex() != 0: self.filechanged = True break if not self.filechanged: return tags = self.listtotag() if self.piclabel.context != 'Cover Varies': if not self.piclabel.images: tags['__image'] = [] else: tags["__image"] = self.piclabel.images newtags = [z for z in tags if z not in self.get_fieldlist] if newtags and newtags != ['__image']: settaglist(newtags + self.get_fieldlist) self.emit(SIGNAL('extendedtags'), tags) def _settag(self, row, field, value, status=None, preview=False, check=False, multi=False): tb = self.table tb.setSortingEnabled(False) if row >= tb.rowCount(): tb.insertRow(row) field_item = StatusWidgetItem(field, status, self._colors, preview) tb.setItem(row, 0, field_item) if not multi and (len(value) == 1 or isinstance(value, basestring)): valitem = StatusWidgetItem(to_string(value), status, self._colors, preview) tb.setItem(row, 1, valitem) else: valitem = StatusWidgetCombo(value, status, self._colors, preview) tb.setCellWidget(row, 1, valitem) else: field_item = tb.item(row, 0) field_item.setText(field) field_item.status = status val_item = self.get_item(row, 1) val_item.setText(value) val_item.status = status if check: lowered_tag = field.lower() for row in xrange(tb.rowCount()): item = tb.item(row, 0) text = unicode(item.text()) if text != field and text.lower() == lowered_tag: item.setText(field) if item.status not in [ADD, REMOVE]: item.status = EDIT try: tb.item(row, 1).status = EDIT except AttributeError: tb.cellWidget(row, 1).status = EDIT tb.setSortingEnabled(True) return field_item
def __init__(self, title, controls, parent=None): """title => Dialog's title. controls is a list of 3-element-lists. The three 3-element lists consist of: description => Descriptive label for the control. control_type => One of TEXT, COMBO, CHECKBOX corresponding to a QLineEdit, QComboBox and QCheckBox being created respectively. default => Default arguments. Can be any string for TEXT, Must be a list of strings for COMBO as these will form the items selectable by the combo box. Can be either True or False for CheckBox. A dialog will be created with vertical layout. Like so: <label> <control> <label> <control> When the user has finished editing an 'editingFinished' signal will be emitted containing a list with the new value. The list will consist of the value for each control in the order given. For TEXT it'll a string. COMBO an integer corresponding to the selected index. True or False for CHECK. """ QDialog.__init__(self, parent) vbox = QVBoxLayout() self._controls = [] winsettings(title, self) self.setWindowTitle(translate("WebDB", 'Configure: %s') % title) for desc, ctype, default in controls: if ctype == TEXT: control = QLineEdit(default) vbox.addLayout(create_buddy(desc, control)) elif ctype == COMBO: control = QComboBox() control.addItems(default[0]) control.setCurrentIndex(default[1]) label = QLabel(desc) label.setBuddy(control) vbox.addWidget(label) vbox.addWidget(control) elif ctype == CHECKBOX: control = QCheckBox(desc) if default: control.setCheckState(Qt.Checked) else: control.setCheckState(Qt.Unchecked) vbox.addWidget(control) elif ctype == SPINBOX: control = QSpinBox() control.setMinimum(default[0]) control.setMaximum(default[1]) control.setValue(default[2]) vbox.addLayout(create_buddy(desc, control)) self._controls.append(control) okcancel = OKCancel() self.connect(okcancel, SIGNAL('ok'), self.okClicked) self.connect(okcancel, SIGNAL('cancel'), self.close) vbox.addLayout(okcancel) vbox.addStretch() self.setLayout(vbox)
def __init__(self, parent=None, row=None, files=None, preview_mode=False, artwork=True, status=None): if status is None: status = {'cover_pattern': 'folder'} self.status = status QDialog.__init__(self, parent) winsettings('extendedtags', self) self.get_fieldlist = [] self.previewMode = preview_mode add = QColor.fromRgb(255, 255, 0) edit = QColor.fromRgb(0, 255, 0) remove = QColor.fromRgb(255, 0, 0) self._colors = { ADD: QBrush(add), EDIT: QBrush(edit), REMOVE: QBrush(remove) } self.table = QTableWidget(0, 2, self) self.table.setVerticalHeader(VerticalHeader()) self.table.verticalHeader().setVisible(False) self.table.setSortingEnabled(True) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setHorizontalHeaderLabels([ translate('Extended Tags', 'Field'), translate('Extended Tags', 'Value') ]) header = self.table.horizontalHeader() header.setVisible(True) header.setSortIndicatorShown(True) header.setStretchLastSection(True) header.setSortIndicator(0, Qt.AscendingOrder) self.piclabel = PicWidget(buttons=True) self.connect(self.piclabel, SIGNAL('imageChanged'), self._imageChanged) if not isinstance(self.piclabel.removepic, QAction): self.connect(self.piclabel.removepic, SIGNAL('clicked()'), self.removePic) else: self.connect(self.piclabel.removepic, SIGNAL('triggered()'), self.removePic) if row >= 0 and files: buttons = MoveButtons(files, row) self.connect(buttons, SIGNAL('indexChanged'), self._prevnext) buttons.setVisible(True) else: buttons = MoveButtons([], row) buttons.setVisible(False) self._files = files self.okcancel = OKCancel() self.okcancel.insertWidget(0, buttons) self._reset = QToolButton() self._reset.setToolTip( translate('Extended Tags', 'Resets the selected fields to their original value.')) self._reset.setIcon(get_icon('edit-undo', ':/undo.png')) self.connect(self._reset, SIGNAL('clicked()'), self.resetFields) self.listbuttons = ListButtons() self.listbuttons.layout().addWidget(self._reset) self.listbuttons.moveup.hide() self.listbuttons.movedown.hide() listframe = QFrame() listframe.setFrameStyle(QFrame.Box) hbox = QHBoxLayout() hbox.addWidget(self.table, 1) hbox.addLayout(self.listbuttons, 0) listframe.setLayout(hbox) layout = QVBoxLayout() if artwork: imageframe = QFrame() imageframe.setFrameStyle(QFrame.Box) vbox = QVBoxLayout() vbox.setMargin(0) vbox.addWidget(self.piclabel) vbox.addStretch() vbox.addStrut(0) imageframe.setLayout(vbox) hbox = QHBoxLayout() hbox.addWidget(listframe, 1) hbox.addSpacing(4) hbox.addWidget(imageframe) hbox.addStrut(1) layout.addLayout(hbox) else: layout.addWidget(listframe) layout.addLayout(self.okcancel) self.setLayout(layout) self.connect(self.okcancel, SIGNAL("cancel"), self.closeMe) self.connect(self.table, SIGNAL("itemDoubleClicked(QTableWidgetItem *)"), self.editField) self.connect(self.table, SIGNAL("itemSelectionChanged()"), self._checkListBox) self.connect(self.okcancel, SIGNAL("ok"), self.okClicked) clicked = SIGNAL('clicked()') self.connect(self.listbuttons, SIGNAL('edit'), self.editField) self.connect(self.listbuttons.add, clicked, self.addField) self.connect(self.listbuttons.remove, clicked, self.removeField) self.connect(self.listbuttons, SIGNAL('duplicate'), self.duplicate) self.setMinimumSize(450, 350) self.canceled = False self.filechanged = False if row >= 0 and files: self._prevnext(row) else: self.loadFiles(files)
class ExTags(QDialog): """A dialog that shows you the tags in a file In addition, any attached cover art is shown.""" def __init__(self, parent=None, row=None, files=None, preview_mode=False, artwork=True, status=None): if status is None: status = {'cover_pattern': 'folder'} self.status = status QDialog.__init__(self, parent) winsettings('extendedtags', self) self.get_fieldlist = [] self.previewMode = preview_mode add = QColor.fromRgb(255, 255, 0) edit = QColor.fromRgb(0, 255, 0) remove = QColor.fromRgb(255, 0, 0) self._colors = { ADD: QBrush(add), EDIT: QBrush(edit), REMOVE: QBrush(remove) } self.table = QTableWidget(0, 2, self) self.table.setVerticalHeader(VerticalHeader()) self.table.verticalHeader().setVisible(False) self.table.setSortingEnabled(True) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setHorizontalHeaderLabels([ translate('Extended Tags', 'Field'), translate('Extended Tags', 'Value') ]) header = self.table.horizontalHeader() header.setVisible(True) header.setSortIndicatorShown(True) header.setStretchLastSection(True) header.setSortIndicator(0, Qt.AscendingOrder) self.piclabel = PicWidget(buttons=True) self.connect(self.piclabel, SIGNAL('imageChanged'), self._imageChanged) if not isinstance(self.piclabel.removepic, QAction): self.connect(self.piclabel.removepic, SIGNAL('clicked()'), self.removePic) else: self.connect(self.piclabel.removepic, SIGNAL('triggered()'), self.removePic) if row >= 0 and files: buttons = MoveButtons(files, row) self.connect(buttons, SIGNAL('indexChanged'), self._prevnext) buttons.setVisible(True) else: buttons = MoveButtons([], row) buttons.setVisible(False) self._files = files self.okcancel = OKCancel() self.okcancel.insertWidget(0, buttons) self._reset = QToolButton() self._reset.setToolTip( translate('Extended Tags', 'Resets the selected fields to their original value.')) self._reset.setIcon(get_icon('edit-undo', ':/undo.png')) self.connect(self._reset, SIGNAL('clicked()'), self.resetFields) self.listbuttons = ListButtons() self.listbuttons.layout().addWidget(self._reset) self.listbuttons.moveup.hide() self.listbuttons.movedown.hide() listframe = QFrame() listframe.setFrameStyle(QFrame.Box) hbox = QHBoxLayout() hbox.addWidget(self.table, 1) hbox.addLayout(self.listbuttons, 0) listframe.setLayout(hbox) layout = QVBoxLayout() if artwork: imageframe = QFrame() imageframe.setFrameStyle(QFrame.Box) vbox = QVBoxLayout() vbox.setMargin(0) vbox.addWidget(self.piclabel) vbox.addStretch() vbox.addStrut(0) imageframe.setLayout(vbox) hbox = QHBoxLayout() hbox.addWidget(listframe, 1) hbox.addSpacing(4) hbox.addWidget(imageframe) hbox.addStrut(1) layout.addLayout(hbox) else: layout.addWidget(listframe) layout.addLayout(self.okcancel) self.setLayout(layout) self.connect(self.okcancel, SIGNAL("cancel"), self.closeMe) self.connect(self.table, SIGNAL("itemDoubleClicked(QTableWidgetItem *)"), self.editField) self.connect(self.table, SIGNAL("itemSelectionChanged()"), self._checkListBox) self.connect(self.okcancel, SIGNAL("ok"), self.okClicked) clicked = SIGNAL('clicked()') self.connect(self.listbuttons, SIGNAL('edit'), self.editField) self.connect(self.listbuttons.add, clicked, self.addField) self.connect(self.listbuttons.remove, clicked, self.removeField) self.connect(self.listbuttons, SIGNAL('duplicate'), self.duplicate) self.setMinimumSize(450, 350) self.canceled = False self.filechanged = False if row >= 0 and files: self._prevnext(row) else: self.loadFiles(files) def addField(self): win = EditField(parent=self, field_list=self.get_fieldlist) win.setModal(True) win.show() self.connect(win, SIGNAL("donewithmyshit"), self.editFieldBuddy) def _checkListBox(self): if self.table.rowCount() <= 0: self.table.setEnabled(False) self.listbuttons.edit.setEnabled(False) self.listbuttons.remove.setEnabled(False) self.listbuttons.duplicate.setEnabled(False) self._reset.setEnabled(False) else: self.table.setEnabled(True) self._reset.setEnabled(True) if len(self.table.selectedIndexes()) / 2 > 1: self.listbuttons.edit.setEnabled(False) self.listbuttons.duplicate.setEnabled(False) else: self.listbuttons.edit.setEnabled(True) self.listbuttons.remove.setEnabled(True) self.listbuttons.duplicate.setEnabled(True) self.table.resizeColumnToContents(0) def closeEvent(self, event): self.piclabel.close() QDialog.closeEvent(self, event) def closeMe(self): self.canceled = True self.close() def _deletePressed(self, item): if self.table.deletePressed: self.table.deletePressed = False self.removeField() def duplicate(self): self.editField(True) def editField(self, duplicate=False): """Opens a dialog to edit the currently selected Field. If duplicate is True the Edit Field dialog will be populated with the currently selected field's values. The new field'll then be added to the field list.""" row = self.table.currentRow() if row != -1: prevtag = self.get_field(row) if duplicate is True: win = EditField(prevtag, self, self.get_fieldlist, edit=False) else: win = EditField(prevtag, self, self.get_fieldlist) win.setModal(True) win.show() #Have to check for truth, because this method is #called by the doubleclicked signal. if duplicate is True: buddy = partial(self.editFieldBuddy, duplicate=True) else: buddy = self.editFieldBuddy self.connect(win, SIGNAL("donewithmyshit"), buddy) def editFieldBuddy(self, tag, value, prevtag=None, duplicate=False): rowcount = self.table.rowCount() if prevtag is not None: if duplicate: row = rowcount self._settag(rowcount, tag, value, ADD, self.previewMode, True) else: if tag == prevtag[0]: row = self.table.currentRow() self._settag(row, tag, value, EDIT, self.previewMode, True) if row + 1 < rowcount: self.table.selectRow(row + 1) else: cur_item = self.table.item(self.table.currentRow(), 0) self.resetFields([cur_item]) self.table.setCurrentItem( cur_item, QItemSelectionModel.ClearAndSelect) self.table.selectRow(self.table.row(cur_item)) self.removeField() valitem = self._settag(rowcount, tag, value, ADD, self.previewMode, True) cur_item.linked = [valitem] else: self._settag(rowcount, tag, value, ADD, self.previewMode, True) self._checkListBox() self.filechanged = True self.table.clearSelection() def get_field(self, row, status=None): getitem = self.table.item item = getitem(row, 0) tag = unicode(item.text()) try: value = unicode(getitem(row, 1).text()) except AttributeError: value = unicode(self.table.cellWidget(row, 1).currentText()) if status: return (tag, value, item.status) else: return (tag, value) def _imageChanged(self): self.filechanged = True def loadSettings(self): cparser = PuddleConfig() self.get_fieldlist = gettaglist() get = lambda k, v: cparser.get('extendedtags', k, v, True) add = QColor.fromRgb(*get('add', [255, 255, 0])) edit = QColor.fromRgb(*get('edit', [0, 255, 0])) remove = QColor.fromRgb(*get('remove', [255, 0, 0])) self._colors = { ADD: QBrush(add), EDIT: QBrush(edit), REMOVE: QBrush(remove) } item = self.table.item for row in xrange(self.table.rowCount()): field_item = self.get_item(row, 0) field_item.statusColors = self._colors field_item.status = field_item.status val_item = self.get_item(row, 1) val_item.statusColors = self._colors val_item.status = val_item.status def listtotag(self): get_field = self.get_field tags = {} lowered = {} listitems = [ get_field(row, True) for row in xrange(self.table.rowCount()) ] for field, val, status in listitems: if status != REMOVE: if val == KEEP: continue l_field = field.lower() if l_field in lowered: tags[lowered[l_field]].append(val) else: lowered[l_field] = field tags[field] = [ z.strip() for z in val.split('\\') if z.strip() ] else: if field.lower() not in lowered: tags[field] = [] lowered[field.lower()] = field return tags def loadFiles(self, audios): if self.filechanged: self.save() self.filechanged = False self.table.clearContents() self.table.setRowCount(0) self.piclabel.lastfilename = audios[0].filepath self.piclabel.setEnabled(False) self.piclabel.setImages(None) if len(audios) == 1: audio = audios[0] self.setWindowTitle(audios[0].filepath) self._loadsingle(audio) else: self.setWindowTitle(translate('Extended Tags', 'Different files.')) from puddlestuff.tagmodel import status k = status['table'].model().taginfo[0] common, numvalues, imagetags = commontags(audios) images = common['__image'] del (common['__image']) previews = set(audios[0].preview) italics = set(audios[0].equal_fields()) self.piclabel.currentFile = audios[0] self.piclabel.filePattern = self.status['cover_pattern'] for audio in audios[1:]: previews = previews.intersection(audio.preview) italics = italics.intersection(audio.equal_fields()) row = 0 for field, values in common.iteritems(): if field in italics: preview = UNCHANGED #field in italics => field in previews. elif field in previews: preview = BOLD else: preview = UNCHANGED if numvalues[field] != len(audios): self._settag(row, field, values, multi=True) row += 1 else: if isinstance(values, basestring): self._settag(row, field, values, None, preview) row += 1 else: for v in values: self._settag(row, field, v, None, preview) row += 1 self.piclabel.setImageTags(imagetags) if images: self.piclabel.setEnabled(True) self.piclabel.setImages(images) else: self.piclabel.setImages(None) self.piclabel.setEnabled(True) if images == 0: self.piclabel.context = 'Cover Varies' self.piclabel.removepic.setEnabled(True) self._checkListBox() def _loadsingle(self, tags): items = [] d = tags.usertags.copy() italics = tags.equal_fields() self.piclabel.currentFile = tags self.piclabel.filePattern = self.status['cover_pattern'] for key, val in sorted(d.items()): if key in italics: preview = UNCHANGED elif key in tags.preview: preview = BOLD else: preview = UNCHANGED if isinstance(val, basestring): items.append([key, val, None, preview]) else: [items.append([key, z, None, preview]) for z in val] [self._settag(i, *item) for i, item in enumerate(items)] self.piclabel.lastfilename = tags.filepath if not tags.library: self.piclabel.setImageTags(tags.IMAGETAGS) if tags.IMAGETAGS: if '__image' in tags.preview: images = tags.preview['__image'] else: images = tags.images self.piclabel.setEnabled(True) if images: self.piclabel.setImages(deepcopy(images)) else: self.piclabel.setImages(None) self._checkListBox() self.setWindowTitle(tags[PATH]) def okClicked(self): self.save() self.close() def _prevnext(self, row): if self.filechanged: self.save() self.loadFiles([self._files[row]]) self.emit(SIGNAL('rowChanged'), row) def get_item(self, row, column=None): if column is None: #Assume QModelIndex passed column = row.column() row = row.row() item = self.table.item(row, column) if item is None: item = self.table.cellWidget(row, column) return item def removeField(self): tb = self.table tb.setSortingEnabled(False) to_remove = {} rows = [] for index in self.table.selectedIndexes(): row = index.row() item = self.get_item(index) if item.status == ADD: to_remove[row] = item rows.append(row) item.status = REMOVE item.status = REMOVE [tb.removeRow(tb.row(z)) for z in to_remove.values()] tb.setSortingEnabled(True) self.filechanged = True self._checkListBox() if rows: row = max(rows) self.table.clearSelection() if row + 1 < self.table.rowCount(): self.table.selectRow(row + 1) def resetFields(self, items=None): box = self.table to_remove = {} #Stores row: item values so that only one item #gets removed per row. max_row = -1 for index in box.selectedIndexes(): row = index.row() item = self.table.item(row, index.column()) if item is None: item = self.table.cellWidget(row, index.column()) for i in item.linked: try: to_remove[box.row(i)] = i except RuntimeError: pass item.reset() if row > max_row: max_row = row if item.status == REMOVE: to_remove[row] = item self.table.clearSelection() if max_row != -1 and max_row + 1 < self.table.rowCount(): self.table.selectRow(max_row + 1) for item in to_remove.values(): self.table.removeRow(self.table.row(item)) self._checkListBox() def removePic(self): if self.piclabel.context == 'Cover Varies': self.piclabel.context = 'No Images' self.piclabel.removepic.setEnabled(False) if not isinstance(self.piclabel.removepic, QAction): self.disconnect(self.piclabel.removepic, SIGNAL('clicked()'), self.removePic) else: self.discconnect(self.piclabel.removepic, SIGNAL('triggered()'), self.removePic) self.piclabel.setImages(None) def save(self): if not self.filechanged: table = self.table for row in range(table.rowCount()): combo = table.cellWidget(row, 1) if combo is not None and combo.currentIndex() != 0: self.filechanged = True break if not self.filechanged: return tags = self.listtotag() if self.piclabel.context != 'Cover Varies': if not self.piclabel.images: tags['__image'] = [] else: tags["__image"] = self.piclabel.images newtags = [z for z in tags if z not in self.get_fieldlist] if newtags and newtags != ['__image']: settaglist(newtags + self.get_fieldlist) self.emit(SIGNAL('extendedtags'), tags) def _settag(self, row, field, value, status=None, preview=False, check=False, multi=False): tb = self.table tb.setSortingEnabled(False) if row >= tb.rowCount(): tb.insertRow(row) field_item = StatusWidgetItem(field, status, self._colors, preview) tb.setItem(row, 0, field_item) if not multi and (len(value) == 1 or isinstance(value, basestring)): valitem = StatusWidgetItem(to_string(value), status, self._colors, preview) tb.setItem(row, 1, valitem) else: valitem = StatusWidgetCombo(value, status, self._colors, preview) tb.setCellWidget(row, 1, valitem) else: field_item = tb.item(row, 0) field_item.setText(field) field_item.status = status val_item = self.get_item(row, 1) val_item.setText(value) val_item.status = status if check: lowered_tag = field.lower() for row in xrange(tb.rowCount()): item = tb.item(row, 0) text = unicode(item.text()) if text != field and text.lower() == lowered_tag: item.setText(field) if item.status not in [ADD, REMOVE]: item.status = EDIT try: tb.item(row, 1).status = EDIT except AttributeError: tb.cellWidget(row, 1).status = EDIT tb.setSortingEnabled(True) return field_item
def __init__(self, parent=None, minval=0, numtracks=0, enablenumtracks=False): QDialog.__init__(self, parent) self.setWindowTitle( translate('Autonumbering Wizard', "Autonumbering Wizard")) winsettings('autonumbering', self) def hbox(*widgets): box = QHBoxLayout() [box.addWidget(z) for z in widgets] box.addStretch() return box vbox = QVBoxLayout() self._start = QSpinBox() self._start.setValue(minval) self._start.setMaximum(65536) startlabel = QLabel(translate('Autonumbering Wizard', "&Start: ")) startlabel.setBuddy(self._start) vbox.addLayout(hbox(startlabel, self._start)) self._padlength = QSpinBox() self._padlength.setValue(1) self._padlength.setMaximum(65535) self._padlength.setMinimum(1) label = QLabel( translate('Autonumbering Wizard', 'Max length after padding with zeroes: ')) label.setBuddy(self._padlength) vbox.addLayout(hbox(label, self._padlength)) self._separator = QCheckBox( translate('Autonumbering Wizard', "Add track &separator ['/']: Number of tracks")) self._numtracks = QSpinBox() self._numtracks.setEnabled(False) self._numtracks.setMaximum(65535) if numtracks: self._numtracks.setValue(numtracks) self._restart_numbering = QCheckBox( translate('Autonumbering Wizard', "&Restart numbering at each directory group.")) self.connect(self._restart_numbering, SIGNAL("stateChanged(int)"), self.showDirectorySplittingOptions) vbox.addLayout(hbox(self._separator, self._numtracks)) vbox.addWidget(self._restart_numbering) self.custom_numbering_widgets = [] label = QLabel( translate('Autonumbering Wizard', "Group tracks using pattern:: ")) self.grouping = QLineEdit() label.setBuddy(self.grouping) vbox.addLayout(hbox(label, self.grouping)) self.custom_numbering_widgets.extend([label, self.grouping]) label = QLabel(translate('Autonumbering Wizard', "Output field: ")) self.output_field = QComboBox() label.setBuddy(self.output_field) self.output_field.setEditable(True) completer = self.output_field.completer() completer.setCaseSensitivity(Qt.CaseSensitive) completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.output_field.setCompleter(completer) self.output_field.addItems(gettaglist()) vbox.addLayout(hbox(label, self.output_field)) self.custom_numbering_widgets.extend([label, self.output_field]) self.count_by_group = QCheckBox( translate(u'Autonumbering Wizard', u'Increase counter only on group change')) vbox.addWidget(self.count_by_group) self.custom_numbering_widgets.append(self.count_by_group) okcancel = OKCancel() vbox.addLayout(okcancel) self.setLayout(vbox) self.connect(okcancel, SIGNAL('ok'), self.emitValuesAndSave) self.connect(okcancel, SIGNAL('cancel'), self.close) self.connect(self._separator, SIGNAL("stateChanged(int)"), lambda v: self._numtracks.setEnabled(v == Qt.Checked)) # self.connect(self._restart_numbering, SIGNAL("stateChanged(int)"), # self.showDirectorySplittingOptions) self._separator.setChecked(enablenumtracks) self._loadSettings()