"""SkeletonApp.""" # from PyQt6 import Qt from PyQt6 import QtGui from PyQt6 import QtCore from PyQt6 import QtWidgets from PyQt6.QtWidgets import QApplication, QSizePolicy, QDialog, QMenu from PyQt6.QtWidgets import QMainWindow from PyQt6.QtWidgets import QWidget # include from PyQt6.QtWidgets import QToolBar # include from PyQt6.QtGui import QIcon, QPalette, QGuiApplication, QImage, QPixmap, qRed, qGreen, qBlue, qAlpha, qRgba, QAction, QKeySequence, qRgb # include from PyQt6.QtGui import QClipboard # include from PyQt6.QtGui import QColorSpace # include from PyQt6.QtCore import QDir, QTranslator, Qt # include from PyQt6.QtWidgets import QFileDialog # include from PyQt6.QtGui import QImageReader # include from PyQt6.QtGui import QImageWriter # include from PyQt6.QtCore import QBuffer # include from PyQt6.QtWidgets import QLabel # include from PyQt6.QtWidgets import QMenuBar # include from PyQt6.QtWidgets import QMessageBox # include from PyQt6.QtCore import QMimeData # include from PyQt6.QtGui import QPainter # include from PyQt6.QtGui import QScreen # include from PyQt6.QtWidgets import QScrollArea # include from PyQt6.QtWidgets import QScrollBar # include from PyQt6.QtCore import QStandardPaths # include from PyQt6.QtWidgets import QStatusBar from io import BytesIO # include from io import FileIO # include # # class ScrollArea: # """Scroll area.""" # def __init__(self, scroll_area=QScrollArea()): # pass # # # class ImageLabel: # """Image label.""" # def __init__(self, image_label=QLabel()): # pass class SkeletonApp(QMainWindow): """SkeletonApp.""" firstDialog = None def __init__(self, parent=None): super(SkeletonApp, self).__init__(parent) self.image_label = QLabel() self.image_label.setBackgroundRole(QPalette.ColorRole.Base) self.image_label.setSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Ignored) self.image_label.setScaledContents(True) self.scroll_area = QScrollArea() self.scroll_area.setBackgroundRole(QPalette.ColorRole.Dark) self.scroll_area.setWidget(self.image_label) self.scroll_area.setVisible(False) self.setCentralWidget(self.scroll_area) self.resize(QGuiApplication.primaryScreen().availableSize() * 3 / 5) self.fileMenu: QMenu = self.menuBar().addMenu(self.tr("&File")) self.editMenu: QMenu = self.menuBar().addMenu(self.tr("&Edit")) self.viewMenu: QMenu = self.menuBar().addMenu(self.tr("&View")) self.helpMenu: QMenu = self.menuBar().addMenu(self.tr("&Help")) self.filtersMenu: QMenu = self.menuBar().addMenu(self.tr("&Filters")) self.data = [] self.reader = None self.pixmap = QPixmap() self.image = self.pixmap.toImage() self.bit_planes = 0 self.depth = 0 self.format = None self.bytes_per_line = 0 self.nbChannels = 1 self.nbRealChannels = 1 self.pixel = None self.row = [] self.image_width = 0 self.image_height = 0 self.translator = QTranslator(self) self.scaleFactor = 1.0 self.pictures_locations = None self.mimeTypeFilters = [] self.dataResult = [] self.createActions() self.updateActions() def loadFile(self, file_name: str): """Load file.""" self.data.clear() self.reader = QImageReader(file_name) self.reader.setAutoTransform(True) # const _new_image: QImage = self.reader.read() if _new_image.isNull(): QMessageBox.information( self, QGuiApplication.applicationDisplayName(), self.tr(F"Cannot load {QDir.toNativeSeparators(file_name)}:{self.reader.errorString()}")) return False self.filtersMenu.setDisabled(False) self.setImage(new_image=_new_image) self.image_width = self.image.width() self.image_height = self.image.height() self.bit_planes = int(self.image.bitPlaneCount()) self.depth = int(self.image.depth()) self.format = self.image.format() print(type(self.format)) self.bytes_per_line = int(self.image.bytesPerLine()) self.nbChannels = 1 if self.depth > 1: self.nbChannels = self.depth / 8 self.nbRealChannels = 1 if self.bit_planes > 1: self.nbRealChannels = int(self.bit_planes / 8) file = open("test_zapisu.txt", 'w') if file: for i in range(self.image_height): row = [] for j in range(self.image_width): # działa w logice pixel(x,y) - najpierw specyfikuje kolumnę impix: qRgb = self.image.pixel(j, i) pixel = [] if self.nbChannels == 1: pixel = [qRed(impix)] file.writelines(str(pix) + " " for pix in pixel) elif self.nbChannels == 3: pixel = [qRed(impix), qGreen(impix), qBlue(impix)] file.writelines(str(pix) + " " for pix in pixel) elif self.nbChannels == 4: pixel = [qRed(impix), qGreen(impix), qBlue(impix), qAlpha(impix)] file.writelines(str(pix) + " " for pix in pixel) file.write("\n") row.append(pixel) self.data.append(row) file.close() self.setWindowFilePath(file_name) message = self.tr(f"Opened \"{QDir.toNativeSeparators(file_name)}\", {self.image.width()}x{self.image.height()}, Depth: {self.image.depth()}, " f"Bit planes: {self.image.bitPlaneCount()}") self.statusBar().showMessage(message) return True def setImage(self, new_image: QImage): """Set image.""" self.image = new_image if self.image.colorSpace().isValid(): self.image.convertToColorSpace(QColorSpace(QColorSpace.NamedColorSpace.SRgb)) self.image_label.setPixmap(QPixmap.fromImage(self.image)) self.scaleFactor = 1.0 self.scroll_area.setVisible(True) # self.printAct.setEnabled(True) self.fitToWindowAct.setEnabled(True) self.updateActions() if not self.fitToWindowAct.isChecked(): self.image_label.adjustSize() def scaleImage(self, factor: float): """Scale image.""" self.scaleFactor *= factor self.image_label.resize(self.scaleFactor * self.image_label.pixmap().width(), self.scaleFactor * self.image_label.pixmap().height()) self.adjustScrollBar(self.scroll_area.horizontalScrollBar(), factor) self.adjustScrollBar(self.scroll_area.verticalScrollBar(), factor) self.zoomInAct.setEnabled(self.scaleFactor < 3.0) self.zoomOutAct.setEnabled(self.scaleFactor > 0.333) def adjustScrollBar(self, scroll_bar: QScrollBar, factor: float): """adjust scroll bar""" scroll_bar.setValue(int(factor * scroll_bar.value() + ((factor - 1) * scroll_bar.pageStep() / 2))) def about(self): """about""" QMessageBox.about(self, self.tr("About skeletonApp"), self.tr("

Aplikacja szkieletowa do kursu Przetwarzanie obrazu

")) def saveFile(self, file_name: str): """Save.""" writer = QImageWriter(file_name) if not writer.write(self.image): QMessageBox.information( self, QGuiApplication.applicationDisplayName(), self.tr(F"Cannot write {QDir.toNativeSeparators(file_name)}: {writer.errorString()}")) return False message = self.tr(f"Wrote \"{QDir.toNativeSeparators(file_name)}\"") self.statusBar().showMessage(message) return True def initializeImageFileDialog(self, dialog, acceptMode): """Initialize Image File Dialog.""" self.firstDialog = True if self.firstDialog: self.firstDialog = False self.pictures_locations: list = QStandardPaths.standardLocations(QStandardPaths.StandardLocation.PicturesLocation) dialog.setDirectory(QDir.currentPath() if not self.pictures_locations else self.pictures_locations[-1]) self.mimeTypeFilters = [] supported_mime_types = None if acceptMode == QFileDialog.AcceptMode.AcceptOpen: supported_mime_types = QImageReader.supportedMimeTypes().__str__() else: supported_mime_types = QImageWriter.supportedMimeTypes().__str__() for mime_type_name in supported_mime_types: self.mimeTypeFilters.append(mime_type_name) self.mimeTypeFilters.sort() dialog.setMimeTypeFilters(self.mimeTypeFilters) dialog.selectMimeTypeFilter("image/x-portable-pixmap") if acceptMode == QFileDialog.AcceptMode.AcceptSave: dialog.setDefaultSuffix("ppm") def open(self): """Open file""" dialog = QFileDialog(self, self.tr("Open File")) self.initializeImageFileDialog(dialog, QFileDialog.AcceptMode.AcceptOpen) dialog.exec() if dialog.accepted: self.loadFile(dialog.selectedFiles()[0]) def saveAs(self): """Save file""" dialog = QFileDialog(self, self.tr("Save File As")) self.initializeImageFileDialog(dialog, QFileDialog.AcceptMode.AcceptSave) dialog.exec() if dialog.accepted: self.saveFile(dialog.selectedFiles()[0]) def copy(self): """Copy image""" QGuiApplication.clipboard().setImage(self.image) def clipboardImage(self) -> QImage: """Clipboard Image""" mimeData: QMimeData = QGuiApplication.clipboard().mimeData() if mimeData: if mimeData.hasImage(): image: QImage = QImage(mimeData.imageData()) if not image.isNull(): return image return QImage() def paste(self): """Paste image.""" newImage: QImage = self.clipboardImage() if newImage.isNull(): self.statusBar().showMessage(self.tr("No image in clipboard")) else: self.setImage(newImage) self.setWindowFilePath(str('')) message: str = self.tr( f"Obtained image from clipboard, {newImage.width()}x{newImage.height()}, Depth: {newImage.depth()}") self.statusBar().showMessage(message) def zoomIn(self): """Zoom in""" self.scaleImage(1.25) def zoomOut(self): """Zoom out""" self.scaleImage(0.8) def normalSize(self): """Normalize Size""" self.image_label.adjustSize() self.scaleFactor = 1.0 def fitToWindow(self): """Fit to window""" fit: bool = self.fitToWindowAct.isChecked() print(fit) self.scroll_area.setWidgetResizable(fit) if not fit: self.normalSize() self.updateActions() def applyTestFilter(self): """Apply test filter""" self.dataResult.clear() for i in range(0, self.height()): row = [[]] for j in range(0, self.width()): pixel = [] for k in range(0, self.nbRealChannels): print(self.nbChannels) print(i, j, k) pixel.append(255 - int(self.data[i][j][k])) if self.nbRealChannels < self.nbChannels: pixel.append(255) row.append(pixel) self.dataResult.append(row) imageResult = QImage(width=self.width(), height=self.height(), format=self.image.format()) if self.nbChannels > 1: for i in range(0, self.image.height()): for j in range(0, self.image.width()): imageResult.setPixel( j, i, qRgba(self.dataResult[i][j][0], self.dataResult[i][j][1], self.dataResult[i][j][2], self.dataResult[i][j][3])) else: if self.image.bitPlaneCount() == 8: for i in range(0, self.image.height()): for j in range(0, self.image.width()): imageResult.setPixel( j, i, qRgba(self.dataResult[i][j][0], self.dataResult[i][j][0], self.dataResult[i][j][0], 255)) else: imageResult.setColorCount(2) imageResult.setColor(0, qRgba(0, 0, 0, 255)) imageResult.setColor(1, qRgba(255, 0, 0, 0)) for i in range(0, self.image.height()): for j in range(0, self.image.width()): if self.dataResult[i][j][0] == 0: imageResult.setPixel(j, i, 0) else: imageResult.setPixel(j, i, 1) self.setImage(imageResult) def updateActions(self): """update actions""" self.saveAsAct.setEnabled(not self.image.isNull()) self.copyAct.setEnabled(not self.image.isNull()) self.zoomInAct.setEnabled(not self.fitToWindowAct.isChecked()) self.zoomOutAct.setEnabled(not self.fitToWindowAct.isChecked()) self.normalSizeAct.setEnabled(not self.fitToWindowAct.isChecked()) def createActions(self): """Create actions.""" self.openAct = QAction(self.tr("&Open..."), self) self.openAct.triggered.connect(self.open) self.openAct.setShortcut(QKeySequence.StandardKey.Open) self.fileMenu.addAction(self.openAct) self.saveAsAct = QAction(self.tr("&Save As..."), self) self.saveAsAct.triggered.connect(self.saveAs) self.saveAsAct.setShortcut(self.tr("Shift+S")) self.fileMenu.addAction(self.saveAsAct) self.fileMenu.addSeparator() self.exitAct = QAction(self.tr("&Exit"), self) self.exitAct.triggered.connect(QWidget.close) self.exitAct.setShortcut(self.tr("Ctrl+Q")) self.fileMenu.addAction(self.exitAct) self.copyAct = QAction(self.tr("&Copy"), self) self.copyAct.triggered.connect(self.copy) self.copyAct.setShortcut(QKeySequence.StandardKey.Copy) self.editMenu.addAction(self.copyAct) self.pasteAct = QAction(self.tr("&Paste"), self) self.pasteAct.triggered.connect(self.paste) self.pasteAct.setShortcut(QKeySequence.StandardKey.Paste) self.editMenu.addAction(self.pasteAct) self.zoomInAct = QAction(self.tr("&Zoom In (25%)"), self) self.zoomInAct.triggered.connect(self.zoomIn) self.zoomInAct.setShortcut(QKeySequence.StandardKey.ZoomIn) self.viewMenu.addAction(self.zoomInAct) self.zoomOutAct = QAction(self.tr("&Zoom Out (25%)"), self) self.zoomOutAct.triggered.connect(self.zoomOut) self.zoomOutAct.setShortcut(QKeySequence.StandardKey.ZoomOut) self.viewMenu.addAction(self.zoomOutAct) self.normalSizeAct = QAction(self.tr("&Normal Size"), self) self.normalSizeAct.triggered.connect(self.normalSize) self.normalSizeAct.setShortcut(self.tr("Ctrl+N")) self.viewMenu.addAction(self.normalSizeAct) self.fitToWindowAct = QAction(self.tr("&Fit to Window"), self) self.fitToWindowAct.triggered.connect(self.fitToWindow) self.fitToWindowAct.setShortcut(self.tr("Ctrl+F")) self.viewMenu.addAction(self.fitToWindowAct) self.testFilterAct = QAction(self.tr("&TestFilter"), self) self.testFilterAct.triggered.connect(self.applyTestFilter) self.filtersMenu.addAction(self.testFilterAct) self.aboutAct = QAction(self.tr("&About"), self) self.aboutAct.triggered.connect(self.about) self.aboutAct.setShortcut(self.tr("Ctrl+I")) self.helpMenu.addAction(self.aboutAct) self.fitToWindowAct.setCheckable(True) self.filtersMenu.setDisabled(True) # self.testFilter.setDisabled(True) self.saveAsAct.setEnabled(False) self.copyAct.setEnabled(False) self.zoomInAct.setEnabled(False) self.zoomOutAct.setEnabled(False) self.normalSizeAct.setEnabled(False) self.fitToWindowAct.setEnabled(False) self.aboutAct.setEnabled(True)