File indexing completed on 2025-02-02 04:22:25

0001 """
0002 SPDX-FileCopyrightText: 2017 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
0003 SPDX-FileCopyrightText: 2018 Ragnar Brynúlfsson <me@ragnarb.com>
0004 
0005 This file is part of the Comics Project Management Tools(CPMT).
0006 
0007 SPDX-License-Identifier: GPL-3.0-or-later
0008 """
0009 
0010 """
0011 This is a docker that shows your comic pages.
0012 """
0013 
0014 import os
0015 import sys
0016 import json
0017 import zipfile
0018 from PyQt5.QtGui import QImage, QPainter
0019 from PyQt5.QtWidgets import QDialog, QWidget, QHBoxLayout, QVBoxLayout, QPushButton, QSizePolicy, QDialogButtonBox, QShortcut, QLabel
0020 from PyQt5.QtCore import QSize, Qt
0021 
0022 # To run standalon
0023 from PyQt5.QtWidgets import QApplication
0024 
0025 
0026 class page_viewer(QPushButton):
0027 
0028     def __init__(self, parent=None, flags=None):
0029         super(page_viewer, self).__init__(parent)
0030         self.alignment = 'left'
0031         self.image = QImage()
0032         self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
0033 
0034     def align_left(self):
0035         self.alignment = 'left'
0036 
0037     def align_right(self):
0038         self.alignment = 'right'
0039 
0040     def align_center(self):
0041         self.alignment = 'center'
0042 
0043     def set_image(self, image=QImage()):
0044         self.image = image
0045         self.update()
0046 
0047     def paintEvent(self, event):
0048         painter = QPainter(self)
0049         previewSize = self.size()*self.devicePixelRatioF()
0050         image = ""
0051 
0052         if self.image.width() <= previewSize.width() or self.image.height() <= previewSize.height():
0053             # pixel art
0054             image = self.image.scaled(previewSize, Qt.KeepAspectRatio, Qt.FastTransformation)
0055         else:
0056             image = self.image.scaled(previewSize, Qt.KeepAspectRatio, Qt.SmoothTransformation)
0057         image.setDevicePixelRatio(self.devicePixelRatioF())
0058         if self.alignment == 'right':
0059             x_offset = int(self.width() - image.width()/self.devicePixelRatioF())
0060         elif self.alignment == 'center':
0061             x_offset = int((self.width() - image.width()/self.devicePixelRatioF()) / 2)
0062         else:
0063             x_offset = 0
0064         painter.drawImage(x_offset, 0, image)
0065 
0066     def sizeHint(self):
0067         return QSize(256, 256)
0068 
0069 
0070 class comics_project_page_viewer(QDialog):
0071     pageIndex = 0
0072     spread = True
0073 
0074     def __init__(self):
0075         super().__init__()
0076 
0077         self.setModal(False)
0078         self.setWindowTitle('Untitled')
0079         self.setWindowFlags(
0080             Qt.WindowTitleHint |
0081             Qt.WindowMinimizeButtonHint |
0082             Qt.WindowMaximizeButtonHint |
0083             Qt.WindowCloseButtonHint
0084             )
0085         self.resize(1024, 768)
0086         self.setLayout(QVBoxLayout())
0087 
0088         self.left_viewer = page_viewer()
0089         self.left_viewer.align_right()
0090         self.left_viewer.clicked.connect(self.prev_page)
0091         self.right_viewer = page_viewer()
0092         self.right_viewer.align_left()
0093         self.right_viewer.clicked.connect(self.next_page)
0094         self.single_viewer = page_viewer()
0095         self.single_viewer.align_center()
0096         self.single_viewer.clicked.connect(self.next_page)
0097         self.single_viewer.hide()
0098 
0099         self.page_layout = QHBoxLayout()
0100         self.layout().addLayout(self.page_layout)
0101         self.page_layout.addWidget(self.left_viewer)
0102         self.page_layout.addWidget(self.right_viewer)
0103         self.page_layout.addWidget(self.single_viewer)
0104 
0105         # Keyboard shortcuts
0106         self.home_shortcut = QShortcut('Home', self)
0107         self.home_shortcut.activated.connect(self.first_page)
0108         self.left_shortcut = QShortcut('Left', self)
0109         self.left_shortcut.activated.connect(self.prev_page)
0110         self.right_shortcut = QShortcut('Right', self)
0111         self.right_shortcut.activated.connect(self.next_page)
0112         self.space_shortcut = QShortcut('Space', self)
0113         self.space_shortcut.activated.connect(self.next_page)
0114         self.end_shortcut = QShortcut('End', self)
0115         self.end_shortcut.activated.connect(self.last_page)
0116 
0117         self.first_btn = QPushButton('First')
0118         self.first_btn.clicked.connect(self.first_page)
0119         self.prev_btn = QPushButton('Previous')
0120         self.prev_btn.clicked.connect(self.prev_page)
0121         self.spread_btn = QPushButton('Single Page')
0122         self.spread_btn.clicked.connect(self.toggle_spread)
0123         self.next_btn = QPushButton('Next')
0124         self.next_btn.clicked.connect(self.next_page)
0125         self.last_btn = QPushButton('Last')
0126         self.last_btn.clicked.connect(self.last_page)
0127 
0128         self.buttons_layout = QHBoxLayout()
0129         self.layout().addLayout(self.buttons_layout)
0130         self.buttons_layout.addWidget(self.first_btn)
0131         self.buttons_layout.addWidget(self.prev_btn)
0132         self.buttons_layout.addWidget(self.spread_btn)
0133         self.buttons_layout.addWidget(self.next_btn)
0134         self.buttons_layout.addWidget(self.last_btn)
0135 
0136     def load_comic(self, path_to_config):
0137         self.path_to_config = path_to_config
0138         configFile = open(self.path_to_config, "r", newline="", encoding="utf-16")
0139         self.setupDictionary = json.load(configFile)
0140         self.projecturl = os.path.dirname(str(self.path_to_config))
0141         if 'readingDirection' in self.setupDictionary:
0142             if self.setupDictionary['readingDirection'] == "leftToRight":
0143                 self.setLayoutDirection(Qt.LeftToRight)
0144                 self.left_shortcut.disconnect()
0145                 self.right_shortcut.disconnect()
0146                 self.left_shortcut.activated.connect(self.prev_page)
0147                 self.right_shortcut.activated.connect(self.next_page)
0148             else:
0149                 self.left_shortcut.disconnect()
0150                 self.right_shortcut.disconnect()
0151                 self.setLayoutDirection(Qt.RightToLeft)
0152                 self.left_shortcut.activated.connect(self.next_page)
0153                 self.right_shortcut.activated.connect(self.prev_page)
0154         else:
0155             self.left_shortcut.disconnect()
0156             self.right_shortcut.disconnect()
0157             self.setLayoutDirection(Qt.LeftToRight)
0158             self.left_shortcut.activated.connect(self.prev_page)
0159             self.right_shortcut.activated.connect(self.next_page)
0160         configFile.close()
0161 
0162     def go_to_page_index(self, index):
0163         if index >= 0 and index < len(self.setupDictionary['pages']):
0164             self.pageIndex = index
0165         else:
0166             self.pageIndex = 0
0167         self.flip_page()
0168 
0169     def toggle_spread(self):
0170         if self.spread:
0171             self.spread = False
0172             self.spread_btn.setText('Double Spread')
0173             self.update_single_page(self.pageIndex)
0174             self.left_viewer.hide()
0175             self.right_viewer.hide()
0176             self.single_viewer.show()
0177             self.flip_page()
0178         else:
0179             self.spread = True
0180             self.spread_btn.setText('Single Page')
0181             self.left_viewer.show()
0182             self.right_viewer.show()
0183             self.single_viewer.hide()
0184             self.flip_page()
0185 
0186     def update_single_page(self, index):
0187         image = self.get_mergedimage(index)
0188         self.single_viewer.set_image(image)
0189 
0190     def update_left_page(self, index):
0191         image = self.get_mergedimage(index)
0192         self.left_viewer.set_image(image)
0193 
0194     def update_right_page(self, index):
0195         image = self.get_mergedimage(index)
0196         self.right_viewer.set_image(image)
0197 
0198     def first_page(self):
0199         self.pageIndex = 0
0200         self.flip_page()
0201 
0202     def prev_page(self):
0203         if self.pageIndex <= 0:
0204             self.pageIndex = len(self.setupDictionary['pages']) - 1
0205         else:
0206             if self.spread:
0207                 self.pageIndex -= 2
0208             else:
0209                 self.pageIndex -= 1
0210         self.flip_page()
0211 
0212     def next_page(self):
0213         if self.pageIndex >= len(self.setupDictionary['pages']) - 1:
0214             self.pageIndex = 0
0215         else:
0216             if self.spread:
0217                 self.pageIndex += 2
0218             else:
0219                 self.pageIndex += 1
0220         self.flip_page()
0221 
0222     def last_page(self):
0223         self.pageIndex = len(self.setupDictionary['pages']) - 1
0224         self.flip_page()
0225 
0226     def flip_page(self):
0227         if self.spread:
0228             if self.pageIndex % 2 == 0: # Even/Left
0229                 left_page_number = self.pageIndex - 1
0230                 right_page_number = self.pageIndex
0231             else: # Odd/Right
0232                 left_page_number = self.pageIndex
0233                 right_page_number = self.pageIndex + 1
0234             self.update_left_page(left_page_number)
0235             self.update_right_page(right_page_number)
0236             if left_page_number < 0:
0237                 page_numbers = str(right_page_number + 1)
0238             elif right_page_number >= len(self.setupDictionary['pages']):
0239                 page_numbers = str(left_page_number + 1)
0240             else:
0241                 page_numbers = '{left} / {right}'.format(
0242                     left = str(left_page_number + 1),
0243                     right = str(right_page_number + 1))
0244             self.setWindowTitle('{name} - {page_numbers}'.format(
0245                 name = self.setupDictionary['projectName'],
0246                 page_numbers = page_numbers))
0247         else:
0248             if self.pageIndex >= len(self.setupDictionary['pages']):
0249                 self.pageIndex = len(self.setupDictionary['pages']) - 1
0250             if self.pageIndex < 0:
0251                 self.pageIndex = 0
0252             self.update_single_page(self.pageIndex)
0253             self.setWindowTitle('{name} - {page_numbers}'.format(
0254                 name = self.setupDictionary['projectName'],
0255                 page_numbers = str(self.pageIndex + 1)))
0256 
0257 
0258     def get_mergedimage(self, index):
0259         if index < len(self.setupDictionary['pages']) and index > -1:
0260             image_url = os.path.join(self.projecturl, self.setupDictionary['pages'][index])
0261             if os.path.exists(image_url):
0262                 page = zipfile.ZipFile(image_url, "r")
0263                 image = QImage.fromData(page.read("mergedimage.png"))
0264                 page.close()
0265                 return image
0266         image = QImage(QSize(10, 10), QImage.Format_ARGB32)
0267         image.fill(Qt.GlobalColor(19))
0268         return image
0269 
0270 if __name__ == '__main__':
0271     ''' Run the page viewer outside Krita '''
0272     app = QApplication(sys.argv)
0273     if sys.argv:
0274         print(sys.argv)
0275         path_to_config = sys.argv[1]
0276         if os.path.exists(path_to_config):
0277             print('Run comics viewer')
0278             page_viewer_dialog = comics_project_page_viewer()
0279             page_viewer_dialog.load_comic(path_to_config)
0280             page_viewer_dialog.go_to_page_index(0)
0281             page_viewer_dialog.show()
0282         else:
0283             print('No comic found in {comic}'.format(comic=comic))
0284     else:
0285         print('Pass the path to a Krita comicConfig.json file to this script, to view the comic.')
0286 
0287     sys.exit(app.exec_())
0288 
0289 
0290