2012-11-14 20:34:33 -08:00
# coding=utf-8
2012-11-06 12:56:30 -08:00
"""
The main window of the comictagger app
"""
"""
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 .
"""
2012-11-02 13:54:17 -07:00
from PyQt4 import QtCore , QtGui , uic
2012-11-14 17:25:01 -08:00
from PyQt4 . QtCore import QUrl , pyqtSignal
2012-11-20 13:20:26 -08:00
import signal
2012-11-02 20:56:01 -07:00
import locale
2012-11-06 12:29:18 -08:00
import platform
2012-11-14 17:25:01 -08:00
import os
2012-11-19 20:04:58 -08:00
import pprint
import json
2012-12-04 23:34:53 -08:00
import webbrowser
2012-11-02 13:54:17 -07:00
from volumeselectionwindow import VolumeSelectionWindow
2012-11-18 17:01:17 -08:00
from options import MetaDataStyle
2012-11-16 11:32:46 -08:00
from comicinfoxml import ComicInfoXml
2012-11-02 13:54:17 -07:00
from genericmetadata import GenericMetadata
2012-11-28 12:15:20 -08:00
from comicvinetalker import ComicVineTalker , ComicVineTalkerException
2012-11-02 13:54:17 -07:00
from comicarchive import ComicArchive
2012-11-02 20:56:01 -07:00
from crediteditorwindow import CreditEditorWindow
2012-11-06 12:29:18 -08:00
from settingswindow import SettingsWindow
from settings import ComicTaggerSettings
2012-11-14 17:25:01 -08:00
from pagebrowser import PageBrowserWindow
2012-11-14 20:34:33 -08:00
from filenameparser import FileNameParser
2012-11-19 20:04:58 -08:00
from logwindow import LogWindow
2012-11-20 13:20:26 -08:00
from optionalmsgdialog import OptionalMessageDialog
2012-12-08 11:57:51 -08:00
from pagelisteditor import PageListEditor
2013-01-16 14:46:22 -08:00
from fileselectionlist import FileSelectionList
2012-12-17 13:19:21 -08:00
from cbltransformer import CBLTransformer
2012-12-18 17:37:55 -08:00
from renamewindow import RenameWindow
2013-01-20 22:39:44 -08:00
from exportwindow import ExportWindow , ExportConflictOpts
2013-01-16 14:46:22 -08:00
from pageloader import PageLoader
2013-01-21 16:19:59 -08:00
from issueidentifier import IssueIdentifier
from autotagstartwindow import AutoTagStartWindow
2013-01-21 20:09:08 -08:00
from autotagprogresswindow import AutoTagProgressWindow
2013-01-22 21:25:50 -08:00
from autotagmatchwindow import AutoTagMatchWindow
2012-11-02 13:54:17 -07:00
import utils
2012-11-14 17:25:01 -08:00
import ctversion
2012-11-02 20:56:01 -07:00
2013-01-21 16:19:59 -08:00
class OnlineMatchResults ( ) :
def __init__ ( self ) :
self . goodMatches = [ ]
self . noMatches = [ ]
self . multipleMatches = [ ]
self . writeFailures = [ ]
self . fetchDataFailures = [ ]
class MultipleMatch ( ) :
2013-01-22 21:25:50 -08:00
def __init__ ( self , ca , match_list ) :
self . ca = ca
2013-01-21 16:19:59 -08:00
self . matches = match_list
2012-11-02 13:54:17 -07:00
# this reads the environment and inits the right locale
locale . setlocale ( locale . LC_ALL , " " )
2012-11-14 17:25:01 -08:00
# helper func to allow a label to be clickable
def clickable ( widget ) :
class Filter ( QtCore . QObject ) :
dblclicked = pyqtSignal ( )
def eventFilter ( self , obj , event ) :
if obj == widget :
if event . type ( ) == QtCore . QEvent . MouseButtonDblClick :
self . dblclicked . emit ( )
return True
return False
filter = Filter ( widget )
widget . installEventFilter ( filter )
return filter . dblclicked
2012-11-16 11:32:46 -08:00
2012-11-14 17:25:01 -08:00
2012-11-02 13:54:17 -07:00
class TaggerWindow ( QtGui . QMainWindow ) :
appName = " ComicTagger "
2012-11-14 17:25:01 -08:00
version = ctversion . version
2012-11-02 13:54:17 -07:00
2013-01-17 16:52:42 -08:00
def __init__ ( self , file_list , settings , parent = None ) :
2012-11-02 13:54:17 -07:00
super ( TaggerWindow , self ) . __init__ ( parent )
2012-11-13 13:25:15 -08:00
uic . loadUi ( os . path . join ( ComicTaggerSettings . baseDir ( ) , ' taggerwindow.ui ' ) , self )
2013-01-16 14:46:22 -08:00
self . settings = settings
2012-12-08 11:57:51 -08:00
self . pageListEditor = PageListEditor ( self . tabPages )
gridlayout = QtGui . QGridLayout ( self . tabPages )
gridlayout . addWidget ( self . pageListEditor )
2013-01-16 14:46:22 -08:00
#---------------------------
self . fileSelectionList = FileSelectionList ( self . widgetListHolder , self . settings )
gridlayout = QtGui . QGridLayout ( self . widgetListHolder )
gridlayout . addWidget ( self . fileSelectionList )
self . fileSelectionList . selectionChanged . connect ( self . fileListSelectionChanged )
2013-01-16 22:19:06 -08:00
self . fileSelectionList . listCleared . connect ( self . fileListCleared )
2013-01-23 10:55:10 -08:00
# ATB: Disable the list...
2013-01-16 22:19:06 -08:00
#self.splitter.setSizes([100,0])
#self.splitter.setHandleWidth(0)
#self.splitter.handle(1).setDisabled(True)
2013-01-23 10:55:10 -08:00
# This is ugly, and should probably be done in a resource file
if platform . system ( ) == " Linux " :
self . scrollAreaWidgetContents . resize ( self . scrollAreaWidgetContents . width ( ) , 630 )
#if platform.system() == "Darwin":
# self.scrollAreaWidgetContents.resize( 550, self.scrollAreaWidgetContents.height())
# we can't specify relative font sizes in the UI designer, so
# walk through all the lablels in the main form, and make them
# a smidge smaller
for child in self . scrollAreaWidgetContents . children ( ) :
if ( isinstance ( child , QtGui . QLabel ) ) :
f = child . font ( )
if f . pointSize ( ) > 10 :
f . setPointSize ( f . pointSize ( ) - 2 )
f . setItalic ( True )
child . setFont ( f )
2013-01-16 14:46:22 -08:00
#---------------------------
2012-11-16 14:09:40 -08:00
self . setWindowIcon ( QtGui . QIcon ( os . path . join ( ComicTaggerSettings . baseDir ( ) , ' graphics/app.png ' ) ) )
2012-11-12 22:06:09 -08:00
2012-11-16 14:09:40 -08:00
self . lblCover . setPixmap ( QtGui . QPixmap ( os . path . join ( ComicTaggerSettings . baseDir ( ) , ' graphics/nocover.png ' ) ) )
2012-11-02 13:54:17 -07:00
2013-01-18 13:12:23 -08:00
self . save_data_style = settings . last_selected_save_data_style
self . load_data_style = settings . last_selected_load_data_style
2012-11-19 22:34:09 -08:00
2012-11-02 13:54:17 -07:00
self . setAcceptDrops ( True )
2013-01-16 22:19:06 -08:00
self . configMenus ( )
self . statusBar ( )
2012-11-02 13:54:17 -07:00
self . populateComboBoxes ( )
2013-01-16 22:19:06 -08:00
self . resetApp ( )
2012-11-28 10:49:31 -08:00
# set up some basic field validators
validator = QtGui . QIntValidator ( 1900 , 2099 , self )
self . lePubYear . setValidator ( validator )
validator = QtGui . QIntValidator ( 1 , 12 , self )
self . lePubMonth . setValidator ( validator )
validator = QtGui . QIntValidator ( 1 , 99999 , self )
self . leIssueCount . setValidator ( validator )
self . leVolumeNum . setValidator ( validator )
self . leVolumeCount . setValidator ( validator )
self . leAltIssueNum . setValidator ( validator )
self . leAltIssueCount . setValidator ( validator )
#TODO set up an RE validator for issueNum that allows
# for all sorts of wacky things
2013-01-17 16:52:42 -08:00
#make sure some editable comboboxes don't take drop actions
self . cbFormat . lineEdit ( ) . setAcceptDrops ( False )
self . cbMaturityRating . lineEdit ( ) . setAcceptDrops ( False )
2012-11-28 10:49:31 -08:00
2012-11-02 13:54:17 -07:00
# hook up the callbacks
2013-01-18 13:12:23 -08:00
self . cbLoadDataStyle . currentIndexChanged . connect ( self . setLoadDataStyle )
self . cbSaveDataStyle . currentIndexChanged . connect ( self . setSaveDataStyle )
2012-11-02 20:56:01 -07:00
self . btnEditCredit . clicked . connect ( self . editCredit )
self . btnAddCredit . clicked . connect ( self . addCredit )
self . btnRemoveCredit . clicked . connect ( self . removeCredit )
self . twCredits . cellDoubleClicked . connect ( self . editCredit )
2012-11-14 17:25:01 -08:00
clickable ( self . lblCover ) . connect ( self . showPageBrowser )
2012-12-09 22:39:54 -08:00
2012-11-04 21:04:30 -08:00
self . connectDirtyFlagSignals ( )
2012-12-09 22:39:54 -08:00
self . pageListEditor . modified . connect ( self . setDirtyFlag )
self . pageListEditor . firstFrontCoverChanged . connect ( self . frontCoverChanged )
self . pageListEditor . listOrderChanged . connect ( self . pageListOrderChanged )
2012-11-04 21:04:30 -08:00
2012-11-02 13:54:17 -07:00
self . updateStyleTweaks ( )
2012-11-20 09:26:15 -08:00
self . show ( )
self . setAppPosition ( )
2013-01-23 10:55:10 -08:00
if self . settings . last_form_side_width != - 1 :
self . splitter . setSizes ( [ self . settings . last_form_side_width , self . settings . last_list_side_width ] )
2012-11-19 22:34:09 -08:00
self . raise_ ( )
2012-11-20 13:20:26 -08:00
QtCore . QCoreApplication . processEvents ( )
2012-11-19 22:34:09 -08:00
2013-01-20 12:43:48 -08:00
self . fileSelectionList . addAppAction ( self . actionAutoIdentify )
self . fileSelectionList . addAppAction ( self . actionAutoTag )
self . fileSelectionList . addAppAction ( self . actionCopyTags )
2013-01-18 22:15:33 -08:00
self . fileSelectionList . addAppAction ( self . actionRename )
self . fileSelectionList . addAppAction ( self . actionRemoveAuto )
2013-01-20 00:36:21 -08:00
self . fileSelectionList . addAppAction ( self . actionRepackage )
2013-01-18 22:15:33 -08:00
2013-01-17 16:52:42 -08:00
if len ( file_list ) != 0 :
self . fileSelectionList . addPathList ( file_list )
2012-11-20 14:38:10 -08:00
if self . settings . show_disclaimer :
2012-11-28 09:59:52 -08:00
checked = OptionalMessageDialog . msg ( self , " Welcome! " ,
"""
Thanks for trying ComicTagger ! < br > < br >
Be aware that this is beta - level software , and consider it experimental .
You should use it very carefully when modifying your data files . As the
license says , it ' s " AS IS! " <br><br>
Also , be aware that writing tags to comic archives will change their file hashes ,
which has implications with respect to other software packages . It ' s best to
use ComicTagger on local copies of your comics . < br > < br >
Have fun !
"""
2012-11-20 14:38:10 -08:00
)
self . settings . show_disclaimer = not checked
2012-11-20 13:20:26 -08:00
def sigint_handler ( self , * args ) :
# defer the actual close in the app loop thread
QtCore . QTimer . singleShot ( 200 , self . close )
2012-11-19 22:34:09 -08:00
2013-01-16 22:19:06 -08:00
def resetApp ( self ) :
self . lblCover . setPixmap ( QtGui . QPixmap ( os . path . join ( ComicTaggerSettings . baseDir ( ) , ' graphics/nocover.png ' ) ) )
self . comic_archive = None
self . dirtyFlag = False
self . clearForm ( )
self . pageListEditor . resetPage ( )
self . updateAppTitle ( )
self . updateMenus ( )
self . updateInfoBox ( )
self . droppedFile = None
self . page_browser = None
self . page_loader = None
2012-11-02 13:54:17 -07:00
def updateAppTitle ( self ) :
2012-11-04 21:04:30 -08:00
2012-11-02 13:54:17 -07:00
if self . comic_archive is None :
self . setWindowTitle ( self . appName )
else :
2012-11-05 11:20:13 -08:00
mod_str = " "
ro_str = " "
if self . dirtyFlag :
mod_str = " [modified] "
if not self . comic_archive . isWritable ( ) :
2012-11-17 16:32:01 -08:00
ro_str = " [read only] "
2012-11-05 11:20:13 -08:00
self . setWindowTitle ( self . appName + " - " + self . comic_archive . path + mod_str + ro_str )
2012-11-02 13:54:17 -07:00
def configMenus ( self ) :
# File Menu
self . actionExit . setShortcut ( ' Ctrl+Q ' )
self . actionExit . setStatusTip ( ' Exit application ' )
2012-11-04 21:04:30 -08:00
self . actionExit . triggered . connect ( self . close )
2012-11-02 13:54:17 -07:00
self . actionLoad . setShortcut ( ' Ctrl+O ' )
self . actionLoad . setStatusTip ( ' Load comic archive ' )
self . actionLoad . triggered . connect ( self . selectFile )
2013-01-18 22:15:33 -08:00
self . actionLoadFolder . setShortcut ( ' Ctrl+Shift+O ' )
2013-01-17 16:52:42 -08:00
self . actionLoadFolder . setStatusTip ( ' Load folder with comic archives ' )
self . actionLoadFolder . triggered . connect ( self . selectFolder )
2012-11-02 13:54:17 -07:00
self . actionWrite_Tags . setShortcut ( ' Ctrl+S ' )
self . actionWrite_Tags . setStatusTip ( ' Save tags to comic archive ' )
self . actionWrite_Tags . triggered . connect ( self . commitMetadata )
2013-01-20 12:43:48 -08:00
self . actionAutoTag . setShortcut ( ' Ctrl+T ' )
self . actionAutoTag . setStatusTip ( ' Auto-tag multiple archives ' )
self . actionAutoTag . triggered . connect ( self . autoTag )
self . actionCopyTags . setShortcut ( ' Ctrl+C ' )
self . actionCopyTags . setStatusTip ( ' Copy one tag style to another ' )
self . actionCopyTags . triggered . connect ( self . copyTags )
2012-12-06 12:32:24 -08:00
self . actionRemoveAuto . setShortcut ( ' Ctrl+D ' )
2013-01-20 12:43:48 -08:00
self . actionRemoveAuto . setStatusTip ( ' Remove currently selected modify tag style from the archive ' )
2012-12-06 12:32:24 -08:00
self . actionRemoveAuto . triggered . connect ( self . removeAuto )
2013-01-20 12:43:48 -08:00
2012-11-04 11:24:48 -08:00
self . actionRemoveCBLTags . setStatusTip ( ' Remove ComicBookLover tags from comic archive ' )
self . actionRemoveCBLTags . triggered . connect ( self . removeCBLTags )
self . actionRemoveCRTags . setStatusTip ( ' Remove ComicRack tags from comic archive ' )
self . actionRemoveCRTags . triggered . connect ( self . removeCRTags )
2012-11-19 20:04:58 -08:00
self . actionViewRawCRTags . setStatusTip ( ' View raw ComicRack tag block from file ' )
self . actionViewRawCRTags . triggered . connect ( self . viewRawCRTags )
self . actionViewRawCBLTags . setStatusTip ( ' View raw ComicBookLover tag block from file ' )
self . actionViewRawCBLTags . triggered . connect ( self . viewRawCBLTags )
2012-12-06 12:32:24 -08:00
self . actionRepackage . setShortcut ( ' Ctrl+E ' )
2012-11-02 13:54:17 -07:00
self . actionRepackage . setStatusTip ( ' Re-create archive as CBZ ' )
self . actionRepackage . triggered . connect ( self . repackageArchive )
2012-12-18 15:14:00 -08:00
self . actionRename . setShortcut ( ' Ctrl+N ' )
self . actionRename . setStatusTip ( ' Rename archive based on tags ' )
self . actionRename . triggered . connect ( self . renameArchive )
2012-11-02 13:54:17 -07:00
2012-11-06 12:29:18 -08:00
self . actionSettings . setStatusTip ( ' Configure ComicTagger ' )
self . actionSettings . triggered . connect ( self . showSettings )
2012-11-02 13:54:17 -07:00
# Tag Menu
self . actionParse_Filename . setShortcut ( ' Ctrl+F ' )
self . actionParse_Filename . setStatusTip ( ' Try to extract tags from filename ' )
self . actionParse_Filename . triggered . connect ( self . useFilename )
2012-11-14 22:20:46 -08:00
self . actionSearchOnline . setShortcut ( ' Ctrl+W ' )
self . actionSearchOnline . setStatusTip ( ' Search online for tags ' )
self . actionSearchOnline . triggered . connect ( self . queryOnline )
2013-01-20 12:43:48 -08:00
self . actionAutoIdentify . setShortcut ( ' Ctrl+I ' )
self . actionAutoIdentify . triggered . connect ( self . autoIdentifySearch )
2012-11-02 13:54:17 -07:00
2012-12-18 15:14:00 -08:00
self . actionApplyCBLTransform . setShortcut ( ' Ctrl+L ' )
self . actionApplyCBLTransform . setStatusTip ( ' Modify tags specifically for CBL format ' )
self . actionApplyCBLTransform . triggered . connect ( self . applyCBLTransform )
2012-11-04 11:24:48 -08:00
#self.actionClearEntryForm.setShortcut( 'Ctrl+C' )
2012-11-03 11:46:04 -07:00
self . actionClearEntryForm . setStatusTip ( ' Clear all the data on the screen ' )
self . actionClearEntryForm . triggered . connect ( self . clearForm )
2012-11-14 22:20:46 -08:00
# Window Menu
self . actionPageBrowser . setShortcut ( ' Ctrl+P ' )
self . actionPageBrowser . setStatusTip ( ' Show the page browser ' )
self . actionPageBrowser . triggered . connect ( self . showPageBrowser )
2012-11-02 13:54:17 -07:00
# Help Menu
self . actionAbout . setStatusTip ( ' Show the ' + self . appName + ' info ' )
self . actionAbout . triggered . connect ( self . aboutApp )
2012-12-04 23:34:53 -08:00
self . actionWiki . triggered . connect ( self . showWiki )
self . actionReportBug . triggered . connect ( self . reportBug )
self . actionComicTaggerForum . triggered . connect ( self . showForum )
2012-11-02 13:54:17 -07:00
# ToolBar
2012-11-15 09:22:27 -08:00
self . actionLoad . setIcon ( QtGui . QIcon ( os . path . join ( ComicTaggerSettings . baseDir ( ) , ' graphics/open.png ' ) ) )
2013-01-23 14:05:38 -08:00
self . actionLoadFolder . setIcon ( QtGui . QIcon ( os . path . join ( ComicTaggerSettings . baseDir ( ) , ' graphics/longbox.png ' ) ) )
2012-11-15 09:22:27 -08:00
self . actionWrite_Tags . setIcon ( QtGui . QIcon ( os . path . join ( ComicTaggerSettings . baseDir ( ) , ' graphics/save.png ' ) ) )
self . actionParse_Filename . setIcon ( QtGui . QIcon ( os . path . join ( ComicTaggerSettings . baseDir ( ) , ' graphics/parse.png ' ) ) )
self . actionSearchOnline . setIcon ( QtGui . QIcon ( os . path . join ( ComicTaggerSettings . baseDir ( ) , ' graphics/search.png ' ) ) )
2013-01-20 12:43:48 -08:00
self . actionAutoIdentify . setIcon ( QtGui . QIcon ( os . path . join ( ComicTaggerSettings . baseDir ( ) , ' graphics/auto.png ' ) ) )
2013-01-23 14:05:38 -08:00
self . actionAutoTag . setIcon ( QtGui . QIcon ( os . path . join ( ComicTaggerSettings . baseDir ( ) , ' graphics/autotag.png ' ) ) )
2012-11-15 09:22:27 -08:00
self . actionClearEntryForm . setIcon ( QtGui . QIcon ( os . path . join ( ComicTaggerSettings . baseDir ( ) , ' graphics/clear.png ' ) ) )
self . actionPageBrowser . setIcon ( QtGui . QIcon ( os . path . join ( ComicTaggerSettings . baseDir ( ) , ' graphics/browse.png ' ) ) )
2012-11-14 22:20:46 -08:00
2012-11-02 13:54:17 -07:00
self . toolBar . addAction ( self . actionLoad )
2013-01-23 14:05:38 -08:00
self . toolBar . addAction ( self . actionLoadFolder )
2012-11-02 13:54:17 -07:00
self . toolBar . addAction ( self . actionWrite_Tags )
2012-11-14 22:20:46 -08:00
self . toolBar . addAction ( self . actionSearchOnline )
2013-01-20 12:43:48 -08:00
self . toolBar . addAction ( self . actionAutoIdentify )
2013-01-23 14:05:38 -08:00
self . toolBar . addAction ( self . actionAutoTag )
2012-11-03 11:46:04 -07:00
self . toolBar . addAction ( self . actionClearEntryForm )
2012-11-14 22:20:46 -08:00
self . toolBar . addAction ( self . actionPageBrowser )
2012-12-06 12:32:24 -08:00
2013-01-20 00:36:21 -08:00
def repackageArchive ( self ) :
ca_list = self . fileSelectionList . getSelectedArchiveList ( )
rar_count = 0
for ca in ca_list :
if ca . isRar ( ) :
rar_count + = 1
if rar_count == 0 :
QtGui . QMessageBox . information ( self , self . tr ( " Export as Zip Archive " ) , self . tr ( " No RAR archives selected! " ) )
return
if not self . dirtyFlagVerification ( " Export as Zip Archive " ,
2013-01-21 16:19:59 -08:00
" If you export archives as Zip now, unsaved data in the form may be lost. Are you sure? " ) :
2013-01-20 00:36:21 -08:00
return
if rar_count != 0 :
2013-01-20 22:39:44 -08:00
dlg = ExportWindow ( self , self . settings ,
self . tr ( " You have selected {0} archive(s) to export to Zip format. New archives will be created in the same folder as the original. \n \n Please choose options below, and select OK. \n " . format ( rar_count ) ) )
dlg . setModal ( True )
if not dlg . exec_ ( ) :
2013-01-20 00:36:21 -08:00
return
2013-01-20 22:39:44 -08:00
2013-01-20 00:36:21 -08:00
progdialog = QtGui . QProgressDialog ( " " , " Cancel " , 0 , rar_count , self )
progdialog . setWindowTitle ( " Exporting as ZIP " )
progdialog . setWindowModality ( QtCore . Qt . WindowModal )
2013-01-20 22:39:44 -08:00
progdialog . show ( )
2013-01-20 00:36:21 -08:00
prog_idx = 0
2013-01-20 22:39:44 -08:00
new_archives_to_add = [ ]
archives_to_remove = [ ]
2013-01-29 18:19:18 -08:00
skipped_list = [ ]
failed_list = [ ]
success_count = 0
2013-01-20 22:39:44 -08:00
2013-01-20 00:36:21 -08:00
for ca in ca_list :
if ca . isRar ( ) :
QtCore . QCoreApplication . processEvents ( )
if progdialog . wasCanceled ( ) :
break
progdialog . setValue ( prog_idx )
2013-01-21 16:19:59 -08:00
prog_idx + = 1
2013-01-20 22:39:44 -08:00
progdialog . setLabelText ( ca . path )
QtCore . QCoreApplication . processEvents ( )
2013-01-20 00:36:21 -08:00
2013-01-20 22:39:44 -08:00
original_path = os . path . abspath ( ca . path )
export_name = os . path . splitext ( original_path ) [ 0 ] + " .cbz "
if os . path . lexists ( export_name ) :
if dlg . fileConflictBehavior == ExportConflictOpts . dontCreate :
export_name = None
2013-01-29 18:19:18 -08:00
skipped_list . append ( ca . path )
2013-01-20 22:39:44 -08:00
elif dlg . fileConflictBehavior == ExportConflictOpts . createUnique :
export_name = utils . unique_file ( export_name )
if export_name is not None :
if ca . exportAsZip ( export_name ) :
2013-01-29 18:19:18 -08:00
success_count + = 1
2013-01-20 22:39:44 -08:00
if dlg . addToList :
new_archives_to_add . append ( export_name )
if dlg . deleteOriginal :
archives_to_remove . append ( ca )
os . unlink ( ca . path )
else :
2013-01-29 18:19:18 -08:00
# last export failed, so remove the zip, if it exists
failed_list . append ( ca . path )
if os . path . lexists ( export_name ) :
os . remove ( export_name )
2013-01-20 22:39:44 -08:00
progdialog . close ( )
2012-12-05 21:45:53 -08:00
2013-01-20 22:39:44 -08:00
self . fileSelectionList . addPathList ( new_archives_to_add )
self . fileSelectionList . removeArchiveList ( archives_to_remove )
2013-01-29 18:19:18 -08:00
summary = u " Successfully created {0} Zip archive(s). " . format ( success_count )
if len ( skipped_list ) > 0 :
summary + = u " \n \n The following {0} RAR archive(s) were skipped due to file name conflicts: \n " . format ( len ( skipped_list ) )
for f in skipped_list :
summary + = u " \t {0} \n " . format ( f )
if len ( failed_list ) > 0 :
summary + = u " \n \n The following {0} RAR archive(s) failed to export due to read/write errors: \n " . format ( len ( failed_list ) )
for f in failed_list :
summary + = u " \t {0} \n " . format ( f )
dlg = LogWindow ( self )
dlg . setText ( summary )
dlg . setWindowTitle ( " Archive Export to Zip Summary " )
dlg . exec_ ( )
2013-01-20 00:36:21 -08:00
2012-11-02 13:54:17 -07:00
def aboutApp ( self ) :
2012-11-16 14:09:40 -08:00
website = " http://code.google.com/p/comictagger "
email = " comictagger@gmail.com "
2012-12-11 09:56:52 -08:00
license_link = " http://www.apache.org/licenses/LICENSE-2.0 "
license_name = " Apache License 2.0 "
2012-11-16 14:09:40 -08:00
msgBox = QtGui . QMessageBox ( )
msgBox . setWindowTitle ( self . tr ( " About " + self . appName ) )
msgBox . setTextFormat ( QtCore . Qt . RichText )
msgBox . setIconPixmap ( QtGui . QPixmap ( os . path . join ( ComicTaggerSettings . baseDir ( ) , ' graphics/about.png ' ) ) )
msgBox . setText ( " <br><br><br> "
+ self . appName + " v " + self . version + " <br> "
+ " (c)2012 Anthony Beville<br><br> "
+ " <a href= ' {0} ' > {0} </a><br><br> " . format ( website )
2012-12-11 09:56:52 -08:00
+ " <a href= ' mailto: {0} ' > {0} </a><br><br> " . format ( email )
+ " License: <a href= ' {0} ' > {1} </a> " . format ( license_link , license_name ) )
2012-11-16 14:09:40 -08:00
msgBox . setStandardButtons ( QtGui . QMessageBox . Ok )
msgBox . exec_ ( )
2012-11-02 13:54:17 -07:00
def dragEnterEvent ( self , event ) :
2013-01-17 16:52:42 -08:00
self . droppedFiles = None
2012-11-02 13:54:17 -07:00
if event . mimeData ( ) . hasUrls ( ) :
2013-01-17 16:52:42 -08:00
# walk through the URL list and build a file list
for url in event . mimeData ( ) . urls ( ) :
if url . isValid ( ) and url . scheme ( ) == " file " :
if self . droppedFiles is None :
self . droppedFiles = [ ]
self . droppedFiles . append ( url . toLocalFile ( ) )
if self . droppedFiles is not None :
event . accept ( )
2012-11-02 13:54:17 -07:00
def dropEvent ( self , event ) :
2013-01-18 13:12:23 -08:00
#if self.dirtyFlagVerification( "Open Archive",
# "If you open a new archive now, data in the form will be lost. Are you sure?"):
self . fileSelectionList . addPathList ( self . droppedFiles )
event . accept ( )
2012-11-02 13:54:17 -07:00
2013-01-18 13:12:23 -08:00
def actualLoadCurrentArchive ( self ) :
2013-01-16 14:46:22 -08:00
if self . metadata . isEmpty :
self . metadata = self . comic_archive . metadataFromFilename ( )
2013-01-28 12:59:08 -08:00
if len ( self . metadata . pages ) == 0 :
2013-01-16 14:46:22 -08:00
self . metadata . setDefaultPageList ( self . comic_archive . getNumberOfPages ( ) )
self . updateCoverImage ( )
if self . page_browser is not None :
self . page_browser . setComicArchive ( self . comic_archive )
self . page_browser . metadata = self . metadata
self . metadataToForm ( )
self . pageListEditor . setData ( self . comic_archive , self . metadata . pages )
self . clearDirtyFlag ( ) # also updates the app title
self . updateInfoBox ( )
self . updateMenus ( )
2012-12-09 22:39:54 -08:00
def updateCoverImage ( self ) :
2013-01-16 14:46:22 -08:00
if self . page_loader is not None :
self . page_loader . abandoned = True
2012-12-09 22:39:54 -08:00
cover_idx = self . metadata . getCoverPageIndexList ( ) [ 0 ]
2013-01-16 14:46:22 -08:00
self . page_loader = PageLoader ( self . comic_archive , cover_idx )
self . page_loader . loadComplete . connect ( self . actualUpdateCoverImage )
self . page_loader . start ( )
2012-12-09 22:39:54 -08:00
2013-01-16 14:46:22 -08:00
def actualUpdateCoverImage ( self , img ) :
self . page_loader = None
self . lblCover . setPixmap ( QtGui . QPixmap ( img ) )
self . lblCover . setScaledContents ( True )
2012-12-06 12:32:24 -08:00
def updateMenus ( self ) :
# First just disable all the questionable items
2013-01-23 14:05:38 -08:00
self . actionAutoTag . setEnabled ( False )
self . actionCopyTags . setEnabled ( False )
self . actionRemoveAuto . setEnabled ( False )
self . actionRemoveCRTags . setEnabled ( False )
self . actionRemoveCBLTags . setEnabled ( False )
2012-12-06 12:32:24 -08:00
self . actionWrite_Tags . setEnabled ( False )
2013-01-23 14:05:38 -08:00
self . actionRepackage . setEnabled ( False )
2012-12-06 12:32:24 -08:00
self . actionViewRawCBLTags . setEnabled ( False )
self . actionViewRawCRTags . setEnabled ( False )
self . actionParse_Filename . setEnabled ( False )
2013-01-20 12:43:48 -08:00
self . actionAutoIdentify . setEnabled ( False )
2012-12-18 15:14:00 -08:00
self . actionRename . setEnabled ( False )
self . actionApplyCBLTransform . setEnabled ( False )
2012-12-06 12:32:24 -08:00
# now, selectively re-enable
if self . comic_archive is not None :
has_cix = self . comic_archive . hasCIX ( )
has_cbi = self . comic_archive . hasCBI ( )
self . actionParse_Filename . setEnabled ( True )
2013-01-20 12:43:48 -08:00
self . actionAutoIdentify . setEnabled ( True )
2013-01-23 14:05:38 -08:00
self . actionAutoTag . setEnabled ( True )
2012-12-18 15:14:00 -08:00
self . actionRename . setEnabled ( True )
self . actionApplyCBLTransform . setEnabled ( True )
2013-01-23 14:05:38 -08:00
self . actionRepackage . setEnabled ( True )
self . actionRemoveAuto . setEnabled ( True )
self . actionRemoveCRTags . setEnabled ( True )
self . actionRemoveCBLTags . setEnabled ( True )
self . actionCopyTags . setEnabled ( True )
2012-11-17 16:32:01 -08:00
2012-12-06 12:32:24 -08:00
if has_cix :
self . actionViewRawCRTags . setEnabled ( True )
if has_cbi :
self . actionViewRawCBLTags . setEnabled ( True )
if self . comic_archive . isWritable ( ) :
self . actionWrite_Tags . setEnabled ( True )
2013-01-23 14:05:38 -08:00
2012-12-06 12:32:24 -08:00
2012-11-02 13:54:17 -07:00
def updateInfoBox ( self ) :
ca = self . comic_archive
2013-01-16 22:19:06 -08:00
if ca is None :
self . lblFilename . setText ( " " )
self . lblArchiveType . setText ( " " )
self . lblTagList . setText ( " " )
self . lblPageCount . setText ( " " )
return
2012-11-14 20:34:33 -08:00
filename = os . path . basename ( ca . path )
filename = os . path . splitext ( filename ) [ 0 ]
filename = FileNameParser ( ) . fixSpaces ( filename )
self . lblFilename . setText ( filename )
if ca . isZip ( ) :
self . lblArchiveType . setText ( " ZIP archive " )
elif ca . isRar ( ) :
self . lblArchiveType . setText ( " RAR archive " )
elif ca . isFolder ( ) :
self . lblArchiveType . setText ( " Folder archive " )
else :
self . lblArchiveType . setText ( " " )
page_count = " ( {0} pages) " . format ( ca . getNumberOfPages ( ) )
self . lblPageCount . setText ( page_count )
tag_info = " "
2012-11-02 13:54:17 -07:00
if ca . hasCIX ( ) :
2012-11-14 20:34:33 -08:00
tag_info = u " • ComicRack tags "
2012-11-02 13:54:17 -07:00
if ca . hasCBI ( ) :
2012-11-14 20:34:33 -08:00
if tag_info != " " :
tag_info + = " \n "
tag_info + = u " • ComicBookLover tags "
self . lblTagList . setText ( tag_info )
2012-11-16 11:32:46 -08:00
2012-11-04 21:04:30 -08:00
def setDirtyFlag ( self , param1 = None , param2 = None , param3 = None ) :
if not self . dirtyFlag :
self . dirtyFlag = True
2013-01-18 13:12:23 -08:00
self . fileSelectionList . setModifiedFlag ( True )
2012-11-04 21:04:30 -08:00
self . updateAppTitle ( )
def clearDirtyFlag ( self ) :
if self . dirtyFlag :
self . dirtyFlag = False
2013-01-18 13:12:23 -08:00
self . fileSelectionList . setModifiedFlag ( False )
2012-11-04 21:04:30 -08:00
self . updateAppTitle ( )
def connectDirtyFlagSignals ( self ) :
# recursivly connect the tab form child slots
self . connectChildDirtyFlagSignals ( self . tabWidget )
def connectChildDirtyFlagSignals ( self , widget ) :
if ( isinstance ( widget , QtGui . QLineEdit ) ) :
widget . textEdited . connect ( self . setDirtyFlag )
if ( isinstance ( widget , QtGui . QTextEdit ) ) :
widget . textChanged . connect ( self . setDirtyFlag )
if ( isinstance ( widget , QtGui . QComboBox ) ) :
widget . currentIndexChanged . connect ( self . setDirtyFlag )
if ( isinstance ( widget , QtGui . QCheckBox ) ) :
widget . stateChanged . connect ( self . setDirtyFlag )
# recursive call on chillun
for child in widget . children ( ) :
2012-12-09 22:39:54 -08:00
if child != self . pageListEditor :
self . connectChildDirtyFlagSignals ( child )
2012-11-04 21:04:30 -08:00
2012-11-02 13:54:17 -07:00
2012-11-03 11:46:04 -07:00
def clearForm ( self ) :
2012-11-04 11:24:48 -08:00
# get a minty fresh metadata object
self . metadata = GenericMetadata ( )
2012-12-08 11:57:51 -08:00
if self . comic_archive is not None :
self . metadata . setDefaultPageList ( self . comic_archive . getNumberOfPages ( ) )
2012-11-04 11:24:48 -08:00
2012-11-03 11:46:04 -07:00
# recursivly clear the tab form
self . clearChildren ( self . tabWidget )
2012-11-04 21:04:30 -08:00
# clear the dirty flag, since there is nothing in there now to lose
self . clearDirtyFlag ( )
2012-12-09 22:39:54 -08:00
self . pageListEditor . setData ( self . comic_archive , self . metadata . pages )
2012-11-04 21:04:30 -08:00
2012-11-03 11:46:04 -07:00
def clearChildren ( self , widget ) :
if ( isinstance ( widget , QtGui . QLineEdit ) or
isinstance ( widget , QtGui . QTextEdit ) ) :
widget . setText ( " " )
if ( isinstance ( widget , QtGui . QComboBox ) ) :
widget . setCurrentIndex ( 0 )
if ( isinstance ( widget , QtGui . QCheckBox ) ) :
widget . setChecked ( False )
if ( isinstance ( widget , QtGui . QTableWidget ) ) :
while widget . rowCount ( ) > 0 :
widget . removeRow ( 0 )
# recursive call on chillun
for child in widget . children ( ) :
self . clearChildren ( child )
2012-11-02 13:54:17 -07:00
def metadataToForm ( self ) :
# copy the the metadata object into to the form
#helper func
def assignText ( field , value ) :
if value is not None :
2012-12-29 21:06:12 -08:00
field . setText ( unicode ( value ) )
2012-11-02 13:54:17 -07:00
md = self . metadata
assignText ( self . leSeries , md . series )
2012-11-19 11:57:16 -08:00
assignText ( self . leIssueNum , md . issue )
2012-11-02 13:54:17 -07:00
assignText ( self . leIssueCount , md . issueCount )
2012-11-19 11:57:16 -08:00
assignText ( self . leVolumeNum , md . volume )
2012-11-02 13:54:17 -07:00
assignText ( self . leVolumeCount , md . volumeCount )
assignText ( self . leTitle , md . title )
assignText ( self . lePublisher , md . publisher )
2012-11-19 11:57:16 -08:00
assignText ( self . lePubMonth , md . month )
assignText ( self . lePubYear , md . year )
2012-11-02 13:54:17 -07:00
assignText ( self . leGenre , md . genre )
assignText ( self . leImprint , md . imprint )
assignText ( self . teComments , md . comments )
assignText ( self . teNotes , md . notes )
assignText ( self . leCriticalRating , md . criticalRating )
assignText ( self . leStoryArc , md . storyArc )
assignText ( self . leScanInfo , md . scanInfo )
assignText ( self . leSeriesGroup , md . seriesGroup )
assignText ( self . leAltSeries , md . alternateSeries )
assignText ( self . leAltIssueNum , md . alternateNumber )
assignText ( self . leAltIssueCount , md . alternateCount )
assignText ( self . leWebLink , md . webLink )
assignText ( self . teCharacters , md . characters )
assignText ( self . teTeams , md . teams )
assignText ( self . teLocations , md . locations )
2012-11-14 21:17:01 -08:00
if md . format is not None and md . format != " " :
i = self . cbFormat . findText ( md . format )
if i == - 1 :
self . cbFormat . setEditText ( md . format )
else :
self . cbFormat . setCurrentIndex ( i )
2012-11-06 12:29:18 -08:00
if md . maturityRating is not None and md . maturityRating != " " :
i = self . cbMaturityRating . findText ( md . maturityRating )
if i == - 1 :
self . cbMaturityRating . setEditText ( md . maturityRating )
else :
self . cbMaturityRating . setCurrentIndex ( i )
2012-11-02 13:54:17 -07:00
if md . language is not None :
i = self . cbLanguage . findData ( md . language )
self . cbLanguage . setCurrentIndex ( i )
if md . country is not None :
i = self . cbCountry . findText ( md . country )
self . cbCountry . setCurrentIndex ( i )
if md . manga is not None :
i = self . cbManga . findData ( md . manga )
self . cbManga . setCurrentIndex ( i )
if md . blackAndWhite is not None and md . blackAndWhite :
self . cbBW . setChecked ( True )
assignText ( self . teTags , utils . listToString ( md . tags ) )
# !!! Should we clear the credits table or just avoid duplicates?
while self . twCredits . rowCount ( ) > 0 :
self . twCredits . removeRow ( 0 )
if md . credits is not None and len ( md . credits ) != 0 :
self . twCredits . setSortingEnabled ( False )
row = 0
for credit in md . credits :
2012-11-02 20:56:01 -07:00
# if the role-person pair already exists, just skip adding it to the list
if self . isDupeCredit ( credit [ ' role ' ] . title ( ) , credit [ ' person ' ] ) :
2012-11-02 13:54:17 -07:00
continue
2012-11-28 09:59:52 -08:00
self . addNewCreditEntry ( row , credit [ ' role ' ] . title ( ) , credit [ ' person ' ] , ( credit [ ' primary ' ] if credit . has_key ( ' primary ' ) else False ) )
2012-11-02 20:56:01 -07:00
2012-11-02 13:54:17 -07:00
row + = 1
self . twCredits . setSortingEnabled ( True )
2012-11-16 11:32:46 -08:00
self . updateCreditColors ( )
2012-11-02 20:56:01 -07:00
2012-11-28 09:59:52 -08:00
def addNewCreditEntry ( self , row , role , name , primary_flag = False ) :
2012-11-02 20:56:01 -07:00
self . twCredits . insertRow ( row )
item_text = role
item = QtGui . QTableWidgetItem ( item_text )
item . setFlags ( QtCore . Qt . ItemIsSelectable | QtCore . Qt . ItemIsEnabled )
2013-01-29 18:19:18 -08:00
item . setData ( QtCore . Qt . ToolTipRole , item_text )
2012-12-04 11:46:54 -08:00
self . twCredits . setItem ( row , 1 , item )
2013-01-29 18:19:18 -08:00
2012-11-02 20:56:01 -07:00
item_text = name
item = QtGui . QTableWidgetItem ( item_text )
2013-01-29 18:19:18 -08:00
item . setData ( QtCore . Qt . ToolTipRole , item_text )
2012-11-02 20:56:01 -07:00
item . setFlags ( QtCore . Qt . ItemIsSelectable | QtCore . Qt . ItemIsEnabled )
2012-12-04 11:46:54 -08:00
self . twCredits . setItem ( row , 2 , item )
item = QtGui . QTableWidgetItem ( " " )
item . setFlags ( QtCore . Qt . ItemIsSelectable | QtCore . Qt . ItemIsEnabled )
self . twCredits . setItem ( row , 0 , item )
self . updateCreditPrimaryFlag ( row , primary_flag )
2012-11-02 13:54:17 -07:00
2012-11-02 20:56:01 -07:00
def isDupeCredit ( self , role , name ) :
r = 0
while r < self . twCredits . rowCount ( ) :
2012-12-04 11:46:54 -08:00
if ( self . twCredits . item ( r , 1 ) . text ( ) == role and
self . twCredits . item ( r , 2 ) . text ( ) == name ) :
2012-11-02 20:56:01 -07:00
return True
r = r + 1
return False
2012-11-02 13:54:17 -07:00
def formToMetadata ( self ) :
#helper func
def xlate ( data , type_str ) :
s = u " {0} " . format ( data ) . strip ( )
if s == " " :
return None
elif type_str == " str " :
return s
else :
return int ( s )
# copy the data from the form into the metadata
md = self . metadata
md . series = xlate ( self . leSeries . text ( ) , " str " )
2012-11-19 11:57:16 -08:00
md . issue = xlate ( self . leIssueNum . text ( ) , " str " )
2012-11-02 13:54:17 -07:00
md . issueCount = xlate ( self . leIssueCount . text ( ) , " int " )
2012-11-19 11:57:16 -08:00
md . volume = xlate ( self . leVolumeNum . text ( ) , " int " )
2012-11-02 13:54:17 -07:00
md . volumeCount = xlate ( self . leVolumeCount . text ( ) , " int " )
md . title = xlate ( self . leTitle . text ( ) , " str " )
md . publisher = xlate ( self . lePublisher . text ( ) , " str " )
2012-11-19 11:57:16 -08:00
md . month = xlate ( self . lePubMonth . text ( ) , " int " )
md . year = xlate ( self . lePubYear . text ( ) , " int " )
2012-11-02 13:54:17 -07:00
md . genre = xlate ( self . leGenre . text ( ) , " str " )
md . imprint = xlate ( self . leImprint . text ( ) , " str " )
md . comments = xlate ( self . teComments . toPlainText ( ) , " str " )
md . notes = xlate ( self . teNotes . toPlainText ( ) , " str " )
md . criticalRating = xlate ( self . leCriticalRating . text ( ) , " int " )
2012-11-06 12:29:18 -08:00
md . maturityRating = xlate ( self . cbMaturityRating . currentText ( ) , " str " )
2012-11-02 13:54:17 -07:00
md . storyArc = xlate ( self . leStoryArc . text ( ) , " str " )
md . scanInfo = xlate ( self . leScanInfo . text ( ) , " str " )
md . seriesGroup = xlate ( self . leSeriesGroup . text ( ) , " str " )
md . alternateSeries = xlate ( self . leAltSeries . text ( ) , " str " )
md . alternateNumber = xlate ( self . leAltIssueNum . text ( ) , " int " )
md . alternateCount = xlate ( self . leAltIssueCount . text ( ) , " int " )
md . webLink = xlate ( self . leWebLink . text ( ) , " str " )
md . characters = xlate ( self . teCharacters . toPlainText ( ) , " str " )
md . teams = xlate ( self . teTeams . toPlainText ( ) , " str " )
md . locations = xlate ( self . teLocations . toPlainText ( ) , " str " )
2012-11-14 21:17:01 -08:00
md . format = xlate ( self . cbFormat . currentText ( ) , " str " )
2012-11-02 13:54:17 -07:00
md . country = xlate ( self . cbCountry . currentText ( ) , " str " )
langiso = self . cbLanguage . itemData ( self . cbLanguage . currentIndex ( ) ) . toString ( )
md . language = xlate ( langiso , " str " )
manga_code = self . cbManga . itemData ( self . cbManga . currentIndex ( ) ) . toString ( )
md . manga = xlate ( manga_code , " str " )
# Make a list from the coma delimited tags string
tmp = xlate ( self . teTags . toPlainText ( ) , " str " )
if tmp != None :
def striplist ( l ) :
return ( [ x . strip ( ) for x in l ] )
md . tags = striplist ( tmp . split ( " , " ) )
if ( self . cbBW . isChecked ( ) ) :
md . blackAndWhite = True
else :
md . blackAndWhite = False
2012-11-02 20:56:01 -07:00
# get the credits from the table
md . credits = list ( )
row = 0
while row < self . twCredits . rowCount ( ) :
2012-12-16 10:23:48 -08:00
role = u " {0} " . format ( self . twCredits . item ( row , 1 ) . text ( ) )
name = u " {0} " . format ( self . twCredits . item ( row , 2 ) . text ( ) )
2012-12-04 11:46:54 -08:00
primary_flag = self . twCredits . item ( row , 0 ) . text ( ) != " "
2012-11-28 09:59:52 -08:00
md . addCredit ( name , role , bool ( primary_flag ) )
2012-11-02 20:56:01 -07:00
row + = 1
2012-12-09 22:39:54 -08:00
md . pages = self . pageListEditor . getPageList ( )
2012-11-02 13:54:17 -07:00
def useFilename ( self ) :
2012-11-17 16:32:01 -08:00
if self . comic_archive is not None :
2012-12-08 11:57:51 -08:00
#copy the form onto metadata object
self . formToMetadata ( )
new_metadata = self . comic_archive . metadataFromFilename ( )
if new_metadata is not None :
self . metadata . overlay ( new_metadata )
self . metadataToForm ( )
2012-11-02 13:54:17 -07:00
2013-01-17 16:52:42 -08:00
def selectFolder ( self ) :
self . selectFile ( folder_mode = True )
def selectFile ( self , folder_mode = False ) :
2012-11-04 11:24:48 -08:00
dialog = QtGui . QFileDialog ( self )
2013-01-17 16:52:42 -08:00
if folder_mode :
dialog . setFileMode ( QtGui . QFileDialog . Directory )
else :
dialog . setFileMode ( QtGui . QFileDialog . ExistingFiles )
2012-11-19 22:34:09 -08:00
if self . settings . last_opened_folder is not None :
dialog . setDirectory ( self . settings . last_opened_folder )
2012-11-04 11:24:48 -08:00
#dialog.setFileMode(QtGui.QFileDialog.Directory )
2012-11-17 16:32:01 -08:00
2013-01-17 16:52:42 -08:00
if not folder_mode :
if platform . system ( ) != " Windows " and utils . which ( " unrar " ) is None :
archive_filter = " Comic archive files (*.cbz *.zip) "
else :
archive_filter = " Comic archive files (*.cbz *.zip *.cbr *.rar) "
filters = [
archive_filter ,
" Any files (*) "
]
dialog . setNameFilters ( filters )
2012-11-04 11:24:48 -08:00
if ( dialog . exec_ ( ) ) :
fileList = dialog . selectedFiles ( )
2013-01-18 13:12:23 -08:00
#if self.dirtyFlagVerification( "Open Archive",
# "If you open a new archive now, data in the form will be lost. Are you sure?"):
self . fileSelectionList . addPathList ( fileList )
2012-11-04 21:04:30 -08:00
2013-01-20 12:43:48 -08:00
def autoIdentifySearch ( self ) :
2012-11-15 10:14:32 -08:00
if self . comic_archive is None :
2013-01-20 12:43:48 -08:00
QtGui . QMessageBox . warning ( self , self . tr ( " Automatic Identify Search " ) ,
2012-11-15 10:14:32 -08:00
self . tr ( " You need to load a comic first! " ) )
return
self . queryOnline ( autoselect = True )
def queryOnline ( self , autoselect = False ) :
2012-11-06 12:29:18 -08:00
2012-11-17 16:32:01 -08:00
issue_number = str ( self . leIssueNum . text ( ) ) . strip ( )
if autoselect and issue_number == " " :
2013-01-20 12:43:48 -08:00
QtGui . QMessageBox . information ( self , " Automatic Identify Search " , " Can ' t auto-identify without an issue number (yet!) " )
2012-11-17 16:32:01 -08:00
return
2012-11-02 13:54:17 -07:00
2012-12-29 21:06:12 -08:00
if unicode ( self . leSeries . text ( ) ) . strip ( ) != " " :
series_name = unicode ( self . leSeries . text ( ) ) . strip ( )
2012-11-02 13:54:17 -07:00
else :
2012-11-17 16:32:01 -08:00
QtGui . QMessageBox . information ( self , self . tr ( " Online Search " ) , self . tr ( " Need to enter a series name to search. " ) )
2012-11-02 13:54:17 -07:00
return
2012-11-28 09:59:52 -08:00
2012-11-16 19:02:04 -08:00
year = str ( self . lePubYear . text ( ) ) . strip ( )
if year == " " :
year = None
2012-12-08 11:57:51 -08:00
cover_index_list = self . metadata . getCoverPageIndexList ( )
selector = VolumeSelectionWindow ( self , series_name , issue_number , year , cover_index_list , self . comic_archive , self . settings , autoselect )
2012-11-16 14:45:35 -08:00
title = " Search: ' " + series_name + " ' - "
selector . setWindowTitle ( title + " Select Series " )
2012-11-02 13:54:17 -07:00
selector . setModal ( True )
selector . exec_ ( )
if selector . result ( ) :
#we should now have a volume ID
2012-11-12 16:12:43 -08:00
QtGui . QApplication . setOverrideCursor ( QtGui . QCursor ( QtCore . Qt . WaitCursor ) )
2012-11-02 13:54:17 -07:00
2012-11-27 10:02:08 -08:00
#copy the form onto metadata object
self . formToMetadata ( )
2012-11-28 12:15:20 -08:00
try :
comicVine = ComicVineTalker ( )
2012-12-17 10:44:33 -08:00
new_metadata = comicVine . fetchIssueData ( selector . volume_id , selector . issue_number , self . settings )
2012-11-28 12:15:20 -08:00
except ComicVineTalkerException :
QtGui . QApplication . restoreOverrideCursor ( )
QtGui . QMessageBox . critical ( self , self . tr ( " Network Issue " ) , self . tr ( " Could not connect to ComicVine to get issue details! " ) )
else :
QtGui . QApplication . restoreOverrideCursor ( )
2012-11-30 09:55:28 -08:00
if new_metadata is not None :
2012-12-17 13:19:21 -08:00
if self . settings . apply_cbl_transform_on_cv_import :
new_metadata = CBLTransformer ( new_metadata , self . settings ) . apply ( )
2012-11-30 09:55:28 -08:00
self . metadata . overlay ( new_metadata )
# Now push the new combined data into the edit controls
self . metadataToForm ( )
else :
QtGui . QMessageBox . critical ( self , self . tr ( " Search " ) , self . tr ( " Could not find an issue {0} for that series " . format ( selector . issue_number ) ) )
2012-11-02 13:54:17 -07:00
def commitMetadata ( self ) :
2012-11-04 21:04:30 -08:00
if ( self . metadata is not None and self . comic_archive is not None ) :
2012-11-17 16:32:01 -08:00
2013-01-18 13:12:23 -08:00
if self . comic_archive . isRar ( ) and self . save_data_style == MetaDataStyle . CBI :
2012-11-20 14:38:10 -08:00
if self . settings . ask_about_cbi_in_rar :
2012-11-29 18:14:06 -08:00
checked = OptionalMessageDialog . msg ( self , " RAR and ComicBookLover " ,
2012-11-23 20:56:56 -08:00
"""
2012-11-29 18:14:06 -08:00
You are about to write a CBL tag block to a RAR archive !
2012-11-23 20:56:56 -08:00
While technically possible , no known reader can read those tags from RAR
2012-11-29 18:14:06 -08:00
yet . < br > < br >
If you would like this feature in the ComicBookLover apps , please go to their
forums and add your voice to a feature request !
2012-11-28 09:59:52 -08:00
< a href = http : / / forums . comicbooklover . com / categories / ipad - features >
2012-11-23 20:56:56 -08:00
http : / / forums . comicbooklover . com / categories / ipad - features < / a > < br >
2012-11-28 09:59:52 -08:00
< a href = http : / / forums . comicbooklover . com / categories / mac - features >
2012-11-29 18:14:06 -08:00
http : / / forums . comicbooklover . com / categories / mac - features < / a >
2012-11-23 20:56:56 -08:00
""" ,
2012-11-20 14:38:10 -08:00
)
self . settings . ask_about_cbi_in_rar = not checked
2012-11-17 16:32:01 -08:00
reply = QtGui . QMessageBox . question ( self ,
self . tr ( " Save Tags " ) ,
2013-01-18 13:12:23 -08:00
self . tr ( " Are you sure you wish to save " + MetaDataStyle . name [ self . save_data_style ] + " tags to this archive? " ) ,
2012-11-17 16:32:01 -08:00
QtGui . QMessageBox . Yes , QtGui . QMessageBox . No )
if reply == QtGui . QMessageBox . Yes :
QtGui . QApplication . setOverrideCursor ( QtGui . QCursor ( QtCore . Qt . WaitCursor ) )
self . formToMetadata ( )
2013-01-18 13:12:23 -08:00
success = self . comic_archive . writeMetadata ( self . metadata , self . save_data_style )
2013-01-29 18:19:18 -08:00
self . comic_archive . loadCache ( [ MetaDataStyle . CBI , MetaDataStyle . CIX ] )
2012-11-27 16:33:51 -08:00
QtGui . QApplication . restoreOverrideCursor ( )
if not success :
QtGui . QMessageBox . warning ( self , self . tr ( " Save failed " ) , self . tr ( " The tag save operation seemed to fail! " ) )
else :
self . clearDirtyFlag ( )
self . updateInfoBox ( )
2012-12-06 12:32:24 -08:00
self . updateMenus ( )
2012-11-27 16:33:51 -08:00
#QtGui.QMessageBox.information(self, self.tr("Yeah!"), self.tr("File written."))
2013-01-18 13:12:23 -08:00
self . fileSelectionList . updateCurrentRow ( )
2012-11-02 13:54:17 -07:00
else :
QtGui . QMessageBox . information ( self , self . tr ( " Whoops! " ) , self . tr ( " No data to commit! " ) )
2013-01-18 13:12:23 -08:00
def setLoadDataStyle ( self , s ) :
if self . dirtyFlagVerification ( " Change Tag Read Style " ,
2013-01-21 16:19:59 -08:00
" If you change read tag style now, data in the form will be lost. Are you sure? " ) :
2013-01-18 13:12:23 -08:00
self . load_data_style , b = self . cbLoadDataStyle . itemData ( s ) . toInt ( )
self . settings . last_selected_load_data_style = self . load_data_style
self . updateMenus ( )
if self . comic_archive is not None :
self . loadArchive ( self . comic_archive )
else :
self . cbLoadDataStyle . currentIndexChanged . disconnect ( self . setLoadDataStyle )
self . adjustLoadStyleCombo ( )
self . cbLoadDataStyle . currentIndexChanged . connect ( self . setLoadDataStyle )
2012-11-19 22:34:09 -08:00
2013-01-18 13:12:23 -08:00
def setSaveDataStyle ( self , s ) :
self . save_data_style , b = self . cbSaveDataStyle . itemData ( s ) . toInt ( )
self . settings . last_selected_save_data_style = self . save_data_style
2012-11-02 13:54:17 -07:00
self . updateStyleTweaks ( )
2012-12-06 12:32:24 -08:00
self . updateMenus ( )
2012-11-16 11:32:46 -08:00
def updateCreditColors ( self ) :
inactive_color = QtGui . QColor ( 255 , 170 , 150 )
active_palette = self . leSeries . palette ( )
active_color = active_palette . color ( QtGui . QPalette . Base )
cix_credits = ComicInfoXml ( ) . getParseableCredits ( )
2013-01-18 13:12:23 -08:00
if self . save_data_style == MetaDataStyle . CIX :
2012-11-16 11:32:46 -08:00
#loop over credit table, mark selected rows
r = 0
while r < self . twCredits . rowCount ( ) :
2012-12-04 11:46:54 -08:00
if str ( self . twCredits . item ( r , 1 ) . text ( ) ) . lower ( ) not in cix_credits :
self . twCredits . item ( r , 1 ) . setBackgroundColor ( inactive_color )
2012-11-16 11:32:46 -08:00
else :
2012-12-04 11:46:54 -08:00
self . twCredits . item ( r , 1 ) . setBackgroundColor ( active_color )
# turn off entire primary column
self . twCredits . item ( r , 0 ) . setBackgroundColor ( inactive_color )
2012-11-16 11:32:46 -08:00
r = r + 1
2013-01-18 13:12:23 -08:00
if self . save_data_style == MetaDataStyle . CBI :
2012-11-16 11:32:46 -08:00
#loop over credit table, make all active color
r = 0
while r < self . twCredits . rowCount ( ) :
self . twCredits . item ( r , 0 ) . setBackgroundColor ( active_color )
2012-12-04 11:46:54 -08:00
self . twCredits . item ( r , 1 ) . setBackgroundColor ( active_color )
2012-11-16 11:32:46 -08:00
r = r + 1
2012-11-02 13:54:17 -07:00
def updateStyleTweaks ( self ) :
# depending on the current data style, certain fields are disabled
inactive_color = QtGui . QColor ( 255 , 170 , 150 )
active_palette = self . leSeries . palette ( )
inactive_palette1 = self . leSeries . palette ( )
inactive_palette1 . setColor ( QtGui . QPalette . Base , inactive_color )
inactive_palette2 = self . leSeries . palette ( )
inactive_palette3 = self . leSeries . palette ( )
inactive_palette3 . setColor ( QtGui . QPalette . Base , inactive_color )
inactive_palette3 . setColor ( QtGui . QPalette . Base , inactive_color )
#helper func
def enableWidget ( item , enable ) :
inactive_palette3 . setColor ( item . backgroundRole ( ) , inactive_color )
inactive_palette2 . setColor ( item . backgroundRole ( ) , inactive_color )
inactive_palette3 . setColor ( item . foregroundRole ( ) , inactive_color )
if enable :
item . setPalette ( active_palette )
2012-11-16 11:32:46 -08:00
item . setAutoFillBackground ( False )
2012-11-02 13:54:17 -07:00
if type ( item ) == QtGui . QCheckBox :
item . setEnabled ( True )
elif type ( item ) == QtGui . QComboBox :
item . setEnabled ( True )
else :
item . setReadOnly ( False )
else :
2012-11-16 11:32:46 -08:00
item . setAutoFillBackground ( True )
2012-11-02 13:54:17 -07:00
if type ( item ) == QtGui . QCheckBox :
item . setPalette ( inactive_palette2 )
item . setEnabled ( False )
elif type ( item ) == QtGui . QComboBox :
item . setPalette ( inactive_palette3 )
item . setEnabled ( False )
else :
item . setReadOnly ( True )
item . setPalette ( inactive_palette1 )
cbi_only = [ self . leVolumeCount , self . cbCountry , self . leCriticalRating , self . teTags ]
cix_only = [
self . leImprint , self . teNotes , self . cbBW , self . cbManga ,
self . leStoryArc , self . leScanInfo , self . leSeriesGroup ,
self . leAltSeries , self . leAltIssueNum , self . leAltIssueCount ,
self . leWebLink , self . teCharacters , self . teTeams ,
2012-11-14 21:17:01 -08:00
self . teLocations , self . cbMaturityRating , self . cbFormat
2012-11-02 13:54:17 -07:00
]
2012-11-16 11:32:46 -08:00
2013-01-18 13:12:23 -08:00
if self . save_data_style == MetaDataStyle . CIX :
2012-11-02 13:54:17 -07:00
for item in cix_only :
enableWidget ( item , True )
for item in cbi_only :
enableWidget ( item , False )
2013-01-18 13:12:23 -08:00
if self . save_data_style == MetaDataStyle . CBI :
2012-11-02 13:54:17 -07:00
for item in cbi_only :
enableWidget ( item , True )
for item in cix_only :
enableWidget ( item , False )
2012-11-16 11:32:46 -08:00
self . updateCreditColors ( )
2013-01-18 13:12:23 -08:00
self . pageListEditor . setMetadataStyle ( self . save_data_style )
2012-11-16 11:32:46 -08:00
2012-11-02 20:56:01 -07:00
def cellDoubleClicked ( self , r , c ) :
self . editCredit ( )
def addCredit ( self ) :
self . modifyCredits ( " add " )
def editCredit ( self ) :
if ( self . twCredits . currentRow ( ) > - 1 ) :
self . modifyCredits ( " edit " )
2012-12-04 11:46:54 -08:00
def updateCreditPrimaryFlag ( self , row , primary ) :
# if we're clearing a flagm do it and quit
if not primary :
self . twCredits . item ( row , 0 ) . setText ( " " )
return
2012-11-02 20:56:01 -07:00
2012-12-04 11:46:54 -08:00
# otherwise, we need to check for, and clear, other primaries with same role
role = str ( self . twCredits . item ( row , 1 ) . text ( ) )
r = 0
while r < self . twCredits . rowCount ( ) :
if ( self . twCredits . item ( r , 0 ) . text ( ) != " " and
str ( self . twCredits . item ( r , 1 ) . text ( ) ) . lower ( ) == role . lower ( ) ) :
self . twCredits . item ( r , 0 ) . setText ( " " )
r = r + 1
# Now set our new primary
self . twCredits . item ( row , 0 ) . setText ( " Yes " )
2012-11-02 20:56:01 -07:00
def modifyCredits ( self , action ) :
if action == " edit " :
row = self . twCredits . currentRow ( )
2012-12-04 11:46:54 -08:00
role = self . twCredits . item ( row , 1 ) . text ( )
name = self . twCredits . item ( row , 2 ) . text ( )
primary = self . twCredits . item ( row , 0 ) . text ( ) != " "
2012-11-02 20:56:01 -07:00
else :
role = " "
name = " "
2012-12-04 11:46:54 -08:00
primary = False
2012-11-02 20:56:01 -07:00
2012-12-04 11:46:54 -08:00
editor = CreditEditorWindow ( self , CreditEditorWindow . ModeEdit , role , name , primary )
2012-11-02 20:56:01 -07:00
editor . setModal ( True )
editor . exec_ ( )
if editor . result ( ) :
2012-12-04 11:46:54 -08:00
new_role , new_name , new_primary = editor . getCredits ( )
2012-11-02 20:56:01 -07:00
2012-12-04 11:46:54 -08:00
if new_name == name and new_role == role and new_primary == primary :
2012-11-02 20:56:01 -07:00
#nothing has changed, just quit
return
2012-12-04 11:46:54 -08:00
# name and role is the same, but primary flag changed
if new_name == name and new_role == role :
self . updateCreditPrimaryFlag ( row , new_primary )
return
2012-11-02 20:56:01 -07:00
# check for dupes
ok_to_mod = True
if self . isDupeCredit ( new_role , new_name ) :
# delete the dupe credit from list
reply = QtGui . QMessageBox . question ( self ,
2012-11-04 21:04:30 -08:00
self . tr ( " Duplicate Credit! " ) ,
self . tr ( " This will create a duplicate credit entry. Would you like to merge the entries, or create a duplicate? " ) ,
self . tr ( " Merge " ) , self . tr ( " Duplicate " ) )
2012-11-02 20:56:01 -07:00
if reply == 0 :
# merge
if action == " edit " :
# just remove the row that would be same
self . twCredits . removeRow ( row )
2012-12-04 11:46:54 -08:00
# TODO -- need to find the row of the dupe, and possible change the primary flag
2012-11-02 20:56:01 -07:00
ok_to_mod = False
if ok_to_mod :
#modify it
if action == " edit " :
2012-12-04 11:46:54 -08:00
self . twCredits . item ( row , 1 ) . setText ( new_role )
self . twCredits . item ( row , 2 ) . setText ( new_name )
self . updateCreditPrimaryFlag ( row , new_primary )
2012-11-02 20:56:01 -07:00
else :
# add new entry
row = self . twCredits . rowCount ( )
2012-12-04 11:46:54 -08:00
self . addNewCreditEntry ( row , new_role , new_name , new_primary )
2012-11-16 11:32:46 -08:00
self . updateCreditColors ( )
2012-11-04 21:04:30 -08:00
self . setDirtyFlag ( )
2012-11-02 20:56:01 -07:00
def removeCredit ( self ) :
row = self . twCredits . currentRow ( )
if row != - 1 :
self . twCredits . removeRow ( row )
2012-11-04 21:04:30 -08:00
self . setDirtyFlag ( )
2012-11-02 13:54:17 -07:00
2012-11-06 12:29:18 -08:00
def showSettings ( self ) :
2012-11-24 10:17:35 -08:00
2012-11-06 12:29:18 -08:00
settingswin = SettingsWindow ( self , self . settings )
settingswin . setModal ( True )
settingswin . exec_ ( )
if settingswin . result ( ) :
pass
2012-11-02 13:54:17 -07:00
2012-11-20 09:26:15 -08:00
def setAppPosition ( self ) :
if self . settings . last_main_window_width != 0 :
self . move ( self . settings . last_main_window_x , self . settings . last_main_window_y )
2012-11-20 13:20:26 -08:00
self . resize ( self . settings . last_main_window_width , self . settings . last_main_window_height )
2012-11-20 09:26:15 -08:00
else :
screen = QtGui . QDesktopWidget ( ) . screenGeometry ( )
size = self . frameGeometry ( )
self . move ( ( screen . width ( ) - size . width ( ) ) / 2 , ( screen . height ( ) - size . height ( ) ) / 2 )
2012-11-02 13:54:17 -07:00
2013-01-18 13:12:23 -08:00
def adjustLoadStyleCombo ( self ) :
2012-11-02 13:54:17 -07:00
# select the current style
2013-01-18 13:12:23 -08:00
if ( self . load_data_style == MetaDataStyle . CBI ) :
self . cbLoadDataStyle . setCurrentIndex ( 0 )
elif ( self . load_data_style == MetaDataStyle . CIX ) :
self . cbLoadDataStyle . setCurrentIndex ( 1 )
def adjustSaveStyleCombo ( self ) :
# select the current style
if ( self . save_data_style == MetaDataStyle . CBI ) :
self . cbSaveDataStyle . setCurrentIndex ( 0 )
elif ( self . save_data_style == MetaDataStyle . CIX ) :
self . cbSaveDataStyle . setCurrentIndex ( 1 )
2012-11-04 11:24:48 -08:00
self . updateStyleTweaks ( )
def populateComboBoxes ( self ) :
# Add the entries to the tag style combobox
2013-01-18 13:12:23 -08:00
self . cbLoadDataStyle . addItem ( " ComicBookLover " , MetaDataStyle . CBI )
self . cbLoadDataStyle . addItem ( " ComicRack " , MetaDataStyle . CIX )
self . adjustLoadStyleCombo ( )
self . cbSaveDataStyle . addItem ( " ComicBookLover " , MetaDataStyle . CBI )
self . cbSaveDataStyle . addItem ( " ComicRack " , MetaDataStyle . CIX )
self . adjustSaveStyleCombo ( )
2012-11-02 13:54:17 -07:00
# Add the entries to the country combobox
self . cbCountry . addItem ( " " , " " )
for c in utils . countries :
self . cbCountry . addItem ( c [ 1 ] , c [ 0 ] )
# Add the entries to the language combobox
self . cbLanguage . addItem ( " " , " " )
lang_dict = utils . getLanguageDict ( )
for key in sorted ( lang_dict , cmp = locale . strcoll , key = lang_dict . get ) :
self . cbLanguage . addItem ( lang_dict [ key ] , key )
# Add the entries to the manga combobox
self . cbManga . addItem ( " " , " " )
self . cbManga . addItem ( " Yes " , " Yes " )
self . cbManga . addItem ( " Yes (Right to Left) " , " YesAndRightToLeft " )
self . cbManga . addItem ( " No " , " No " )
2012-11-04 11:24:48 -08:00
2012-11-06 12:29:18 -08:00
# Add the entries to the maturity combobox
self . cbMaturityRating . addItem ( " " , " " )
self . cbMaturityRating . addItem ( " Everyone " , " " )
self . cbMaturityRating . addItem ( " G " , " " )
self . cbMaturityRating . addItem ( " Early Childhood " , " " )
self . cbMaturityRating . addItem ( " Everyone 10+ " , " " )
self . cbMaturityRating . addItem ( " PG " , " " )
self . cbMaturityRating . addItem ( " Kids to Adults " , " " )
self . cbMaturityRating . addItem ( " Teen " , " " )
self . cbMaturityRating . addItem ( " MA15+ " , " " )
self . cbMaturityRating . addItem ( " Mature 17+ " , " " )
self . cbMaturityRating . addItem ( " R18+ " , " " )
self . cbMaturityRating . addItem ( " X18+ " , " " )
self . cbMaturityRating . addItem ( " Adults Only 18+ " , " " )
self . cbMaturityRating . addItem ( " Rating Pending " , " " )
2012-11-14 20:34:33 -08:00
# Add entries to the format combobox
2012-11-14 21:17:01 -08:00
self . cbFormat . addItem ( " " )
self . cbFormat . addItem ( " .1 " )
self . cbFormat . addItem ( " -1 " )
self . cbFormat . addItem ( " 1 Shot " )
self . cbFormat . addItem ( " 1/2 " )
self . cbFormat . addItem ( " 1-Shot " )
self . cbFormat . addItem ( " Annotation " )
self . cbFormat . addItem ( " Annotations " )
self . cbFormat . addItem ( " Annual " )
self . cbFormat . addItem ( " Anthology " )
self . cbFormat . addItem ( " B&W " )
self . cbFormat . addItem ( " B/W " )
self . cbFormat . addItem ( " B&&W " )
self . cbFormat . addItem ( " Black & White " )
self . cbFormat . addItem ( " Box Set " )
self . cbFormat . addItem ( " Box-Set " )
self . cbFormat . addItem ( " Crossover " )
self . cbFormat . addItem ( " Director ' s Cut " )
self . cbFormat . addItem ( " Epilogue " )
self . cbFormat . addItem ( " Event " )
self . cbFormat . addItem ( " FCBD " )
self . cbFormat . addItem ( " Flyer " )
self . cbFormat . addItem ( " Giant " )
self . cbFormat . addItem ( " Giant Size " )
self . cbFormat . addItem ( " Giant-Size " )
self . cbFormat . addItem ( " Graphic Novel " )
self . cbFormat . addItem ( " Hardcover " )
self . cbFormat . addItem ( " Hard-Cover " )
self . cbFormat . addItem ( " King " )
self . cbFormat . addItem ( " King Size " )
self . cbFormat . addItem ( " King-Size " )
self . cbFormat . addItem ( " Limited Series " )
self . cbFormat . addItem ( " Magazine " )
self . cbFormat . addItem ( " -1 " )
self . cbFormat . addItem ( " NSFW " )
self . cbFormat . addItem ( " One Shot " )
self . cbFormat . addItem ( " One-Shot " )
self . cbFormat . addItem ( " Point 1 " )
self . cbFormat . addItem ( " Preview " )
self . cbFormat . addItem ( " Prologue " )
self . cbFormat . addItem ( " Reference " )
self . cbFormat . addItem ( " Review " )
self . cbFormat . addItem ( " Reviewed " )
self . cbFormat . addItem ( " Scanlation " )
self . cbFormat . addItem ( " Script " )
self . cbFormat . addItem ( " Series " )
self . cbFormat . addItem ( " Sketch " )
self . cbFormat . addItem ( " Special " )
self . cbFormat . addItem ( " TPB " )
self . cbFormat . addItem ( " Trade Paper Back " )
self . cbFormat . addItem ( " WebComic " )
self . cbFormat . addItem ( " Web Comic " )
self . cbFormat . addItem ( " Year 1 " )
self . cbFormat . addItem ( " Year One " )
2012-11-14 20:34:33 -08:00
2012-12-06 12:32:24 -08:00
def removeAuto ( self ) :
2013-01-18 13:12:23 -08:00
self . removeTags ( self . save_data_style )
2012-11-06 12:29:18 -08:00
2012-11-04 11:24:48 -08:00
def removeCBLTags ( self ) :
self . removeTags ( MetaDataStyle . CBI )
def removeCRTags ( self ) :
self . removeTags ( MetaDataStyle . CIX )
def removeTags ( self , style ) :
# remove the indicated tags from the archive
2013-01-18 22:15:33 -08:00
ca_list = self . fileSelectionList . getSelectedArchiveList ( )
has_md_count = 0
for ca in ca_list :
if ca . hasMetadata ( style ) :
has_md_count + = 1
2013-01-20 12:43:48 -08:00
if has_md_count == 0 :
QtGui . QMessageBox . information ( self , self . tr ( " Remove Tags " ) ,
self . tr ( " No archives with {0} tags selected! " . format ( MetaDataStyle . name [ style ] ) ) )
return
2013-01-18 22:15:33 -08:00
if has_md_count != 0 and not self . dirtyFlagVerification ( " Remove Tags " ,
2013-01-21 16:19:59 -08:00
" If you remove tags now, unsaved data in the form will be lost. Are you sure? " ) :
2013-01-18 22:15:33 -08:00
return
if has_md_count != 0 :
2012-11-04 11:24:48 -08:00
reply = QtGui . QMessageBox . question ( self ,
self . tr ( " Remove Tags " ) ,
2013-01-18 22:15:33 -08:00
self . tr ( " Are you sure you wish to remove the {0} tags from {1} archive(s)? " . format ( MetaDataStyle . name [ style ] , has_md_count ) ) ,
2012-11-04 11:24:48 -08:00
QtGui . QMessageBox . Yes , QtGui . QMessageBox . No )
if reply == QtGui . QMessageBox . Yes :
2013-01-18 22:15:33 -08:00
progdialog = QtGui . QProgressDialog ( " " , " Cancel " , 0 , has_md_count , self )
progdialog . setWindowTitle ( " Removing Tags " )
progdialog . setWindowModality ( QtCore . Qt . WindowModal )
2013-01-20 22:39:44 -08:00
progdialog . show ( )
2013-01-18 22:15:33 -08:00
prog_idx = 0
2013-01-29 18:19:18 -08:00
failed_list = [ ]
success_count = 0
2013-01-18 22:15:33 -08:00
for ca in ca_list :
if ca . hasMetadata ( style ) :
QtCore . QCoreApplication . processEvents ( )
if progdialog . wasCanceled ( ) :
break
progdialog . setValue ( prog_idx )
2013-01-21 16:19:59 -08:00
prog_idx + = 1
2013-01-20 22:39:44 -08:00
progdialog . setLabelText ( ca . path )
QtCore . QCoreApplication . processEvents ( )
2013-01-18 22:15:33 -08:00
if ca . hasMetadata ( style ) and ca . isWritable ( ) :
if not ca . removeMetadata ( style ) :
2013-01-29 18:19:18 -08:00
failed_list . append ( ca . path )
else :
success_count + = 1
ca . loadCache ( [ MetaDataStyle . CBI , MetaDataStyle . CIX ] )
2013-01-18 22:15:33 -08:00
progdialog . close ( )
self . fileSelectionList . updateSelectedRows ( )
self . updateInfoBox ( )
self . updateMenus ( )
2012-11-04 11:24:48 -08:00
2013-01-29 18:19:18 -08:00
summary = u " Successfully removed tags in {0} archive(s). " . format ( success_count )
if len ( failed_list ) > 0 :
summary + = u " \n \n The remove operation failed in the following {0} archive(s): \n " . format ( len ( failed_list ) )
for f in failed_list :
summary + = u " \t {0} \n " . format ( f )
dlg = LogWindow ( self )
dlg . setText ( summary )
dlg . setWindowTitle ( " Tag Remove Summary " )
dlg . exec_ ( )
2013-01-20 12:43:48 -08:00
def copyTags ( self ) :
# copy the indicated tags in the archive
ca_list = self . fileSelectionList . getSelectedArchiveList ( )
has_src_count = 0
src_style = self . load_data_style
dest_style = self . save_data_style
if src_style == dest_style :
QtGui . QMessageBox . information ( self , self . tr ( " Copy Tags " ) , self . tr ( " Can ' t copy tag style onto itself. " +
" Read style and modify style must be different. " ) )
return
for ca in ca_list :
if ca . hasMetadata ( src_style ) :
has_src_count + = 1
if has_src_count == 0 :
QtGui . QMessageBox . information ( self , self . tr ( " Copy Tags " ) , self . tr ( " No archives with {0} tags selected! " . format (
MetaDataStyle . name [ src_style ] ) ) )
return
if has_src_count != 0 and not self . dirtyFlagVerification ( " Copy Tags " ,
2013-01-21 16:19:59 -08:00
" If you copy tags now, unsaved data in the form may be lost. Are you sure? " ) :
2013-01-20 12:43:48 -08:00
return
if has_src_count != 0 :
reply = QtGui . QMessageBox . question ( self ,
2013-01-21 16:19:59 -08:00
self . tr ( " Copy Tags " ) ,
self . tr ( " Are you sure you wish to copy the {0} tags to {1} tags in {2} archive(s)? " . format (
2013-01-20 12:43:48 -08:00
MetaDataStyle . name [ src_style ] , MetaDataStyle . name [ dest_style ] , has_src_count ) ) ,
2013-01-21 16:19:59 -08:00
QtGui . QMessageBox . Yes , QtGui . QMessageBox . No )
2013-01-20 12:43:48 -08:00
if reply == QtGui . QMessageBox . Yes :
progdialog = QtGui . QProgressDialog ( " " , " Cancel " , 0 , has_src_count , self )
progdialog . setWindowTitle ( " Copying Tags " )
progdialog . setWindowModality ( QtCore . Qt . WindowModal )
2013-01-20 22:39:44 -08:00
progdialog . show ( )
2013-01-20 12:43:48 -08:00
prog_idx = 0
2013-01-29 18:19:18 -08:00
failed_list = [ ]
success_count = 0
2013-01-20 12:43:48 -08:00
for ca in ca_list :
if ca . hasMetadata ( src_style ) :
QtCore . QCoreApplication . processEvents ( )
if progdialog . wasCanceled ( ) :
break
progdialog . setValue ( prog_idx )
2013-01-21 16:19:59 -08:00
prog_idx + = 1
2013-01-20 22:39:44 -08:00
progdialog . setLabelText ( ca . path )
2013-01-20 12:43:48 -08:00
QtCore . QCoreApplication . processEvents ( )
if ca . hasMetadata ( src_style ) and ca . isWritable ( ) :
md = ca . readMetadata ( src_style )
if dest_style == MetaDataStyle . CBI and self . settings . apply_cbl_transform_on_bulk_operation :
md = CBLTransformer ( md , self . settings ) . apply ( )
if not ca . writeMetadata ( md , dest_style ) :
2013-01-29 18:19:18 -08:00
failed_list . append ( ca . path )
else :
success_count + = 1
ca . loadCache ( [ MetaDataStyle . CBI , MetaDataStyle . CIX ] )
2013-01-20 12:43:48 -08:00
progdialog . close ( )
self . fileSelectionList . updateSelectedRows ( )
self . updateInfoBox ( )
self . updateMenus ( )
2013-01-29 18:19:18 -08:00
summary = u " Successfully copied tags in {0} archive(s). " . format ( success_count )
if len ( failed_list ) > 0 :
summary + = u " \n \n The copy operation failed in the following {0} archive(s): \n " . format ( len ( failed_list ) )
for f in failed_list :
summary + = u " \t {0} \n " . format ( f )
dlg = LogWindow ( self )
dlg . setText ( summary )
dlg . setWindowTitle ( " Tag Copy Summary " )
dlg . exec_ ( )
2013-01-21 16:19:59 -08:00
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 :
2013-01-21 20:09:08 -08:00
if self . settings . apply_cbl_transform_on_cv_import :
2013-01-21 16:19:59 -08:00
cv_md = CBLTransformer ( cv_md , self . settings ) . apply ( )
QtGui . QApplication . restoreOverrideCursor ( )
return cv_md
2013-01-24 22:17:45 -08:00
def autoTagLog ( self , text ) :
IssueIdentifier . defaultWriteOutput ( text )
if self . atprogdialog is not None :
self . atprogdialog . textEdit . insertPlainText ( text )
self . atprogdialog . textEdit . ensureCursorVisible ( )
QtCore . QCoreApplication . processEvents ( )
QtCore . QCoreApplication . processEvents ( )
QtCore . QCoreApplication . processEvents ( )
2013-01-22 17:25:17 -08:00
def identifyAndTagSingleArchive ( self , ca , match_results , dlg ) :
2013-01-21 16:19:59 -08:00
success = False
ii = IssueIdentifier ( ca , self . settings )
2013-01-22 17:25:17 -08:00
2013-01-21 16:19:59 -08:00
# 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
2013-01-22 17:25:17 -08:00
if dlg . dontUseYear :
2013-01-21 20:09:08 -08:00
md . year = None
2013-01-22 17:25:17 -08:00
if dlg . assumeIssueOne and ( md . issue is None or md . issue == " " ) :
md . issue = " 1 "
2013-01-21 16:19:59 -08:00
ii . setAdditionalMetadata ( md )
ii . onlyUseAdditionalMetaData = True
2013-01-24 22:17:45 -08:00
ii . setOutputFunction ( self . autoTagLog )
2013-01-21 16:19:59 -08:00
ii . cover_page_index = md . getCoverPageIndexList ( ) [ 0 ]
2013-01-21 20:09:08 -08:00
ii . setCoverURLCallback ( self . atprogdialog . setTestImage )
2013-01-21 16:19:59 -08:00
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 :
2013-01-24 22:17:45 -08:00
self . autoTagLog ( " Online search: Multiple matches. Save aborted \n " )
2013-01-22 21:25:50 -08:00
match_results . multipleMatches . append ( MultipleMatch ( ca , matches ) )
2013-01-25 09:20:20 -08:00
elif low_confidence and not dlg . autoSaveOnLow :
self . autoTagLog ( " Online search: Low confidence match. Save aborted \n " )
match_results . noMatches . append ( ca . path )
2013-01-21 16:19:59 -08:00
elif not found_match :
2013-01-24 22:17:45 -08:00
self . autoTagLog ( " Online search: No match found. Save aborted \n " )
2013-01-21 16:19:59 -08:00
match_results . noMatches . append ( ca . path )
else :
# a single match!
2013-01-25 09:20:20 -08:00
if low_confidence :
self . autoTagLog ( " Online search: Low confidence match, but saving anyways, as incdicated... \n " )
2013-01-21 16:19:59 -08:00
# now get the particular issue data
cv_md = self . actualIssueDataFetch ( matches [ 0 ] )
if cv_md is None :
match_results . fetchDataFailures . append ( ca . path )
2013-01-28 09:38:59 -08:00
if cv_md is not None :
2013-01-21 16:19:59 -08:00
md . overlay ( cv_md )
if not ca . writeMetadata ( md , self . save_data_style ) :
match_results . writeFailures . append ( ca . path )
2013-01-25 09:20:20 -08:00
self . autoTagLog ( " Save failed ;-( \n " )
2013-01-21 16:19:59 -08:00
else :
match_results . goodMatches . append ( ca . path )
success = True
2013-01-29 18:19:18 -08:00
self . autoTagLog ( " Save complete! \n " )
ca . loadCache ( [ MetaDataStyle . CBI , MetaDataStyle . CIX ] )
2013-01-25 09:20:20 -08:00
2013-01-21 16:19:59 -08:00
return success , match_results
2013-01-20 12:43:48 -08:00
def autoTag ( self ) :
2013-01-21 16:19:59 -08:00
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
2013-01-22 17:25:17 -08:00
atstartdlg = AutoTagStartWindow ( self , self . settings ,
2013-01-21 16:19:59 -08:00
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 " ) )
2013-01-22 17:25:17 -08:00
atstartdlg . setModal ( True )
if not atstartdlg . exec_ ( ) :
2013-01-21 16:19:59 -08:00
return
2013-01-21 20:09:08 -08:00
self . atprogdialog = AutoTagProgressWindow ( self )
self . atprogdialog . setModal ( True )
self . atprogdialog . show ( )
self . atprogdialog . progressBar . setMaximum ( len ( ca_list ) )
self . atprogdialog . setWindowTitle ( " Auto-Tagging " )
2013-01-24 22:17:45 -08:00
self . autoTagLog ( u " ======================================================================== \n " )
self . autoTagLog ( u " Auto-Tagging Started for {0} items \n " . format ( len ( ca_list ) ) )
2013-01-21 16:19:59 -08:00
prog_idx = 0
match_results = OnlineMatchResults ( )
for ca in ca_list :
2013-01-24 22:17:45 -08:00
self . autoTagLog ( u " ============================================================ \n " )
self . autoTagLog ( u " Auto-Tagging {0} of {1} \n " . format ( prog_idx + 1 , len ( ca_list ) ) )
self . autoTagLog ( u " {0} \n " . format ( ca . path ) )
2013-01-21 20:09:08 -08:00
cover_idx = ca . readMetadata ( style ) . getCoverPageIndexList ( ) [ 0 ]
image_data = ca . getPage ( cover_idx )
self . atprogdialog . setArchiveImage ( image_data )
self . atprogdialog . setTestImage ( None )
2013-01-21 16:19:59 -08:00
QtCore . QCoreApplication . processEvents ( )
2013-01-21 20:09:08 -08:00
if self . atprogdialog . isdone :
2013-01-21 16:19:59 -08:00
break
2013-01-21 20:09:08 -08:00
self . atprogdialog . progressBar . setValue ( prog_idx )
2013-01-21 16:19:59 -08:00
prog_idx + = 1
2013-01-21 20:09:08 -08:00
self . atprogdialog . label . setText ( ca . path )
2013-01-21 16:19:59 -08:00
QtCore . QCoreApplication . processEvents ( )
if ca . isWritable ( ) :
2013-01-22 17:25:17 -08:00
success , match_results = self . identifyAndTagSingleArchive ( ca , match_results , atstartdlg )
2013-01-21 16:19:59 -08:00
2013-01-21 20:09:08 -08:00
self . atprogdialog . close ( )
2013-01-21 16:19:59 -08:00
self . fileSelectionList . updateSelectedRows ( )
self . loadArchive ( self . fileSelectionList . getCurrentArchive ( ) )
2013-01-21 20:09:08 -08:00
self . atprogdialog = None
2013-01-24 22:17:45 -08:00
summary = u " "
summary + = u " Successfully tagged archives: {0} \n " . format ( len ( match_results . goodMatches ) )
2013-01-21 20:09:08 -08:00
if len ( match_results . multipleMatches ) > 0 :
2013-01-24 22:17:45 -08:00
summary + = u " Archives with multiple matches: {0} \n " . format ( len ( match_results . multipleMatches ) )
2013-01-21 20:09:08 -08:00
if len ( match_results . noMatches ) > 0 :
2013-01-24 22:17:45 -08:00
summary + = u " Archives with no matches: {0} \n " . format ( len ( match_results . noMatches ) )
2013-01-21 20:09:08 -08:00
if len ( match_results . fetchDataFailures ) > 0 :
2013-01-24 22:17:45 -08:00
summary + = u " Archives that failed due to data fetch errors: {0} \n " . format ( len ( match_results . fetchDataFailures ) )
2013-01-21 20:09:08 -08:00
if len ( match_results . writeFailures ) > 0 :
2013-01-24 22:17:45 -08:00
summary + = u " Archives that failed due to file writing errors: {0} \n " . format ( len ( match_results . writeFailures ) )
2013-01-21 20:09:08 -08:00
2013-01-24 22:17:45 -08:00
self . autoTagLog ( summary )
2013-01-21 20:09:08 -08:00
if len ( match_results . multipleMatches ) > 0 :
2013-01-24 22:17:45 -08:00
summary + = u " \n \n Do you want to manually select the ones with multiple matches now? "
2013-01-21 20:09:08 -08:00
reply = QtGui . QMessageBox . question ( self ,
2013-01-24 22:17:45 -08:00
self . tr ( u " Auto-Tag Summary " ) ,
2013-01-21 20:09:08 -08:00
self . tr ( summary ) ,
QtGui . QMessageBox . Yes , QtGui . QMessageBox . No )
if reply == QtGui . QMessageBox . Yes :
2013-01-22 21:25:50 -08:00
matchdlg = AutoTagMatchWindow ( self , match_results . multipleMatches , style , self . actualIssueDataFetch )
matchdlg . setModal ( True )
matchdlg . exec_ ( )
self . fileSelectionList . updateSelectedRows ( )
self . loadArchive ( self . fileSelectionList . getCurrentArchive ( ) )
2013-01-21 20:09:08 -08:00
else :
QtGui . QMessageBox . information ( self , self . tr ( " Auto-Tag Summary " ) , self . tr ( summary ) )
2013-01-21 16:19:59 -08:00
2013-01-20 12:43:48 -08:00
2012-11-04 21:04:30 -08:00
def dirtyFlagVerification ( self , title , desc ) :
if self . dirtyFlag :
reply = QtGui . QMessageBox . question ( self ,
self . tr ( title ) ,
self . tr ( desc ) ,
QtGui . QMessageBox . Yes , QtGui . QMessageBox . No )
if reply != QtGui . QMessageBox . Yes :
return False
return True
def closeEvent ( self , event ) :
if self . dirtyFlagVerification ( " Exit " + self . appName ,
" If you quit now, data in the form will be lost. Are you sure? " ) :
2012-11-20 13:20:26 -08:00
appsize = self . size ( )
self . settings . last_main_window_width = appsize . width ( )
self . settings . last_main_window_height = appsize . height ( )
2012-11-20 09:26:15 -08:00
self . settings . last_main_window_x = self . x ( )
self . settings . last_main_window_y = self . y ( )
2013-01-23 10:55:10 -08:00
self . settings . last_form_side_width = self . splitter . sizes ( ) [ 0 ]
self . settings . last_list_side_width = self . splitter . sizes ( ) [ 1 ]
2012-11-19 22:34:09 -08:00
self . settings . save ( )
2013-01-23 10:55:10 -08:00
2012-11-04 21:04:30 -08:00
event . accept ( )
else :
event . ignore ( )
2012-11-04 11:24:48 -08:00
2012-11-14 17:25:01 -08:00
def showPageBrowser ( self ) :
if self . page_browser is None :
2012-12-08 11:57:51 -08:00
self . page_browser = PageBrowserWindow ( self , self . metadata )
2012-11-14 17:25:01 -08:00
if self . comic_archive is not None :
self . page_browser . setComicArchive ( self . comic_archive )
self . page_browser . finished . connect ( self . pageBrowserClosed )
def pageBrowserClosed ( self ) :
self . page_browser = None
2012-11-19 20:04:58 -08:00
def viewRawCRTags ( self ) :
if self . comic_archive is not None and self . comic_archive . hasCIX ( ) :
dlg = LogWindow ( self )
dlg . setText ( self . comic_archive . readRawCIX ( ) )
dlg . setWindowTitle ( " Raw ComicRack Tag View " )
dlg . exec_ ( )
def viewRawCBLTags ( self ) :
if self . comic_archive is not None and self . comic_archive . hasCBI ( ) :
dlg = LogWindow ( self )
text = pprint . pformat ( json . loads ( self . comic_archive . readRawCBI ( ) ) , indent = 4 )
dlg . setText ( text )
dlg . setWindowTitle ( " Raw ComicBookLover Tag View " )
dlg . exec_ ( )
2012-12-04 23:34:53 -08:00
def showWiki ( self ) :
webbrowser . open ( " http://code.google.com/p/comictagger/wiki/Home?tm=6 " )
def reportBug ( self ) :
webbrowser . open ( " http://code.google.com/p/comictagger/issues/list " )
2012-11-19 20:04:58 -08:00
2012-12-04 23:34:53 -08:00
def showForum ( self ) :
webbrowser . open ( " http://comictagger.forumotion.com/ " )
2012-12-09 22:39:54 -08:00
def frontCoverChanged ( self , int ) :
self . metadata . pages = self . pageListEditor . getPageList ( )
self . updateCoverImage ( )
2012-11-20 09:26:15 -08:00
2012-12-09 22:39:54 -08:00
def pageListOrderChanged ( self ) :
self . metadata . pages = self . pageListEditor . getPageList ( )
2012-12-18 15:14:00 -08:00
def applyCBLTransform ( self ) :
self . formToMetadata ( )
self . metadata = CBLTransformer ( self . metadata , self . settings ) . apply ( )
self . metadataToForm ( )
def renameArchive ( self ) :
2013-01-23 10:55:10 -08:00
ca_list = self . fileSelectionList . getSelectedArchiveList ( )
if len ( ca_list ) == 0 :
QtGui . QMessageBox . information ( self , self . tr ( " Rename " ) , self . tr ( " No archives selected! " ) )
return
if self . dirtyFlagVerification ( " File Rename " ,
" If you rename files now, unsaved data in the form will be lost. Are you sure? " ) :
dlg = RenameWindow ( self , ca_list , self . load_data_style , self . settings )
dlg . setModal ( True )
if dlg . exec_ ( ) :
self . fileSelectionList . updateSelectedRows ( )
self . loadArchive ( self . comic_archive )
2013-01-18 19:12:25 -08:00
2013-01-18 13:12:23 -08:00
2012-12-18 17:37:55 -08:00
2013-01-16 14:46:22 -08:00
def fileListSelectionChanged ( self , qvarFI ) :
fi = qvarFI . toPyObject ( )
2013-01-18 13:12:23 -08:00
self . loadArchive ( fi . ca )
def loadArchive ( self , comic_archive ) :
2013-01-16 14:46:22 -08:00
self . comic_archive = None
self . clearForm ( )
2012-12-18 17:37:55 -08:00
2013-01-20 00:36:21 -08:00
self . settings . last_opened_folder = os . path . abspath ( os . path . split ( comic_archive . path ) [ 0 ] )
2013-01-18 13:12:23 -08:00
self . comic_archive = comic_archive
self . metadata = self . comic_archive . readMetadata ( self . load_data_style )
2013-01-16 14:46:22 -08:00
if self . metadata is None :
self . metadata = GenericMetadata ( )
2013-01-18 13:12:23 -08:00
self . actualLoadCurrentArchive ( )
2013-01-16 22:19:06 -08:00
def fileListCleared ( self ) :
self . resetApp ( )