diff --git a/comicarchive.py b/comicarchive.py
index c44c329..2b55cd6 100644
--- a/comicarchive.py
+++ b/comicarchive.py
@@ -419,8 +419,8 @@ class ComicArchive:
self.path = path
self.ci_xml_filename = 'ComicInfo.xml'
self.comet_default_filename = 'CoMet.xml'
- self.comet_filename = None
-
+ self.resetCache()
+
if self.zipTest():
self.archive_type = self.ArchiveType.Zip
self.archiver = ZipArchiver( self.path )
@@ -436,6 +436,14 @@ class ComicArchive:
self.archive_type = self.ArchiveType.Unknown
self.archiver = UnknownArchiver( self.path )
+ # Clears the cached data
+ def resetCache( self ):
+ self.has_cix = None
+ self.has_cbi = None
+ self.comet_filename = None
+ self.page_count = None
+ self.page_list = None
+
def setExternalRarProgram( self, rar_exe_path ):
if self.isRar():
self.archiver.rar_exe_path = rar_exe_path
@@ -512,12 +520,16 @@ class ComicArchive:
def writeMetadata( self, metadata, style ):
+ retcode = None
if style == MetaDataStyle.CIX:
- return self.writeCIX( metadata )
+ retcode = self.writeCIX( metadata )
elif style == MetaDataStyle.CBI:
- return self.writeCBI( metadata )
+ retcode = self.writeCBI( metadata )
elif style == MetaDataStyle.COMET:
- return self.writeCoMet( metadata )
+ retcode = self.writeCoMet( metadata )
+ self.resetCache()
+ return retcode
+
def hasMetadata( self, style ):
@@ -561,29 +573,32 @@ class ComicArchive:
def getPageNameList( self , sort_list=True):
- # get the list file names in the archive, and sort
- files = self.archiver.getArchiveFilenameList()
-
- # seems like some archive creators are on Windows, and don't know about case-sensitivity!
- if sort_list:
- files.sort(key=lambda x: x.lower())
-
- # make a sub-list of image files
- page_list = []
- for name in files:
- if ( name[-4:].lower() in [ ".jpg", "jpeg", ".png" ] and os.path.basename(name)[0] != "." ):
- page_list.append(name)
+ if self.page_list is None:
+ # get the list file names in the archive, and sort
+ files = self.archiver.getArchiveFilenameList()
+
+ # seems like some archive creators are on Windows, and don't know about case-sensitivity!
+ if sort_list:
+ files.sort(key=lambda x: x.lower())
+
+ # make a sub-list of image files
+ self.page_list = []
+ for name in files:
+ if ( name[-4:].lower() in [ ".jpg", "jpeg", ".png" ] and os.path.basename(name)[0] != "." ):
+ self.page_list.append(name)
- return page_list
+ return self.page_list
def getNumberOfPages( self ):
- return len( self.getPageNameList( sort_list=False ) )
+ if self.page_count is None:
+ self.page_count = len( self.getPageNameList( ) )
+ return self.page_count
def readCBI( self ):
raw_cbi = self.readRawCBI()
if raw_cbi is None:
- md =GenericMetadata()
+ md = GenericMetadata()
else:
md = ComicBookInfo().metadataFromString( raw_cbi )
@@ -598,12 +613,16 @@ class ComicArchive:
return self.archiver.getArchiveComment()
def hasCBI(self):
- #if ( not ( self.isZip() or self.isRar()) or not self.seemsToBeAComicArchive() ):
- if not self.seemsToBeAComicArchive():
- return False
+ if self.has_cbi is None:
- comment = self.archiver.getArchiveComment()
- return ComicBookInfo().validateString( comment )
+ #if ( not ( self.isZip() or self.isRar()) or not self.seemsToBeAComicArchive() ):
+ if not self.seemsToBeAComicArchive():
+ self.has_cbi = False
+ else:
+ comment = self.archiver.getArchiveComment()
+ self.has_cbi = ComicBookInfo().validateString( comment )
+
+ return self.has_cbi
def writeCBI( self, metadata ):
self.applyArchiveInfoToMetadata( metadata )
@@ -633,7 +652,6 @@ class ComicArchive:
def readRawCIX( self ):
if not self.hasCIX():
- print self.path, "doesn't have ComicInfo.xml data!"
return None
return self.archiver.readArchiveFile( self.ci_xml_filename )
@@ -652,12 +670,15 @@ class ComicArchive:
return self.archiver.removeArchiveFile( self.ci_xml_filename )
def hasCIX(self):
- if not self.seemsToBeAComicArchive():
- return False
- elif self.ci_xml_filename in self.archiver.getArchiveFilenameList():
- return True
- else:
- return False
+ if self.has_cix is None:
+
+ if not self.seemsToBeAComicArchive():
+ self.has_cix = False
+ elif self.ci_xml_filename in self.archiver.getArchiveFilenameList():
+ self.has_cix = True
+ else:
+ self.has_cix = False
+ return self.has_cix
def readCoMet( self ):
diff --git a/fileselectionlist.py b/fileselectionlist.py
new file mode 100644
index 0000000..af80e74
--- /dev/null
+++ b/fileselectionlist.py
@@ -0,0 +1,217 @@
+# coding=utf-8
+"""
+A PyQt4 widget for managing list of files
+"""
+
+"""
+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.
+"""
+
+import os
+
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+from PyQt4 import uic
+from PyQt4.QtCore import pyqtSignal
+
+from settings import ComicTaggerSettings
+from comicarchive import ComicArchive
+from genericmetadata import GenericMetadata, PageType
+from options import MetaDataStyle
+
+class FileTableWidget( QTableWidget ):
+
+ def __init__(self, parent ):
+ super(FileTableWidget, self).__init__(parent)
+
+
+ self.setColumnCount(5)
+ self.setHorizontalHeaderLabels (["File", "Folder", "CR", "CBL", ""])
+ self.horizontalHeader().setStretchLastSection( True )
+
+
+class FileTableWidgetItem(QTableWidgetItem):
+ def __lt__(self, other):
+ return (self.data(Qt.UserRole).toBool() <
+ other.data(Qt.UserRole).toBool())
+
+
+class FileInfo( ):
+ def __init__(self, path, ca, cix_md, cbi_md ):
+ self.path = path
+ self.cix_md = cix_md
+ self.cbi_md = cbi_md
+ self.ca = ca
+
+class FileSelectionList(QWidget):
+
+ selectionChanged = pyqtSignal(QVariant)
+
+ def __init__(self, parent , settings ):
+ super(FileSelectionList, self).__init__(parent)
+
+ uic.loadUi(os.path.join(ComicTaggerSettings.baseDir(), 'fileselectionlist.ui' ), self)
+
+ self.settings = settings
+ #self.twList = FileTableWidget( self )
+ #gridlayout = QGridLayout( self )
+ #gridlayout.addWidget( self.twList )
+ self.setAcceptDrops(True)
+
+ self.twList.itemSelectionChanged.connect( self.itemSelectionChangedCB )
+
+ def dragEnterEvent(self, event):
+ self.droppedFiles = None
+ if event.mimeData().hasUrls():
+
+ # 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()
+
+ def dropEvent(self, event):
+ self.addPathList( self.droppedFiles)
+ event.accept()
+
+ def addPathList( self, pathlist ):
+ filelist = []
+ for p in pathlist:
+ # if path is a folder, walk it recursivly, and all files underneath
+ if os.path.isdir( unicode(p)):
+ for root,dirs,files in os.walk( unicode(p) ):
+ for f in files:
+ filelist.append(os.path.join(root,unicode(f)))
+ else:
+ filelist.append(unicode(p))
+
+ # we now have a list of files to add
+
+ progdialog = QProgressDialog("", "Cancel", 0, len(filelist), self)
+ progdialog.setWindowTitle( "Adding Files" )
+ progdialog.setWindowModality(Qt.WindowModal)
+
+ self.twList.setSortingEnabled(False)
+ for idx,f in enumerate(filelist):
+ QCoreApplication.processEvents()
+ if progdialog.wasCanceled():
+ break
+ progdialog.setValue(idx)
+ self.addPathItem( f )
+
+ progdialog.close()
+ self.twList.setSortingEnabled(True)
+
+ #Maybe set a max size??
+ self.twList.resizeColumnsToContents()
+
+
+ def isListDupe( self, path ):
+ r = 0
+ while r < self.twList.rowCount():
+ fi = self.twList.item(r, 0).data( Qt.UserRole ).toPyObject()
+ if fi.path == path:
+ return True
+ r = r + 1
+
+ return False
+
+ def addPathItem( self, path):
+ path = unicode( path )
+ #print "processing", path
+
+ if self.isListDupe(path):
+ return
+
+ ca = ComicArchive( path )
+ if self.settings.rar_exe_path != "":
+ ca.setExternalRarProgram( self.settings.rar_exe_path )
+
+ if ca.seemsToBeAComicArchive() :
+
+ row = self.twList.rowCount()
+ self.twList.insertRow( row )
+
+ cix_md = None
+ cbi_md = None
+
+ has_cix = ca.hasCIX()
+ if has_cix:
+ cix_md = ca.readCIX()
+
+ has_cbi = ca.hasCBI()
+ if has_cbi:
+ cbi_md = ca.readCBI()
+
+ fi = FileInfo( path, ca, cix_md, cbi_md )
+
+ item_text = os.path.split(path)[1]
+ item = QTableWidgetItem(item_text)
+ item.setFlags(Qt.ItemIsSelectable| Qt.ItemIsEnabled)
+ item.setData( Qt.UserRole , fi )
+ item.setData( Qt.ToolTipRole ,item_text)
+ self.twList.setItem(row, 0, item)
+
+ item_text = os.path.split(path)[0]
+ item = QTableWidgetItem(item_text)
+ item.setFlags(Qt.ItemIsSelectable| Qt.ItemIsEnabled)
+ item.setData( Qt.ToolTipRole ,item_text)
+ self.twList.setItem(row, 1, item)
+
+ # Attempt to use a special checkbox widget in the cell.
+ # Couldn't figure out how to disable it with "enabled" colors
+ #w = QWidget()
+ #cb = QCheckBox(w)
+ #cb.setCheckState(Qt.Checked)
+ #layout = QHBoxLayout()
+ #layout.addWidget( cb )
+ #layout.setAlignment(Qt.AlignHCenter)
+ #layout.setMargin(2)
+ #w.setLayout(layout)
+ #self.twList.setCellWidget( row, 2, w )
+
+ item = FileTableWidgetItem()
+ item.setFlags(Qt.ItemIsSelectable| Qt.ItemIsEnabled)
+ item.setTextAlignment(Qt.AlignHCenter)
+ if has_cix:
+ item.setCheckState(Qt.Checked)
+ item.setData(Qt.UserRole, True)
+ else:
+ item.setData(Qt.UserRole, False)
+ self.twList.setItem(row, 2, item)
+
+ item = FileTableWidgetItem()
+ item.setFlags(Qt.ItemIsSelectable| Qt.ItemIsEnabled)
+ item.setTextAlignment(Qt.AlignHCenter)
+ if has_cbi:
+ item.setCheckState(Qt.Checked)
+ item.setData(Qt.UserRole, True)
+ else:
+ item.setData(Qt.UserRole, False)
+ self.twList.setItem(row, 3, item)
+
+ def itemSelectionChangedCB( self ):
+ idx = self.twList.currentRow()
+
+ fi = self.twList.item(idx, 0).data( Qt.UserRole ).toPyObject()
+
+ #if fi.cix_md is not None:
+ # print u"{0}".format(fi.cix_md)
+
+ self.selectionChanged.emit( QVariant(fi))
diff --git a/fileselectionlist.ui b/fileselectionlist.ui
new file mode 100644
index 0000000..8bba869
--- /dev/null
+++ b/fileselectionlist.ui
@@ -0,0 +1,69 @@
+
+
+ pageListEditor
+
+
+
+ 0
+ 0
+ 527
+ 323
+
+
+
+ Form
+
+
+ -
+
+
+ true
+
+
+ QAbstractItemView::SelectRows
+
+
+ 61
+
+
+ 36
+
+
+ false
+
+
+
+ File
+
+
+
+
+ Path
+
+
+
+
+ CR
+
+
+
+
+
+ AlignHCenter|AlignVCenter|AlignCenter
+
+
+
+
+ CBL
+
+
+ AlignHCenter|AlignVCenter|AlignCenter
+
+
+
+
+
+
+
+
+
diff --git a/pagelisteditor.py b/pagelisteditor.py
index 47c317c..8e477b0 100644
--- a/pagelisteditor.py
+++ b/pagelisteditor.py
@@ -27,6 +27,7 @@ from PyQt4 import uic
from settings import ComicTaggerSettings
from genericmetadata import GenericMetadata, PageType
from options import MetaDataStyle
+from pageloader import PageLoader
def itemMoveEvents( widget ):
@@ -79,6 +80,7 @@ class PageListEditor(QWidget):
self.comic_archive = None
self.pages_list = None
+ self.page_loader = None
self.current_pixmap = QPixmap(os.path.join(ComicTaggerSettings.baseDir(), 'graphics/nocover.png' ))
self.setDisplayPixmap( 0, 0)
@@ -151,17 +153,19 @@ class PageListEditor(QWidget):
#idx = int(str (self.listWidget.item( row ).text()))
idx = int(self.listWidget.item( row ).data(Qt.UserRole).toPyObject()[0]['Image'])
-
+
+ if self.page_loader is not None:
+ self.page_loader.abandoned = True
+
if self.comic_archive is not None:
- image_data = self.comic_archive.getPage( idx )
- else:
- image_data = None
-
- if image_data is not None:
- img = QImage()
- img.loadFromData( image_data )
- self.current_pixmap = QPixmap(QPixmap(img))
- self.setDisplayPixmap( 0, 0)
+ self.page_loader = PageLoader( self.comic_archive, idx )
+ self.page_loader.loadComplete.connect( self.actualChangePageImage )
+ self.page_loader.start()
+
+ def actualChangePageImage( self, img ):
+ self.page_loader = None
+ self.current_pixmap = QPixmap(img)
+ self.setDisplayPixmap( 0, 0)
def getFirstFrontCover( self ):
frontCover = 0
diff --git a/pageloader.py b/pageloader.py
new file mode 100644
index 0000000..b7cc184
--- /dev/null
+++ b/pageloader.py
@@ -0,0 +1,77 @@
+"""
+A PyQT4 class to load a page image from a ComicArchive in a background thread
+"""
+
+"""
+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 PyQt4.QtCore import pyqtSignal
+
+from comicarchive import ComicArchive
+
+"""
+This class holds onto a reference of each instance in a list
+since problems occur if the ref count goes to zero and the GC
+tries to reap the object while the thread is going.
+
+If the client class wants to stop the thread, they should mark
+it as "abandoned", and no signals will be issued
+"""
+
+class PageLoader( QtCore.QThread ):
+
+ loadComplete = pyqtSignal( QtGui.QImage )
+
+ instanceList = []
+ mutex = QtCore.QMutex()
+
+ """
+ Remove all finished threads from the list
+ """
+ @staticmethod
+ def reapInstances():
+ for obj in reversed(PageLoader.instanceList ):
+ if obj.isFinished():
+ PageLoader.instanceList.remove(obj)
+
+ def __init__(self, ca, page_num ):
+ QtCore.QThread.__init__(self)
+ self.ca = ca
+ self.page_num = page_num
+ self.abandoned = False
+
+ # remove any old instances, and then add ourself
+ PageLoader.mutex.lock()
+ PageLoader.reapInstances()
+ PageLoader.instanceList.append( self )
+ PageLoader.mutex.unlock()
+
+ def run(self):
+ image_data = self.ca.getPage( self.page_num )
+ if self.abandoned:
+ return
+
+ if image_data is not None:
+ img = QtGui.QImage()
+ img.loadFromData( image_data )
+
+ if self.abandoned:
+ return
+
+ self.loadComplete.emit( img )
+
+
\ No newline at end of file
diff --git a/taggerwindow.py b/taggerwindow.py
index 8311e51..f34310f 100644
--- a/taggerwindow.py
+++ b/taggerwindow.py
@@ -44,8 +44,10 @@ from filenameparser import FileNameParser
from logwindow import LogWindow
from optionalmsgdialog import OptionalMessageDialog
from pagelisteditor import PageListEditor
+from fileselectionlist import FileSelectionList
from cbltransformer import CBLTransformer
from renamewindow import RenameWindow
+from pageloader import PageLoader
import utils
import ctversion
@@ -91,18 +93,32 @@ class TaggerWindow( QtGui.QMainWindow):
#signal.signal(signal.SIGINT, self.sigint_handler)
uic.loadUi(os.path.join(ComicTaggerSettings.baseDir(), 'taggerwindow.ui' ), self)
+ self.settings = settings
self.pageListEditor = PageListEditor( self.tabPages )
gridlayout = QtGui.QGridLayout( self.tabPages )
gridlayout.addWidget( self.pageListEditor )
+ #---------------------------
+ self.fileSelectionList = FileSelectionList( self.widgetListHolder, self.settings )
+ gridlayout = QtGui.QGridLayout( self.widgetListHolder )
+ gridlayout.addWidget( self.fileSelectionList )
+
+ self.fileSelectionList.selectionChanged.connect( self.fileListSelectionChanged )
+ # ATB: Disable the list for now...
+ self.splitter.setSizes([100,0])
+ self.splitter.setHandleWidth(0)
+ self.splitter.handle(1).setDisabled(True)
+
+ #---------------------------
+
+
self.setWindowIcon(QtGui.QIcon(os.path.join(ComicTaggerSettings.baseDir(), 'graphics/app.png' )))
self.lblCover.setPixmap(QtGui.QPixmap(os.path.join(ComicTaggerSettings.baseDir(), 'graphics/nocover.png' )))
#print platform.system(), platform.release()
self.dirtyFlag = False
- self.settings = settings
self.data_style = settings.last_selected_data_style
#set up a default metadata object
@@ -117,6 +133,7 @@ class TaggerWindow( QtGui.QMainWindow):
self.droppedFile = None
self.page_browser = None
+ self.page_loader = None
self.populateComboBoxes()
@@ -362,7 +379,6 @@ class TaggerWindow( QtGui.QMainWindow):
msgBox.setStandardButtons( QtGui.QMessageBox.Ok )
msgBox.exec_()
-
def dragEnterEvent(self, event):
self.droppedFile=None
if event.mimeData().hasUrls():
@@ -371,12 +387,12 @@ class TaggerWindow( QtGui.QMainWindow):
if url.scheme()=="file":
self.droppedFile=url.toLocalFile()
event.accept()
-
+
def dropEvent(self, event):
if self.dirtyFlagVerification( "Open Archive",
"If you open a new archive now, data in the form will be lost. Are you sure?"):
self.openArchive( unicode(self.droppedFile))
-
+
def openArchive( self, path, explicit_style=None, clear_form=True ):
if path is None or path == "":
@@ -431,35 +447,44 @@ class TaggerWindow( QtGui.QMainWindow):
else:
return
- if self.metadata.isEmpty:
- self.metadata = self.comic_archive.metadataFromFilename( )
- 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()
- #self.updatePagesInfo()
+ self.loadCurrentArchive()
else:
QtGui.QMessageBox.information(self, self.tr("Whoops!"), self.tr("That file doesn't appear to be a comic archive!"))
- def updateCoverImage( self ):
- cover_idx = self.metadata.getCoverPageIndexList()[0]
- image_data = self.comic_archive.getPage( cover_idx )
- if not image_data is None:
- img = QtGui.QImage()
- img.loadFromData( image_data )
- self.lblCover.setPixmap(QtGui.QPixmap(img))
- self.lblCover.setScaledContents(True)
+ def loadCurrentArchive( self ):
+ if self.metadata.isEmpty:
+ self.metadata = self.comic_archive.metadataFromFilename( )
+ 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()
+
+ def updateCoverImage( self ):
+ if self.page_loader is not None:
+ self.page_loader.abandoned = True
+
+ cover_idx = self.metadata.getCoverPageIndexList()[0]
+
+ self.page_loader = PageLoader( self.comic_archive, cover_idx )
+ self.page_loader.loadComplete.connect( self.actualUpdateCoverImage )
+ self.page_loader.start()
+
+ def actualUpdateCoverImage( self, img ):
+ self.page_loader = None
+ self.lblCover.setPixmap(QtGui.QPixmap(img))
+ self.lblCover.setScaledContents(True)
+
+
def updateMenus( self ):
# First just disable all the questionable items
@@ -1389,5 +1414,19 @@ class TaggerWindow( QtGui.QMainWindow):
self.comic_archive = None
self.openArchive( dlg.new_name )
+ def fileListSelectionChanged( self, qvarFI ):
+ fi = qvarFI.toPyObject()
+ #if fi.cix_md is not None:
+ # print u"{0}".format(fi.cix_md)
+ self.comic_archive = None
+ self.clearForm()
-
\ No newline at end of file
+ self.comic_archive = fi.ca
+ if self.data_style == MetaDataStyle.CIX:
+ self.metadata = fi.cix_md
+ else:
+ self.metadata = fi.cbi_md
+ if self.metadata is None:
+ self.metadata = GenericMetadata()
+
+ self.loadCurrentArchive()
diff --git a/taggerwindow.ui b/taggerwindow.ui
index df4b273..50beaf1 100644
--- a/taggerwindow.ui
+++ b/taggerwindow.ui
@@ -6,8 +6,8 @@
0
0
- 959
- 541
+ 968
+ 547
@@ -16,6 +16,9 @@
0
+
+ false
+
ComicTagger
@@ -29,353 +32,665 @@
0
-
- -
-
-
-
-
+
+ true
+
+
+
-
+
+
+ true
+
+
+ true
+
+
+ Qt::Horizontal
+
+
+
-
-
-
- QFormLayout::AllNonFixedFieldsGrow
-
-
- Qt::AlignHCenter|Qt::AlignTop
-
-
-
-
-
- Tag Style:
+
+
-
+
+
+ QFormLayout::AllNonFixedFieldsGrow
-
+
+ Qt::AlignHCenter|Qt::AlignTop
+
+ -
+
+
+ Tag Style:
+
+
+
+ -
+
+
+
- -
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 220
- 0
-
-
-
-
- 220
- 16777215
-
-
-
- QFrame::StyledPanel
-
-
- QFrame::Raised
-
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 75
- true
-
-
-
-
-
-
- true
-
-
-
- -
-
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 220
+ 0
+
+
+
+
+ 220
+ 16777215
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
-
-
+
-
+
0
0
-
-
- 200
- 16777215
-
-
- false
+ 75
+ true
-
- QFrame::NoFrame
-
-
- QFrame::Sunken
-
- false
+ true
-
-
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 200
+ 16777215
+
+
+
+
+ false
+
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Sunken
+
+
+
+
+
+ false
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+ -
+
-
+
0
0
-
-
- true
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
-
-
-
-
-
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 220
+ 330
+
+
+
+
+ 220
+ 330
+
+
+
+ QFrame::Panel
+
+
+ QFrame::Sunken
+
+
+
+
+
+ true
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 20
+
+
+
+
+
-
-
-
-
- 0
- 0
-
+
+
+ QLayout::SetDefaultConstraint
-
-
- 220
- 330
-
-
-
-
- 220
- 330
-
-
-
- QFrame::Panel
-
-
- QFrame::Sunken
-
-
-
-
-
- true
-
-
- Qt::AlignCenter
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 20
-
-
-
-
-
-
- -
-
-
- QLayout::SetDefaultConstraint
-
-
-
-
-
- true
-
-
- 0
-
-
-
- Details
-
-
-
-
-
-
- Qt::ScrollBarAsNeeded
-
-
- Qt::ScrollBarAsNeeded
-
-
- false
-
-
-
- true
-
-
-
- 0
- 0
- 685
- 384
-
-
-
-
-
- 10
- 10
- 361
- 341
-
+
-
+
+
+ true
+
+
+ true
+
+
+ 0
+
+
+
+ Details
+
+
+
-
+
+
+ true
-
-
- 4
+
+ Qt::ScrollBarAsNeeded
+
+
+ Qt::ScrollBarAsNeeded
+
+
+ false
+
+
+
+ true
-
-
-
-
- QLayout::SetNoConstraint
+
+
+ 0
+ 0
+ 685
+ 384
+
+
+
+ true
+
+
+
+
+ 10
+ 10
+ 361
+ 341
+
+
+
+
+ 4
-
- QFormLayout::AllNonFixedFieldsGrow
+
-
+
+
+ QLayout::SetNoConstraint
+
+
+ QFormLayout::AllNonFixedFieldsGrow
+
+
-
+
+
+ Series
+
+
+
+ -
+
+
+ -
+
+
+ Title
+
+
+
+ -
+
+
+ -
+
+
+ Publisher
+
+
+
+ -
+
+
+ -
+
+
+ Imprint
+
+
+
+ -
+
+
+ -
+
+
+
+ 85
+ 0
+
+
+
+ Series Group
+
+
+
+ -
+
+
+ -
+
+
+ Story Arc
+
+
+
+ -
+
+
+ -
+
+
+ Genre
+
+
+
+ -
+
+
+ -
+
+
+ Alt. Series
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Maximum
+
+
+
+ 20
+ 10
+
+
+
+
+
+
+ -
+
+
+ 6
+
+
+ 4
+
+
-
+
+
+ QFormLayout::AllNonFixedFieldsGrow
+
+
-
+
+
+
+ 85
+ 0
+
+
+
+ Alt. Issue
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Alt. # Issues
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+ 380
+ 10
+ 301
+ 341
+
+
+
+
+ 6
- -
-
-
- Series
+
+ 3
+
+
-
+
+
-
+
+
+ QFormLayout::AllNonFixedFieldsGrow
+
+
-
+
+
+ Issue
+
+
+
+ -
+
+
+ -
+
+
+ Volume
+
+
+
+ -
+
+
+ -
+
+
+ Year
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 16777215
+
+
+
+ Qt::ImhDigitsOnly
+
+
+
+
+
+ -
+
+
+ QFormLayout::AllNonFixedFieldsGrow
+
+
-
+
+
+ # Issues
+
+
+
+ -
+
+
+ Qt::ImhDigitsOnly
+
+
+
+ -
+
+
+ # Volumes
+
+
+
+ -
+
+
+ Qt::ImhDigitsOnly
+
+
+
+ -
+
+
+ Month
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 16777215
+
+
+
+ Qt::ImhDigitsOnly
+
+
+
+
+
+
+
+ -
+
+
+ QFormLayout::AllNonFixedFieldsGrow
-
-
- -
-
-
- -
-
-
- Title
+
+ 6
-
+
-
+
+
+ Maturity Rating
+
+
+
+ -
+
+
+ false
+
+
+ true
+
+
+
+ -
+
+
+ Manga
+
+
+
+ -
+
+
+ false
+
+
+
+ -
+
+
+ Format
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ true
+
+
+
+ -
+
+
+ Black & White
+
+
+
+ -
+
+
+ Qt::LeftToRight
+
+
+ false
+
+
+
+
+
+
+
- -
-
-
- -
-
-
- Publisher
-
-
-
- -
-
-
- -
-
-
- Imprint
-
-
-
- -
-
-
- -
-
-
-
- 85
- 0
-
-
-
- Series Group
-
-
-
- -
-
-
- -
-
-
- Story Arc
-
-
-
- -
-
-
- -
-
-
- Genre
-
-
-
- -
-
-
- -
-
-
- Alt. Series
-
-
-
- -
-
-
- -
-
+
-
+
Qt::Vertical
@@ -390,654 +705,372 @@
-
-
- -
-
-
- 6
-
-
- 4
-
-
-
+
QFormLayout::AllNonFixedFieldsGrow
-
-
-
-
-
- 85
- 0
-
-
-
- Alt. Issue
-
-
-
-
-
+
-
+
0
0
-
-
- -
-
-
-
-
-
- Alt. # Issues
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
- 380
- 10
- 301
- 341
-
-
-
-
- 6
-
-
- 3
-
- -
-
-
-
-
-
- QFormLayout::AllNonFixedFieldsGrow
-
-
-
-
-
- Issue
-
-
-
- -
-
-
- -
-
-
- Volume
-
-
-
- -
-
-
- -
-
-
- Year
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 50
- 16777215
-
-
-
- Qt::ImhDigitsOnly
-
-
-
-
-
- -
-
-
- QFormLayout::AllNonFixedFieldsGrow
-
-
-
-
-
- # Issues
-
-
-
- -
-
-
- Qt::ImhDigitsOnly
-
-
-
-
-
+
- # Volumes
+ Country
-
-
-
- Qt::ImhDigitsOnly
-
-
-
- -
-
-
- Month
-
-
-
- -
-
+
-
+
0
0
-
-
- 50
- 16777215
-
-
-
- Qt::ImhDigitsOnly
+
+
+ -
+
+
+ Language
-
- -
-
-
- QFormLayout::AllNonFixedFieldsGrow
-
-
- 6
-
-
-
-
-
- Maturity Rating
-
-
-
- -
-
-
- false
-
-
- true
-
-
-
- -
-
-
- Manga
-
-
-
- -
-
-
- false
-
-
-
- -
-
-
- Format
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- true
-
-
-
- -
-
-
- Black & White
-
-
-
- -
-
-
- Qt::LeftToRight
-
-
- false
-
-
-
-
-
-
-
-
- -
-
-
- Qt::Vertical
-
-
- QSizePolicy::Maximum
-
-
-
- 20
- 10
-
-
-
-
- -
-
-
- QFormLayout::AllNonFixedFieldsGrow
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- -
-
-
- Country
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- -
-
-
- Language
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Credits
-
-
- -
-
-
-
-
-
-
-
-
- QAbstractItemView::SingleSelection
-
-
- QAbstractItemView::SelectRows
-
-
- 0
-
-
- 3
-
-
- 2
-
-
- true
-
-
- false
-
-
-
- Primary
-
-
- AlignHCenter|AlignVCenter|AlignCenter
-
-
-
-
- Credit
-
-
-
-
- Name
-
-
+
+
+
+
+
+
+
+ Credits
+
+
+ -
+
+
-
+
+
-
+
+
+ QAbstractItemView::SingleSelection
+
+
+ QAbstractItemView::SelectRows
+
+
+ 0
+
+
+ 3
+
+
+ 2
+
+
+ true
+
+
+ false
+
+
+
+ Primary
+
+
+ AlignHCenter|AlignVCenter|AlignCenter
+
+
+
+
+ Credit
+
+
+
+
+ Name
+
+
+
+
+ -
+
+
+ QFormLayout::ExpandingFieldsGrow
+
+
-
+
+
+ Scan Info
+
+
+
+ -
+
+
+
+
+
-
-
-
- QFormLayout::ExpandingFieldsGrow
-
-
-
-
+
+
-
+
- Scan Info
+ Add Credit
- -
-
+
-
+
+
+ Remove Credit
+
+
+
+ -
+
+
+ Edit Credit
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
- -
-
-
-
-
+
+
+
+
+ Notes
+
+
+ -
+
+
+ QFormLayout::ExpandingFieldsGrow
+
+
-
+
- Add Credit
+ Comments
- -
-
+
-
+
+
+ -
+
- Remove Credit
+ Notes
- -
-
+
-
+
+
+ -
+
- Edit Credit
+ Web
- -
-
-
- Qt::Vertical
+
-
+
+
+ -
+
+
+ User Rating
-
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
- 20
- 40
+ 80
+ 16777215
-
+
-
-
-
-
-
- Notes
-
-
- -
-
-
- QFormLayout::ExpandingFieldsGrow
-
+
+
+
+ Other
+
+
-
-
-
- Comments
+
+
+ QFormLayout::ExpandingFieldsGrow
-
-
- -
-
-
- -
-
-
- Notes
-
-
-
- -
-
-
- -
-
-
- Web
-
-
-
- -
-
-
- -
-
-
- User Rating
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 80
- 16777215
-
-
-
+
-
+
+
+ Characters
+
+
+
+ -
+
+
+ true
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+
+ -
+
+
+ Teams
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+
+ -
+
+
+ Locations
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+
+ -
+
+
+ Other Tags
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+
+ 0
+ 100
+
+
+
+
+
-
-
-
-
-
- Other
-
-
- -
-
-
- QFormLayout::ExpandingFieldsGrow
-
-
-
-
-
- Characters
-
-
-
- -
-
-
- true
-
-
-
- 0
- 0
-
-
-
-
- 0
- 0
-
-
-
-
- 16777215
- 16777215
-
-
-
-
- -
-
-
- Teams
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 16777215
- 16777215
-
-
-
-
- -
-
-
- Locations
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 16777215
- 16777215
-
-
-
-
- -
-
-
- Other Tags
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 16777215
- 16777215
-
-
-
-
- 0
- 100
-
-
-
-
-
-
-
-
-
-
- Pages
-
-
-
+
+
+
+ Pages
+
+
+
+
+
-
-
+
+
+
+ false
+
+
+
@@ -1046,8 +1079,8 @@
0
0
- 959
- 25
+ 968
+ 28