Export confirm window

AutoTag confirm window
Other fixes and such

git-svn-id: http://comictagger.googlecode.com/svn/trunk@321 6c5673fe-1810-88d6-992b-cd32ca31540c
This commit is contained in:
beville 2013-01-22 00:19:59 +00:00
parent b8e8c6433a
commit b712226b1e
9 changed files with 545 additions and 21 deletions

49
autotagstartwindow.py Normal file
View File

@ -0,0 +1,49 @@
"""
A PyQT4 dialog to confirm and set options for auto-tag
"""
"""
Copyright 2012 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from PyQt4 import QtCore, QtGui, uic
from settings import ComicTaggerSettings
from settingswindow import SettingsWindow
from filerenamer import FileRenamer
import os
import utils
class AutoTagStartWindow(QtGui.QDialog):
def __init__( self, parent, settings, msg ):
super(AutoTagStartWindow, self).__init__(parent)
uic.loadUi(os.path.join(ComicTaggerSettings.baseDir(), 'autotagstartwindow.ui' ), self)
self.label.setText( msg )
self.settings = settings
self.cbxNoAutoSaveOnLow.setCheckState( QtCore.Qt.Unchecked )
self.noAutoSaveOnLow = False
def accept( self ):
QtGui.QDialog.accept(self)
self.noAutoSaveOnLow = self.cbxNoAutoSaveOnLow.isChecked()

104
autotagstartwindow.ui Normal file
View File

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>dialogExport</class>
<widget class="QDialog" name="dialogExport">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>513</width>
<height>189</height>
</rect>
</property>
<property name="windowTitle">
<string>Auto-Tag</string>
</property>
<property name="modal">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QCheckBox" name="cbxNoAutoSaveOnLow">
<property name="text">
<string>Don't save on low confidence match</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>dialogExport</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>346</x>
<y>187</y>
</hint>
<hint type="destinationlabel">
<x>277</x>
<y>104</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>dialogExport</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>346</x>
<y>187</y>
</hint>
<hint type="destinationlabel">
<x>277</x>
<y>104</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -263,6 +263,9 @@ class RarArchiver:
def readArchiveFile( self, archive_file ):
# Make sure to escape brackets, since some funky stuff is going on
# underneath with "fnmatch"
archive_file = archive_file.replace("[", '[[]')
entries = UnRAR2.RarFile( self.path ).read_files( archive_file )
#entries is a list of of tuples: ( rarinfo, filedata)

62
exportwindow.py Normal file
View File

@ -0,0 +1,62 @@
"""
A PyQT4 dialog to confirm and set options for export to zip
"""
"""
Copyright 2012 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from PyQt4 import QtCore, QtGui, uic
from settings import ComicTaggerSettings
from settingswindow import SettingsWindow
from filerenamer import FileRenamer
import os
import utils
class ExportConflictOpts:
dontCreate = 1
overwrite = 2
createUnique = 3
class ExportWindow(QtGui.QDialog):
def __init__( self, parent, settings, msg ):
super(ExportWindow, self).__init__(parent)
uic.loadUi(os.path.join(ComicTaggerSettings.baseDir(), 'exportwindow.ui' ), self)
self.label.setText( msg )
self.settings = settings
self.cbxDeleteOriginal.setCheckState( QtCore.Qt.Unchecked )
self.cbxAddToList.setCheckState( QtCore.Qt.Checked )
self.radioDontCreate.setChecked( True )
self.deleteOriginal = False
self.addToList = True
self.fileConflictBehavior = ExportConflictOpts.dontCreate
def accept( self ):
QtGui.QDialog.accept(self)
self.deleteOriginal = self.cbxDeleteOriginal.isChecked()
self.addToList = self.cbxAddToList.isChecked()
if self.radioDontCreate.isChecked():
self.fileConflictBehavior = ExportConflictOpts.dontCreate
elif self.radioCreateNew.isChecked():
self.fileConflictBehavior = ExportConflictOpts.createUnique
#else:
# self.fileConflictBehavior = ExportConflictOpts.overwrite

138
exportwindow.ui Normal file
View File

@ -0,0 +1,138 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>dialogExport</class>
<widget class="QDialog" name="dialogExport">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>515</width>
<height>275</height>
</rect>
</property>
<property name="windowTitle">
<string>Export to Zip Archive</string>
</property>
<property name="modal">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QCheckBox" name="cbxDeleteOriginal">
<property name="text">
<string>Delete Original RAR (Not recommended)</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="cbxAddToList">
<property name="text">
<string>Add New Archive to ComicTagger list</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>When Filename already exists:</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QRadioButton" name="radioDontCreate">
<property name="text">
<string>Don't Export</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QRadioButton" name="radioCreateNew">
<property name="text">
<string>Create New Archive With Unique Name (Number appended)</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>dialogExport</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>346</x>
<y>187</y>
</hint>
<hint type="destinationlabel">
<x>277</x>
<y>104</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>dialogExport</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>346</x>
<y>187</y>
</hint>
<hint type="destinationlabel">
<x>277</x>
<y>104</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -106,6 +106,7 @@ class FileRenamer:
new_name += ext
new_name = new_name.replace("/", "-")
return new_name

View File

@ -114,12 +114,18 @@ class FileSelectionList(QWidget):
self.twList.setSortingEnabled(False)
for ca in ca_list:
for row in range(self.twList.rowCount()):
fi = self.twList.item(row, FileSelectionList.dataColNum).data( Qt.UserRole ).toPyObject()
if fi.ca == ca:
row_ca = self.getArchiveByRow( row )
if row_ca == ca:
self.twList.removeRow(row)
break
self.twList.setSortingEnabled(True)
def getArchiveByRow( self, row):
fi = self.twList.item(row, FileSelectionList.dataColNum).data( Qt.UserRole ).toPyObject()
return fi.ca
def getCurrentArchive( self ):
return self.getArchiveByRow( self.twList.currentRow() )
def removeSelection( self ):
row_list = []
@ -203,8 +209,8 @@ class FileSelectionList(QWidget):
def isListDupe( self, path ):
r = 0
while r < self.twList.rowCount():
fi = self.twList.item(r, FileSelectionList.dataColNum).data( Qt.UserRole ).toPyObject()
if fi.ca.path == path:
ca = self.getArchiveByRow( r )
if ca.path == path:
return True
r = r + 1

View File

@ -128,8 +128,8 @@ class RenameWindow(QtGui.QDialog):
QtCore.QCoreApplication.processEvents()
if progdialog.wasCanceled():
break
idx += 1
progdialog.setValue(idx)
idx += 1
progdialog.setLabelText( item['new_name'] )
if item['new_name'] == os.path.basename( item['archive'].path ):
@ -142,7 +142,7 @@ class RenameWindow(QtGui.QDialog):
folder = os.path.dirname( os.path.abspath( item['archive'].path ) )
new_abs_path = utils.unique_file( os.path.join( folder, item['new_name'] ) )
os.rename( item['archive'].path, new_abs_path )
os.rename( item['archive'].path, new_abs_path)
item['archive'].rename( new_abs_path )

View File

@ -49,10 +49,24 @@ from cbltransformer import CBLTransformer
from renamewindow import RenameWindow
from exportwindow import ExportWindow, ExportConflictOpts
from pageloader import PageLoader
from issueidentifier import IssueIdentifier
from autotagstartwindow import AutoTagStartWindow
import utils
import ctversion
class OnlineMatchResults():
def __init__(self):
self.goodMatches = []
self.noMatches = []
self.multipleMatches = []
self.writeFailures = []
self.fetchDataFailures = []
class MultipleMatch():
def __init__( self, filename, match_list):
self.filename = filename
self.matches = match_list
# this reads the environment and inits the right locale
locale.setlocale(locale.LC_ALL, "")
@ -345,7 +359,7 @@ class TaggerWindow( QtGui.QMainWindow):
return
if not self.dirtyFlagVerification( "Export as Zip Archive",
"If export archives as Zip now, unsaved data in the form may be lost. Are you sure?"):
"If you export archives as Zip now, unsaved data in the form may be lost. Are you sure?"):
return
if rar_count != 0:
@ -369,8 +383,8 @@ class TaggerWindow( QtGui.QMainWindow):
QtCore.QCoreApplication.processEvents()
if progdialog.wasCanceled():
break
prog_idx += 1
progdialog.setValue(prog_idx)
prog_idx += 1
progdialog.setLabelText( ca.path )
QtCore.QCoreApplication.processEvents()
@ -967,7 +981,7 @@ class TaggerWindow( QtGui.QMainWindow):
def setLoadDataStyle(self, s):
if self.dirtyFlagVerification( "Change Tag Read Style",
"If you change tag style now, data in the form will be lost. Are you sure?"):
"If you change read tag style now, data in the form will be lost. Are you sure?"):
self.load_data_style, b = self.cbLoadDataStyle.itemData(s).toInt()
self.settings.last_selected_load_data_style = self.load_data_style
self.updateMenus()
@ -1322,7 +1336,6 @@ class TaggerWindow( QtGui.QMainWindow):
def removeTags( self, style):
# remove the indicated tags from the archive
#ATB
ca_list = self.fileSelectionList.getSelectedArchiveList()
has_md_count = 0
for ca in ca_list:
@ -1335,7 +1348,7 @@ class TaggerWindow( QtGui.QMainWindow):
return
if has_md_count != 0 and not self.dirtyFlagVerification( "Remove Tags",
"If remove tags now, unsaved data in the form will be lost. Are you sure?"):
"If you remove tags now, unsaved data in the form will be lost. Are you sure?"):
return
if has_md_count != 0:
@ -1356,8 +1369,8 @@ class TaggerWindow( QtGui.QMainWindow):
QtCore.QCoreApplication.processEvents()
if progdialog.wasCanceled():
break
prog_idx += 1
progdialog.setValue(prog_idx)
prog_idx += 1
progdialog.setLabelText( ca.path )
QtCore.QCoreApplication.processEvents()
@ -1398,16 +1411,16 @@ class TaggerWindow( QtGui.QMainWindow):
return
if has_src_count != 0 and not self.dirtyFlagVerification( "Copy Tags",
"If copy tags now, unsaved data in the form may be lost. Are you sure?"):
"If you copy tags now, unsaved data in the form may be lost. Are you sure?"):
return
if has_src_count != 0:
reply = QtGui.QMessageBox.question(self,
self.tr("Copy Tags"),
self.tr("Are you sure you wish to copy the {0} tags to {1} tags in {2} archive(s)?".format(
self.tr("Copy Tags"),
self.tr("Are you sure you wish to copy the {0} tags to {1} tags in {2} archive(s)?".format(
MetaDataStyle.name[src_style], MetaDataStyle.name[dest_style], has_src_count)),
QtGui.QMessageBox.Yes, QtGui.QMessageBox.No )
QtGui.QMessageBox.Yes, QtGui.QMessageBox.No )
if reply == QtGui.QMessageBox.Yes:
progdialog = QtGui.QProgressDialog("", "Cancel", 0, has_src_count, self)
progdialog.setWindowTitle( "Copying Tags" )
@ -1420,8 +1433,8 @@ class TaggerWindow( QtGui.QMainWindow):
QtCore.QCoreApplication.processEvents()
if progdialog.wasCanceled():
break
prog_idx += 1
progdialog.setValue(prog_idx)
prog_idx += 1
progdialog.setLabelText( ca.path )
QtCore.QCoreApplication.processEvents()
@ -1442,9 +1455,157 @@ class TaggerWindow( QtGui.QMainWindow):
self.fileSelectionList.updateSelectedRows()
self.updateInfoBox()
self.updateMenus()
def actualIssueDataFetch( self, match ):
# now get the particular issue data
cv_md = None
QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
try:
cv_md = ComicVineTalker().fetchIssueData( match['volume_id'], match['issue_number'], self.settings )
except ComicVineTalkerException:
print "Network error while getting issue details. Save aborted"
if cv_md is not None:
if self.settings.apply_cbl_transform_on_bulk_operation:
cv_md = CBLTransformer( cv_md, self.settings ).apply()
QtGui.QApplication.restoreOverrideCursor()
return cv_md
def identifyAndTagSingleArchive( self, ca, match_results, abortOnLowConfidence ):
success = False
ii = IssueIdentifier( ca, self.settings )
# read in metadata, and parse file name if not there
md = ca.readMetadata( self.save_data_style )
if md.isEmpty:
md = ca.metadataFromFilename()
if md is None or md.isEmpty:
print "!!!!No metadata given to search online with!"
return False, match_results
def myoutput( text ):
IssueIdentifier.defaultWriteOutput( text )
QtCore.QCoreApplication.processEvents()
QtCore.QCoreApplication.processEvents()
QtCore.QCoreApplication.processEvents()
ii.setAdditionalMetadata( md )
ii.onlyUseAdditionalMetaData = True
ii.setOutputFunction( myoutput )
ii.cover_page_index = md.getCoverPageIndexList()[0]
matches = ii.search()
result = ii.search_result
found_match = False
choices = False
low_confidence = False
if result == ii.ResultNoMatches:
pass
elif result == ii.ResultFoundMatchButBadCoverScore:
low_confidence = True
found_match = True
elif result == ii.ResultFoundMatchButNotFirstPage :
found_match = True
elif result == ii.ResultMultipleMatchesWithBadImageScores:
low_confidence = True
choices = True
elif result == ii.ResultOneGoodMatch:
found_match = True
elif result == ii.ResultMultipleGoodMatches:
choices = True
if choices:
print "Online search: Multiple matches. Save aborted"
match_results.multipleMatches.append(MultipleMatch(ca.path,matches))
elif low_confidence and abortOnLowConfidence:
print "Online search: Low confidence match. Save aborted"
match_results.noMatches.append(ca.path)
elif not found_match:
print "Online search: No match found. Save aborted"
match_results.noMatches.append(ca.path)
else:
# a single match!
# now get the particular issue data
cv_md = self.actualIssueDataFetch( matches[0] )
if cv_md is None:
match_results.fetchDataFailures.append(ca.path)
if cv_md is not None:
md.overlay( cv_md )
if not ca.writeMetadata( md, self.save_data_style ):
match_results.writeFailures.append(ca.path)
else:
match_results.goodMatches.append(ca.path)
success = True
return success, match_results
def autoTag( self ):
print "TBD"
ca_list = self.fileSelectionList.getSelectedArchiveList()
style = self.save_data_style
if len(ca_list) == 0:
QtGui.QMessageBox.information(self, self.tr("Auto-Tag"), self.tr("No archives selected!"))
return
if not self.dirtyFlagVerification( "Auto-Tag",
"If you auto-tag now, unsaved data in the form will be lost. Are you sure?"):
return
dlg = AutoTagStartWindow( self, self.settings,
self.tr("You have selected {0} archive(s) to automatically identify and write {1} tags to.\n\n".format(len(ca_list), MetaDataStyle.name[style]) +
"Please choose options below, and select OK.\n" ))
dlg.setModal( True )
if not dlg.exec_():
return
progdialog = QtGui.QProgressDialog("", "Cancel", 0, len(ca_list), self)
progdialog.setWindowTitle( "Auto-Tagging" )
progdialog.setWindowModality(QtCore.Qt.WindowModal)
progdialog.show()
prog_idx = 0
match_results = OnlineMatchResults()
for ca in ca_list:
QtCore.QCoreApplication.processEvents()
if progdialog.wasCanceled():
break
progdialog.setValue(prog_idx)
prog_idx += 1
progdialog.setLabelText( ca.path )
progdialog.setAutoClose( False )
QtCore.QCoreApplication.processEvents()
if ca.isWritable():
success, match_results = self.identifyAndTagSingleArchive( ca, match_results, dlg.noAutoSaveOnLow )
#if not success:
# QtGui.QMessageBox.warning(self, self.tr("Auto-Tag failed"),
# self.tr("The tagging operation seemed to fail for {0} Operation aborted!".format(ca.path)))
# break
print "Good", match_results.goodMatches
print "multipleMatches", match_results.multipleMatches
print "noMatches", match_results.noMatches
print "fetchDataFailures", match_results.fetchDataFailures
print "writeFailures", match_results.writeFailures
progdialog.close()
self.fileSelectionList.updateSelectedRows()
self.loadArchive( self.fileSelectionList.getCurrentArchive() )
def dirtyFlagVerification( self, title, desc):
if self.dirtyFlag:
@ -1520,7 +1681,7 @@ class TaggerWindow( QtGui.QMainWindow):
def renameArchive(self):
if self.comic_archive is not None:
if self.dirtyFlagVerification( "File Rename",
"If rename files now, unsaved data in the form will be lost. Are you sure?"):
"If you rename files now, unsaved data in the form will be lost. Are you sure?"):
#get list of archives from filelist
ca_list = self.fileSelectionList.getSelectedArchiveList()
dlg = RenameWindow( self, ca_list, self.load_data_style, self.settings )