Krita Source Code Documentation
Loading...
Searching...
No Matches
comics_metadata_dialog.py
Go to the documentation of this file.
1"""
2SPDX-FileCopyrightText: 2017 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
3
4This file is part of the Comics Project Management Tools(CPMT).
5
6SPDX-License-Identifier: GPL-3.0-or-later
7"""
8
9"""
10This is a metadata editor that helps out setting the proper metadata
11"""
12import os # For finding the script location.
13import csv
14import re
15from pathlib import Path # For reading all the files in a directory.
16try:
17 from PyQt6.QtGui import QStandardItem, QStandardItemModel, QImage, QIcon, QPixmap, QPainter, QPalette, QFontDatabase
18 from PyQt6.QtWidgets import QComboBox, QCompleter, QStyledItemDelegate, QLineEdit, QDialog, QDialogButtonBox, QVBoxLayout, QFormLayout, QTabWidget, QWidget, QPlainTextEdit, QHBoxLayout, QSpinBox, QDateEdit, QPushButton, QLabel, QTableView
19 from PyQt6.QtCore import QDir, QLocale, QStringListModel, Qt, QDate, QSize, QUuid, QLibraryInfo
20except:
21 from PyQt5.QtGui import QStandardItem, QStandardItemModel, QImage, QIcon, QPixmap, QPainter, QPalette, QFontDatabase
22 from PyQt5.QtWidgets import QComboBox, QCompleter, QStyledItemDelegate, QLineEdit, QDialog, QDialogButtonBox, QVBoxLayout, QFormLayout, QTabWidget, QWidget, QPlainTextEdit, QHBoxLayout, QSpinBox, QDateEdit, QPushButton, QLabel, QTableView
23 from PyQt5.QtCore import QDir, QLocale, QStringListModel, Qt, QDate, QSize, QUuid, QLibraryInfo
24from builtins import i18n, Application
25"""
26multi entry completer cobbled together from the two examples on stackoverflow:3779720
27
28This allows us to let people type in comma-separated lists and get completion for those.
29"""
30
31
32class multi_entry_completer(QCompleter):
33 punctuation = ","
34
35 def __init__(self, parent=None):
36 super(QCompleter, self).__init__(parent)
37
38 def pathFromIndex(self, index):
39 path = QCompleter.pathFromIndex(self, index)
40 string = str(self.widget().text())
41 split = string.split(self.punctuation)
42 if len(split) > 1:
43 path = "%s, %s" % (",".join(split[:-1]), path)
44 return path
45
46 def splitPath(self, path):
47 split = str(path.split(self.punctuation)[-1])
48 if split.startswith(" "):
49 split = split[1:]
50 if split.endswith(" "):
51 split = split[:-1]
52 return [split]
53
54
55"""
56Language combobox that can take locale codes and get the right language for it and visa-versa.
57"""
58
59
60class language_combo_box(QComboBox):
61 languageList = []
62 codesList = []
63 codesToDisplayNames = {}
64
65 def __init__(self, parent=None):
66 super(QComboBox, self).__init__(parent)
67 for locale in QLocale.matchingLocales(QLocale.Language.AnyLanguage, QLocale.Script.AnyScript, QLocale.Country.AnyCountry):
68 if locale.language() == QLocale.Language.C:
69 continue
70 codeName = locale.name().split("_")[0]
71 if codeName not in self.codesToDisplayNames:
72 self.codesToDisplayNames[codeName] = []
73 self.codesList.append(codeName)
74 displayName = locale.nativeLanguageName()
75 if not displayName:
76 displayName = QLocale.languageToString(locale.language()).title()
77 if displayName not in self.codesToDisplayNames[codeName]:
78 self.codesToDisplayNames[codeName].append(displayName)
79
80 self.codesList.sort()
81
82 for lang in self.codesList:
83 languageName = " / ".join(self.codesToDisplayNames[lang])
84
85 self.languageList.append(languageName)
86 self.setIconSize(QSize(32, 22))
87 codeIcon = QImage(self.iconSize(), QImage.Format.Format_ARGB32)
88 painter = QPainter(codeIcon)
89 painter.setBrush(Qt.GlobalColor.transparent)
90 codeIcon.fill(Qt.GlobalColor.transparent)
91 font = QFontDatabase.systemFont(QFontDatabase.SystemFont.FixedFont)
92 painter.setFont(font)
93 painter.setPen(self.palette().color(QPalette.ColorRole.Text))
94 painter.drawText(codeIcon.rect(), Qt.AlignmentFlag.AlignCenter,lang)
95 painter.end()
96 self.addItem(QIcon(QPixmap.fromImage(codeIcon)), languageName)
97
99 return self.codesList[self.currentIndex()]
100
101 def setEntryToCode(self, code):
102 if (code == "C" and "en" in self.codesList):
103 self.setCurrentIndex(self.codesList.index("en"))
104 if code in self.codesList:
105 self.setCurrentIndex(self.codesList.index(code))
106
107class country_combo_box(QComboBox):
108 countryList = []
109 codesList = []
110
111 def __init__(self, parent=None):
112 super(QComboBox, self).__init__(parent)
113
114 def set_country_for_locale(self, languageCode):
115 languageLocale = QLocale(languageCode)
116 self.clear()
119 for locale in QLocale.matchingLocales(languageLocale.language(), QLocale.Script.AnyScript, QLocale.Country.AnyCountry):
120 codeName = locale.name().split("_")[-1]
121 if codeName not in self.codesListcodesList:
122 self.codesListcodesList.append(codeName)
123 self.codesListcodesList.sort()
124
125 if QLocale.system().language() == languageLocale.language():
126 langDefaultCountryCode = QLocale.system().name().split("_")[-1]
127 else:
128 langDefaultCountryCode = languageLocale.name().split("_")[-1]
129
130 for country in self.codesListcodesList:
131 locale = QLocale(languageCode+"-"+country)
132 if locale:
133 countryName = locale.nativeCountryName()
134 self.countryListcountryList.append(countryName.title())
135 self.setIconSize(QSize(32, 22))
136 codeIcon = QImage(self.iconSize(), QImage.Format.Format_ARGB32)
137 painter = QPainter(codeIcon)
138 painter.setBrush(Qt.GlobalColor.transparent)
139 codeIcon.fill(Qt.GlobalColor.transparent)
140 font = QFontDatabase.systemFont(QFontDatabase.SystemFont.FixedFont)
141 painter.setFont(font)
142 painter.setPen(self.palette().color(QPalette.ColorRole.Text))
143 painter.drawText(codeIcon.rect(), Qt.AlignmentFlag.AlignCenter,country)
144 painter.end()
145 self.addItem(QIcon(QPixmap.fromImage(codeIcon)), countryName.title())
146
147 if country == langDefaultCountryCode:
148 self.setCurrentIndex(self.count() - 1)
149
151 if self.currentText() in self.countryListcountryList:
152 return self.codesListcodesList[self.countryListcountryList.index(self.currentText())]
153
154 def setEntryToCode(self, code):
155 if code == "C":
156 self.setCurrentIndex(0)
157 elif code in self.codesListcodesList:
158 self.setCurrentIndex(self.codesListcodesList.index(code))
159
160"""
161A combobox that fills up with licenses from a CSV, and also sets tooltips from that
162csv.
163"""
164
165
166class license_combo_box(QComboBox):
167 def __init__(self, parent=None):
168 super(QComboBox, self).__init__(parent)
169 mainP = os.path.dirname(__file__)
170 languageP = os.path.join(mainP, "LicenseList.csv")
171 model = QStandardItemModel()
172 if (os.path.exists(languageP)):
173 file = open(languageP, "r", newline="", encoding="utf8")
174 languageReader = csv.reader(file)
175 for row in languageReader:
176 license = QStandardItem(row[0])
177 license.setToolTip(row[1])
178 model.appendRow(license)
179 file.close()
180 self.setModel(model)
181
182
183"""
184Allows us to set completers on the author roles.
185"""
186
187
188class author_delegate(QStyledItemDelegate):
189 completerStrings = []
190 completerColumn = 0
191 languageColumn = 0
192
193 def __init__(self, parent=None):
194 super(QStyledItemDelegate, self).__init__(parent)
195
196 def setCompleterData(self, completerStrings=[str()], completerColumn=0):
197 self.completerStringscompleterStrings = completerStrings
198 self.completerColumncompleterColumn = completerColumn
199
200 def setLanguageData(self, languageColumn=0):
201 self.languageColumnlanguageColumn = languageColumn
202
203 def createEditor(self, parent, option, index):
204 if index.column() != self.languageColumnlanguageColumn:
205 editor = QLineEdit(parent)
206 else:
207 editor = QComboBox(parent)
208 editor.addItem("")
209 for i in range(2, 356):
210 if QLocale(i, QLocale.Script.AnyScript, QLocale.Country.AnyCountry) is not None:
211 languagecode = QLocale(i, QLocale.Script.AnyScript, QLocale.Country.AnyCountry).name().split("_")[0]
212 if languagecode != "C":
213 editor.addItem(languagecode)
214 editor.model().sort(0)
215
216 if index.column() == self.completerColumncompleterColumn:
217 editor.setCompleter(QCompleter(self.completerStringscompleterStrings))
218 editor.completer().setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
219
220 return editor
221
222
223"""
224A comic project metadata editing dialog that can take our config diactionary and set all the relevant information.
225
226To help our user, the dialog loads up lists of keywords to populate several autocompletion methods.
227"""
228
229
231 configGroup = "ComicsProjectManagementTools"
232
233 # Translatable genre dictionary that has it's translated entries added to the genrelist and from which the untranslated items are taken.
234 acbfGenreList = {"science_fiction": str(i18n("Science Fiction")), "fantasy": str(i18n("Fantasy")), "adventure": str(i18n("Adventure")), "horror": str(i18n("Horror")), "mystery": str(i18n("Mystery")), "crime": str(i18n("Crime")), "military": str(i18n("Military")), "real_life": str(i18n("Real Life")), "superhero": str(i18n("Superhero")), "humor": str(i18n("Humor")), "western": str(i18n("Western")), "manga": str(i18n("Manga")), "politics": str(i18n("Politics")), "caricature": str(i18n("Caricature")), "sports": str(i18n("Sports")), "history": str(i18n("History")), "biography": str(i18n("Biography")), "education": str(i18n("Education")), "computer": str(i18n("Computer")), "religion": str(i18n("Religion")), "romance": str(i18n("Romance")), "children": str(i18n("Children")), "non-fiction": str(i18n("Non Fiction")), "adult": str(i18n("Adult")), "alternative": str(i18n("Alternative")), "artbook": str(i18n("Artbook")), "other": str(i18n("Other"))}
235 acbfAuthorRolesList = {"Writer": str(i18n("Writer")), "Adapter": str(i18n("Adapter")), "Artist": str(i18n("Artist")), "Penciller": str(i18n("Penciller")), "Inker": str(i18n("Inker")), "Colorist": str(i18n("Colorist")), "Letterer": str(i18n("Letterer")), "Cover Artist": str(i18n("Cover Artist")), "Photographer": str(i18n("Photographer")), "Editor": str(i18n("Editor")), "Assistant Editor": str(i18n("Assistant Editor")), "Designer": str(i18n("Designer")), "Translator": str(i18n("Translator")), "Other": str(i18n("Other"))}
236
237 def __init__(self):
238 super().__init__()
239 # Get the keys for the autocompletion.
246 for g in self.acbfGenreList.values():
247 self.genreKeysList.append(g)
248 for r in self.acbfAuthorRolesList.values():
249 self.authorRoleList.append(r)
250 mainP = Path(os.path.abspath(__file__)).parent
251 self.get_auto_completion_keys(mainP)
252 extraKeyP = Path(QDir.homePath()) / Application.readSetting(self.configGroup, "extraKeysLocation", str())
253 self.get_auto_completion_keys(extraKeyP)
254
255 # Setup the dialog.
256 self.setLayout(QVBoxLayout())
257 mainWidget = QTabWidget()
258 self.layout().addWidget(mainWidget)
259 self.setWindowTitle(i18n("Comic Metadata"))
260 buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
261 self.layout().addWidget(buttons)
262 buttons.accepted.connect(self.accept)
263 buttons.rejected.connect(self.reject)
264
265 # Title, concept, summary, genre, characters, format, rating, language, series, other keywords
266 metadataPage = QWidget()
267 mformLayout = QFormLayout()
268 metadataPage.setLayout(mformLayout)
269
270 self.lnTitle = QLineEdit()
271 self.lnTitle.setToolTip(i18n("The proper title of the comic."))
272
273 self.teSummary = QPlainTextEdit()
274 self.teSummary.setToolTip(i18n("What will you tell others to entice them to read your comic?"))
275
276 self.lnGenre = QLineEdit()
277 genreCompletion = multi_entry_completer()
278 genreCompletion.setModel(QStringListModel(self.genreKeysList))
279 self.lnGenre.setCompleter(genreCompletion)
280 genreCompletion.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
281 self.lnGenre.setToolTip(i18n("The genre of the work. Prefilled values are from the ACBF, but you can fill in your own. Separate genres with commas. Try to limit the amount to about two or three."))
282
283 self.lnCharacters = QLineEdit()
284 characterCompletion = multi_entry_completer()
285 characterCompletion.setModel(QStringListModel(self.characterKeysList))
286 characterCompletion.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
287 characterCompletion.setFilterMode(Qt.MatchFlag.MatchContains) # So that if there is a list of names with last names, people can type in a last name.
288 self.lnCharacters.setCompleter(characterCompletion)
289 self.lnCharacters.setToolTip(i18n("The names of the characters that this comic revolves around. Comma-separated."))
290
291 self.lnFormat = QLineEdit()
292 formatCompletion = multi_entry_completer()
293 formatCompletion.setModel(QStringListModel(self.formatKeysList))
294 formatCompletion.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
295 self.lnFormat.setCompleter(formatCompletion)
296
297 ratingLayout = QHBoxLayout()
298 self.cmbRatingSystem = QComboBox()
299 self.cmbRatingSystem.addItems(self.ratingKeysList.keys())
300 self.cmbRatingSystem.setEditable(True)
301 self.cmbRating = QComboBox()
302 self.cmbRating.setEditable(True)
303 self.cmbRatingSystem.currentIndexChanged.connect(self.slot_refill_ratingsslot_refill_ratings)
304 ratingLayout.addWidget(self.cmbRatingSystem)
305 ratingLayout.addWidget(self.cmbRating)
306
307 self.lnSeriesName = QLineEdit()
308 self.lnSeriesName.setToolTip(i18n("If this is part of a series, enter the name of the series and the number."))
309 self.spnSeriesNumber = QSpinBox()
310 self.spnSeriesNumber.setPrefix(i18n("No. "))
311 self.spnSeriesVol = QSpinBox()
312 self.spnSeriesVol.setPrefix(i18n("Vol. "))
313 seriesLayout = QHBoxLayout()
314 seriesLayout.addWidget(self.lnSeriesName)
315 seriesLayout.addWidget(self.spnSeriesVol)
316 seriesLayout.addWidget(self.spnSeriesNumber)
317
318 otherCompletion = multi_entry_completer()
319 otherCompletion.setModel(QStringListModel(self.otherKeysList))
320 otherCompletion.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
321 otherCompletion.setFilterMode(Qt.MatchFlag.MatchContains)
322 self.lnOtherKeywords = QLineEdit()
323 self.lnOtherKeywords.setCompleter(otherCompletion)
324 self.lnOtherKeywords.setToolTip(i18n("Other keywords that do not fit in the previously mentioned sets. As always, comma-separated."))
325
328 self.cmbLanguage.currentIndexChanged.connect(self.slot_update_countriesslot_update_countries)
329 self.cmbReadingMode = QComboBox()
330 self.cmbReadingMode.addItem(i18n("Left to Right"))
331 self.cmbReadingMode.addItem(i18n("Right to Left"))
332
333 self.cmbCoverPage = QComboBox()
334 self.cmbCoverPage.setToolTip(i18n("Which page is the cover page? This will be empty if there are no pages."))
335
336 mformLayout.addRow(i18n("Title:"), self.lnTitle)
337 mformLayout.addRow(i18n("Cover page:"), self.cmbCoverPage)
338 mformLayout.addRow(i18n("Summary:"), self.teSummary)
339 mformLayout.addRow(i18n("Language:"), self.cmbLanguage)
340 mformLayout.addRow("", self.cmbCountry)
341 mformLayout.addRow(i18n("Reading direction:"), self.cmbReadingMode)
342 mformLayout.addRow(i18n("Genre:"), self.lnGenre)
343 mformLayout.addRow(i18n("Characters:"), self.lnCharacters)
344 mformLayout.addRow(i18n("Format:"), self.lnFormat)
345 mformLayout.addRow(i18n("Rating:"), ratingLayout)
346 mformLayout.addRow(i18n("Series:"), seriesLayout)
347 mformLayout.addRow(i18n("Other:"), self.lnOtherKeywords)
348
349 mainWidget.addTab(metadataPage, i18n("Work"))
350
351 # The page for the authors.
352 authorPage = QWidget()
353 authorPage.setLayout(QVBoxLayout())
354 explanation = QLabel(i18n("The following is a table of the authors that contributed to this comic. You can set their nickname, proper names (first, middle, last), role (penciller, inker, etc), email and homepage."))
355 explanation.setWordWrap(True)
356 self.authorModel = QStandardItemModel(0, 8)
357 labels = [i18n("Nick Name"), i18n("Given Name"), i18n("Middle Name"), i18n("Family Name"), i18n("Role"), i18n("Email"), i18n("Homepage"), i18n("Language")]
358 self.authorModel.setHorizontalHeaderLabels(labels)
359 self.authorTable = QTableView()
360 self.authorTable.setModel(self.authorModel)
361 self.authorTable.verticalHeader().setDragEnabled(True)
362 self.authorTable.verticalHeader().setDropIndicatorShown(True)
363 self.authorTable.verticalHeader().setSectionsMovable(True)
364 self.authorTable.verticalHeader().sectionMoved.connect(self.slot_reset_author_row_visualslot_reset_author_row_visual)
365 delegate = author_delegate()
366 delegate.setCompleterData(self.authorRoleList, 4)
367 delegate.setLanguageData(len(labels) - 1)
368 self.authorTable.setItemDelegate(delegate)
369 author_button_layout = QWidget()
370 author_button_layout.setLayout(QHBoxLayout())
371 btn_add_author = QPushButton(i18n("Add Author"))
372 btn_add_author.clicked.connect(self.slot_add_authorslot_add_author)
373 btn_remove_author = QPushButton(i18n("Remove Author"))
374 btn_remove_author.clicked.connect(self.slot_remove_authorslot_remove_author)
375 author_button_layout.layout().addWidget(btn_add_author)
376 author_button_layout.layout().addWidget(btn_remove_author)
377 authorPage.layout().addWidget(explanation)
378 authorPage.layout().addWidget(self.authorTable)
379 authorPage.layout().addWidget(author_button_layout)
380 mainWidget.addTab(authorPage, i18n("Authors"))
381
382 # The page with publisher information.
383 publisherPage = QWidget()
384 publisherLayout = QFormLayout()
385 publisherPage.setLayout(publisherLayout)
386 self.publisherName = QLineEdit()
387 self.publisherName.setToolTip(i18n("The name of the company, group or person who is responsible for the final version the reader gets."))
388 publishDateLayout = QHBoxLayout()
389 self.publishDate = QDateEdit()
390 self.publishDate.setDisplayFormat(QLocale().system().dateFormat())
391 currentDate = QPushButton(i18n("Set Today"))
392 currentDate.setToolTip(i18n("Sets the publish date to the current date."))
393 currentDate.clicked.connect(self.slot_set_dateslot_set_date)
394 publishDateLayout.addWidget(self.publishDate)
395 publishDateLayout.addWidget(currentDate)
396 self.publishCity = QLineEdit()
397 self.publishCity.setToolTip(i18n("Traditional publishers are always mentioned in source with the city they are located."))
398 self.isbn = QLineEdit()
399 self.license = license_combo_box() # Maybe ought to make this a QLineEdit...
400 self.license.setEditable(True)
401 self.license.completer().setCompletionMode(QCompleter.CompletionMode.PopupCompletion)
402 dataBaseReference = QVBoxLayout()
403 self.ln_database_name = QLineEdit()
404 self.ln_database_name.setToolTip(i18n("If there is an entry in a comics data base, that should be added here. It is unlikely to be a factor for comics from scratch, but useful when doing a conversion."))
405 self.cmb_entry_type = QComboBox()
406 self.cmb_entry_type.addItems(["IssueID", "SeriesID", "URL"])
407 self.cmb_entry_type.setEditable(True)
408 self.ln_source = QLineEdit()
409 self.ln_source.setToolTip(i18n("Whether the comic is an adaptation of an existing source, and if so, how to find information about that source. So for example, for an adapted webcomic, the official website url should go here."))
410 self.label_uuid = QLabel()
411 self.label_uuid.setToolTip(i18n("By default this will be filled with a generated universal unique identifier. The ID by itself is merely so that comic book library management programs can figure out if this particular comic is already in their database and whether it has been rated. Of course, the UUID can be changed into something else by manually changing the JSON, but this is advanced usage."))
412 self.ln_database_entry = QLineEdit()
413 dbHorizontal = QHBoxLayout()
414 dbHorizontal.addWidget(self.ln_database_name)
415 dbHorizontal.addWidget(self.cmb_entry_type)
416 dataBaseReference.addLayout(dbHorizontal)
417 dataBaseReference.addWidget(self.ln_database_entry)
418 publisherLayout.addRow(i18n("Name:"), self.publisherName)
419 publisherLayout.addRow(i18n("City:"), self.publishCity)
420 publisherLayout.addRow(i18n("Date:"), publishDateLayout)
421 publisherLayout.addRow(i18n("ISBN:"), self.isbn)
422 publisherLayout.addRow(i18n("Source:"), self.ln_source)
423 publisherLayout.addRow(i18n("UUID:"), self.label_uuid)
424 publisherLayout.addRow(i18n("License:"), self.license)
425 publisherLayout.addRow(i18n("Database:"), dataBaseReference)
426
427 mainWidget.addTab(publisherPage, i18n("Publisher"))
428 """
429 Ensure that the drag and drop of authors doesn't mess up the labels.
430 """
431
433 headerLabelList = []
434 for i in range(self.authorTable.verticalHeader().count()):
435 headerLabelList.append(str(i))
436 for i in range(self.authorTable.verticalHeader().count()):
437 logicalI = self.authorTable.verticalHeader().logicalIndex(i)
438 headerLabelList[logicalI] = str(i + 1)
439 self.authorModel.setVerticalHeaderLabels(headerLabelList)
440 """
441 Set the publish date to the current date.
442 """
443
444 def slot_set_date(self):
445 self.publishDate.setDate(QDate().currentDate())
446
448 code = self.cmbLanguage.codeForCurrentEntry()
449 self.cmbCountry.set_country_for_locale(code)
450
451 """
452 Append keys to autocompletion lists from the directory mainP.
453 """
454
455 def get_auto_completion_keys(self, mainP=Path()):
456 genre = Path(mainP / "key_genre")
457 characters = Path(mainP / "key_characters")
458 rating = Path(mainP / "key_rating")
459 format = Path(mainP / "key_format")
460 keywords = Path(mainP / "key_other")
461 authorRole = Path(mainP / "key_author_roles")
462 if genre.exists():
463 for t in list(genre.glob('**/*.txt')):
464 file = open(str(t), "r", errors="replace")
465 for l in file:
466 if str(l).strip("\n") not in self.genreKeysList:
467 self.genreKeysList.append(str(l).strip("\n"))
468 file.close()
469 if characters.exists():
470 for t in list(characters.glob('**/*.txt')):
471 file = open(str(t), "r", errors="replace")
472 for l in file:
473 if str(l).strip("\n") not in self.characterKeysList:
474 self.characterKeysList.append(str(l).strip("\n"))
475 file.close()
476 if format.exists():
477 for t in list(format.glob('**/*.txt')):
478 file = open(str(t), "r", errors="replace")
479 for l in file:
480 if str(l).strip("\n") not in self.formatKeysList:
481 self.formatKeysList.append(str(l).strip("\n"))
482 file.close()
483 if rating.exists():
484 for t in list(rating.glob('**/*.csv')):
485 file = open(str(t), "r", newline="", encoding="utf-8")
486 ratings = csv.reader(file)
487 title = os.path.basename(str(t))
488 r = 0
489 for row in ratings:
490 listItem = []
491 if r == 0:
492 title = row[1]
493 else:
494 listItem = self.ratingKeysList[title]
495 item = []
496 item.append(row[0])
497 item.append(row[1])
498 listItem.append(item)
499 self.ratingKeysList[title] = listItem
500 r += 1
501 file.close()
502 if keywords.exists():
503 for t in list(keywords.glob('**/*.txt')):
504 file = open(str(t), "r", errors="replace")
505 for l in file:
506 if str(l).strip("\n") not in self.otherKeysList:
507 self.otherKeysList.append(str(l).strip("\n"))
508 file.close()
509 if authorRole.exists():
510 for t in list(authorRole.glob('**/*.txt')):
511 file = open(str(t), "r", errors="replace")
512 for l in file:
513 if str(l).strip("\n") not in self.authorRoleList:
514 self.authorRoleList.append(str(l).strip("\n"))
515 file.close()
516
517 """
518 Refill the ratings box.
519 This is called whenever the rating system changes.
520 """
521
523 if self.cmbRatingSystem.currentText() in self.ratingKeysList.keys():
524 self.cmbRating.clear()
525 model = QStandardItemModel()
526 for i in self.ratingKeysList[self.cmbRatingSystem.currentText()]:
527 item = QStandardItem()
528 item.setText(i[0])
529 item.setToolTip(i[1])
530 model.appendRow(item)
531 self.cmbRating.setModel(model)
532
533 """
534 Add an author with default values initialised.
535 """
536
538 listItems = []
539 listItems.append(QStandardItem(i18n("Anon"))) # Nick name
540 listItems.append(QStandardItem(i18n("John"))) # First name
541 listItems.append(QStandardItem()) # Middle name
542 listItems.append(QStandardItem(i18n("Doe"))) # Last name
543 listItems.append(QStandardItem()) # role
544 listItems.append(QStandardItem()) # email
545 listItems.append(QStandardItem()) # homepage
546 language = QLocale.system().name().split("_")[0]
547 if language == "C":
548 language = "en"
549 listItems.append(QStandardItem(language)) # Language
550 self.authorModel.appendRow(listItems)
551
552 """
553 Remove the selected author from the author list.
554 """
555
557 self.authorModel.removeRow(self.authorTable.currentIndex().row())
558
559 """
560 Load the UI values from the config dictionary given.
561 """
562
563 def setConfig(self, config):
564
565 if "title" in config.keys():
566 self.lnTitle.setText(config["title"])
567 self.teSummary.clear()
568 if "pages" in config.keys():
569 self.cmbCoverPage.clear()
570 for page in config["pages"]:
571 self.cmbCoverPage.addItem(page)
572 if "cover" in config.keys():
573 if config["cover"] in config["pages"]:
574 self.cmbCoverPage.setCurrentText(config["cover"])
575 if "summary" in config.keys():
576 self.teSummary.appendPlainText(config["summary"])
577 if "genre" in config.keys():
578 genreList = []
579 genreListConf = config["genre"]
580 totalMatch = 100
581 if isinstance(config["genre"], dict):
582 genreListConf = config["genre"].keys()
583 totalMatch = 0
584 for genre in genreListConf:
585 genreKey = genre
586 if genre in self.acbfGenreList:
587 genreKey = self.acbfGenreList[genre]
588 if isinstance(config["genre"], dict):
589 genreValue = config["genre"][genre]
590 if genreValue > 0:
591 genreKey = str(genreKey + "(" + str(genreValue) + ")")
592 genreList.append(genreKey)
593 self.lnGenre.setText(", ".join(genreList))
594 if "characters" in config.keys():
595 self.lnCharacters.setText(", ".join(config["characters"]))
596 if "format" in config.keys():
597 self.lnFormat.setText(", ".join(config["format"]))
598 if "rating" in config.keys():
599 self.cmbRating.setCurrentText(config["rating"])
600 else:
601 self.cmbRating.setCurrentText("")
602 if "ratingSystem" in config.keys():
603 self.cmbRatingSystem.setCurrentText(config["ratingSystem"])
604 else:
605 self.cmbRatingSystem.setCurrentText("")
606 if "otherKeywords" in config.keys():
607 self.lnOtherKeywords.setText(", ".join(config["otherKeywords"]))
608 if "seriesName" in config.keys():
609 self.lnSeriesName.setText(config["seriesName"])
610 if "seriesVolume" in config.keys():
611 self.spnSeriesVol.setValue(config["seriesVolume"])
612 if "seriesNumber" in config.keys():
613 self.spnSeriesNumber.setValue(config["seriesNumber"])
614 if "language" in config.keys():
615 code = config["language"]
616 if "_" in code:
617 self.cmbLanguage.setEntryToCode(code.split("_")[0])
618 self.cmbCountry.setEntryToCode(code.split("_")[-1])
619 elif "-" in code:
620 self.cmbLanguage.setEntryToCode(code.split("-")[0])
621 self.cmbCountry.setEntryToCode(code.split("-")[-1])
622 else:
623 self.cmbLanguage.setEntryToCode(code)
624 if "readingDirection" in config.keys():
625 if QLibraryInfo.version().majorVersion() == 6: # PyQt6
626 if config["readingDirection"] == "leftToRight":
627 self.cmbReadingMode.setCurrentIndex(Qt.LayoutDirection.LeftToRight.value)
628 else:
629 self.cmbReadingMode.setCurrentIndex(Qt.LayoutDirection.RightToLeft.value)
630 else: #PyQt5
631 if config["readingDirection"] == "leftToRight":
632 self.cmbReadingMode.setCurrentIndex(Qt.LayoutDirection.LeftToRight)
633 else:
634 self.cmbReadingMode.setCurrentIndex(Qt.LayoutDirection.RightToLeft)
635 else:
636 if QLibraryInfo.version().majorVersion() == 6: # PyQt6
637 self.cmbReadingMode.setCurrentIndex(QLocale(self.cmbLanguage.codeForCurrentEntry()).textDirection().value)
638 else:
639 self.cmbReadingMode.setCurrentIndex(QLocale(self.cmbLanguage.codeForCurrentEntry()).textDirection())
640 if "publisherName" in config.keys():
641 self.publisherName.setText(config["publisherName"])
642 if "publisherCity" in config.keys():
643 self.publishCity.setText(config["publisherCity"])
644 if "publishingDate" in config.keys():
645 self.publishDate.setDate(QDate.fromString(config["publishingDate"], Qt.DateFormat.ISODate))
646 if "isbn-number" in config.keys():
647 self.isbn.setText(config["isbn-number"])
648 if "source" in config.keys():
649 self.ln_source.setText(config["source"])
650 elif "acbfSource" in config.keys():
651 self.ln_source.setText(config["acbfSource"])
652 if "uuid" in config.keys():
653 self.label_uuid.setText(config["uuid"])
654 else:
655 uuid = str()
656 if "acbfID" in config.keys():
657 uuid = config["acbfID"]
658 uuid = uuid.strip("{")
659 uuid = uuid.strip("}")
660 uuidVerify = uuid.split("-")
661 if len(uuidVerify[0])!=8 or len(uuidVerify[1])!=4 or len(uuidVerify[2])!=4 or len(uuidVerify[3])!=4 or len(uuidVerify[4])!=12:
662 uuid = QUuid.createUuid().toString()
663 self.label_uuid.setText(uuid)
664 config["uuid"] = uuid
665 if "license" in config.keys():
666 self.license.setCurrentText(config["license"])
667 else:
668 self.license.setCurrentText("") # I would like to keep it ambiguous whether the artist has thought about the license or not.
669 if "authorList" in config.keys():
670 authorList = config["authorList"]
671 for i in range(len(authorList)):
672 author = authorList[i]
673 if len(author.keys()) > 0:
674 listItems = []
675 listItems = []
676 listItems.append(QStandardItem(author.get("nickname", "")))
677 listItems.append(QStandardItem(author.get("first-name", "")))
678 listItems.append(QStandardItem(author.get("initials", "")))
679 listItems.append(QStandardItem(author.get("last-name", "")))
680 role = author.get("role", "")
681 if role in self.acbfAuthorRolesList.keys():
682 role = self.acbfAuthorRolesList[role]
683 listItems.append(QStandardItem(role))
684 listItems.append(QStandardItem(author.get("email", "")))
685 listItems.append(QStandardItem(author.get("homepage", "")))
686 listItems.append(QStandardItem(author.get("language", "")))
687 self.authorModel.appendRow(listItems)
688 else:
690 dbRef = config.get("databaseReference", {})
691 self.ln_database_name.setText(dbRef.get("name", ""))
692 self.ln_database_entry.setText(dbRef.get("entry", ""))
693 stringCmbEntryType = self.cmb_entry_type.itemText(0)
694 self.cmb_entry_type.setCurrentText(dbRef.get("type", stringCmbEntryType))
695
696 """
697 Store the GUI values into the config dictionary given.
698
699 @return the config diactionary filled with new values.
700 """
701
702 def getConfig(self, config):
703
704 text = self.lnTitle.text()
705 if len(text) > 0 and text.isspace() is False:
706 config["title"] = text
707 elif "title" in config.keys():
708 config.pop("title")
709 config["cover"] = self.cmbCoverPage.currentText()
710 listkeys = self.lnGenre.text()
711 if len(listkeys) > 0 and listkeys.isspace() is False:
712 preSplit = self.lnGenre.text().split(",")
713 genreMatcher = re.compile(r'\‍((\d+)\‍)')
714 genreList = {}
715 totalValue = 0
716 for key in preSplit:
717 m = genreMatcher.search(key)
718 if m:
719 genre = str(genreMatcher.sub("", key)).strip()
720 match = int(m.group()[:-1][1:])
721 else:
722 genre = key.strip()
723 match = 0
724 if genre in self.acbfGenreList.values():
725 i = list(self.acbfGenreList.values()).index(genre)
726 genreList[list(self.acbfGenreList.keys())[i]] = match
727 else:
728 genreList[genre] = match
729 totalValue += match
730 # Normalize the values:
731 for key in genreList.keys():
732 if genreList[key] > 0:
733 genreList[key] = round(genreList[key] / totalValue * 100)
734 config["genre"] = genreList
735 elif "genre" in config.keys():
736 config.pop("genre")
737 listkeys = self.lnCharacters.text()
738 if len(listkeys) > 0 and listkeys.isspace() is False:
739 config["characters"] = self.lnCharacters.text().split(", ")
740 elif "characters" in config.keys():
741 config.pop("characters")
742 listkeys = self.lnFormat.text()
743 if len(listkeys) > 0 and listkeys.isspace() is False:
744 config["format"] = self.lnFormat.text().split(", ")
745 elif "format" in config.keys():
746 config.pop("format")
747 config["ratingSystem"] = self.cmbRatingSystem.currentText()
748 config["rating"] = self.cmbRating.currentText()
749 listkeys = self.lnOtherKeywords.text()
750 if len(listkeys) > 0 and listkeys.isspace() is False:
751 config["otherKeywords"] = self.lnOtherKeywords.text().split(", ")
752 elif "otherKeywords" in config.keys():
753 config.pop("otherKeywords")
754 text = self.teSummary.toPlainText()
755 if len(text) > 0 and text.isspace() is False:
756 config["summary"] = text
757 elif "summary" in config.keys():
758 config.pop("summary")
759 if len(self.lnSeriesName.text()) > 0:
760 config["seriesName"] = self.lnSeriesName.text()
761 config["seriesNumber"] = self.spnSeriesNumber.value()
762 if self.spnSeriesVol.value() > 0:
763 config["seriesVolume"] = self.spnSeriesVol.value()
764 config["language"] = str(self.cmbLanguage.codeForCurrentEntry()+"-"+self.cmbCountry.codeForCurrentEntry())
765 if self.cmbReadingMode.currentIndex() is int(Qt.LayoutDirection.LeftToRight):
766 config["readingDirection"] = "leftToRight"
767 else:
768 config["readingDirection"] = "rightToLeft"
769 authorList = []
770 for row in range(self.authorTable.verticalHeader().count()):
771 logicalIndex = self.authorTable.verticalHeader().logicalIndex(row)
772 listEntries = ["nickname", "first-name", "initials", "last-name", "role", "email", "homepage", "language"]
773 author = {}
774 for i in range(len(listEntries)):
775 entry = self.authorModel.data(self.authorModel.index(logicalIndex, i))
776 if entry is None:
777 entry = " "
778 if entry.isspace() is False and len(entry) > 0:
779 if listEntries[i] == "role":
780 if entry in self.acbfAuthorRolesList.values():
781 entryI = list(self.acbfAuthorRolesList.values()).index(entry)
782 entry = list(self.acbfAuthorRolesList.keys())[entryI]
783 author[listEntries[i]] = entry
784 elif listEntries[i] in author.keys():
785 author.pop(listEntries[i])
786 authorList.append(author)
787 config["authorList"] = authorList
788 config["publisherName"] = self.publisherName.text()
789 config["publisherCity"] = self.publishCity.text()
790 config["publishingDate"] = self.publishDate.date().toString(Qt.DateFormat.ISODate)
791 config["isbn-number"] = self.isbn.text()
792 config["source"] = self.ln_source.text()
793 config["license"] = self.license.currentText()
794 if self.ln_database_name.text().isalnum() and self.ln_database_entry.text().isalnum():
795 dbRef = {}
796 dbRef["name"] = self.ln_database_name.text()
797 dbRef["entry"] = self.ln_database_entry.text()
798 dbRef["type"] = self.cmb_entry_type.currentText()
799 config["databaseReference"] = dbRef
800
801 return config
float value(const T *src, size_t ch)
setCompleterData(self, completerStrings=[str()], completerColumn=0)
str punctuation
pathFromIndex(self, index)
__init__(self, parent=None)
splitPath(self, path)