Example #1
0
 def image(self):
     if self._index < 0:
         return QImage()
     data = self.test_data[self._index]
     data *= 255
     img = QImage(data.astype(np.uint8), 28, 28, QImage.Format_Grayscale8)
     img.invertPixels()
     return img
Example #2
0
 def proper_display(window: FullscreenWindow):
     mat = Matrix(window.staticZoom, window.staticZoom)
     page_pix = return_page(window.file, window.currentPage).getPixmap(
         matrix=mat, alpha=False)
     fmt = QImage.Format_RGBA8888 if page_pix.alpha else QImage.Format_RGB888
     qt_img = QImage(page_pix.samples, page_pix.width, page_pix.height, page_pix.stride, fmt)
     if window.invertImage:
         qt_img.invertPixels(QImage.InvertRgba)
     pixmap = QPixmap(QPixmap.fromImage(qt_img))
     window.mainLabel.setPixmap(pixmap)
 def proper_display(window: ReaderWindowInteractivity):
     mat = Matrix(window.static_zoom, window.static_zoom)
     page_pix = return_page(window.file,
                            window.current_page).getPixmap(matrix=mat,
                                                           alpha=False)
     fmt = QImage.Format_RGBA8888 if page_pix.alpha else QImage.Format_RGB888
     qt_img = QImage(page_pix.samples, page_pix.width, page_pix.height,
                     page_pix.stride, fmt)
     if window.invert_image:
         qt_img.invertPixels(QImage.InvertRgba)
     pixmap = QPixmap(QPixmap.fromImage(qt_img))
     window.content.setPixmap(pixmap)
Example #4
0
 def zoom_out(self, window: FullscreenWindow):
     if self.currentMatrixZoom > self.minMatrixZoom:
         self.currentMatrixZoom -= 0.1
         page_pix = return_page(window.file, window.currentPage).getPixmap(
             matrix=Matrix(self.currentMatrixZoom, self.currentMatrixZoom))
         fmt = QImage.Format_RGBA8888 if page_pix.alpha else QImage.Format_RGB888
         qt_img = QImage(page_pix.samples, page_pix.width, page_pix.height, page_pix.stride, fmt)
         if window.invertImage:
             qt_img.invertPixels(QImage.InvertRgba)
         pixmap = QPixmap(QPixmap.fromImage(qt_img))
         window.mainLabel.setPixmap(pixmap)
         window.staticZoom = self.currentMatrixZoom
 def zoom_out(self, window: ReaderWindowInteractivity):
     if self.currentMatrixZoom > self.minMatrixZoom:
         self.currentMatrixZoom -= 0.25
         page_pix = return_page(window.file, window.current_page).getPixmap(
             matrix=Matrix(self.currentMatrixZoom, self.currentMatrixZoom))
         fmt = QImage.Format_RGBA8888 if page_pix.alpha else QImage.Format_RGB888
         qt_img = QImage(page_pix.samples, page_pix.width, page_pix.height,
                         page_pix.stride, fmt)
         if window.invert_image:
             qt_img.invertPixels(QImage.InvertRgba)
         pixmap = QPixmap(QPixmap.fromImage(qt_img))
         window.content.setPixmap(pixmap)
         window.static_zoom = self.currentMatrixZoom
Example #6
0
class PopupPaletteFrame(QDialog):
    def __init__(self,
                 parent=None,
                 f=Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint):
        super(PopupPaletteFrame, self).__init__(parent)
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.setAttribute(Qt.WA_NoSystemBackground, True)
        self.setAttribute(Qt.WA_TranslucentBackground, True)

        self.setGeometry(0, 0, 512, 512)

        self.offset = QPoint(0, 0)

        self.local_image = QImage(512, 512, QImage.Format_ARGB32)
        self.fgcolor = '#FFFFFF'
        self.bgcolor = '#000000'

        # This is how to load the merged image from a .kra file.
        ## import zipfile
        ## archive = zipfile.ZipFile(gImgDir + os.sep + 'myPopupPaletteWorkshop.kra', 'r')
        ## img_data = archive.read('mergedimage.png')
        ## img = QImage()
        ## img.loadFromData(img_data)
        ## self.texture = img

        self.texture = QImage(gImgDir + os.sep +
                              'colorwheel_pixel_palette.png')
        self.defaultblackwhiteImg = QImage(gImgDir + os.sep +
                                           'defaultblackwhite10.png')
        self.switcharrowsImg = QImage(gImgDir + os.sep + 'switcharrows16.png')
        self.colorboxImg = QImage(gImgDir + os.sep + 'colorbox28.png')

        lHiColorDir = gShareDir + os.sep + 'icons' + os.sep + 'hicolor'

        # Text box.
        self.textbox = QTextEdit("Test QTextEdit", parent=self)
        self.textbox.move(192, 160)
        self.textbox.setGeometry(192, 160, 128, 32)

        # Big Button in middle.
        self.btnImage = btnImage = QImage(lHiColorDir + os.sep + '128x128' +
                                          os.sep + 'apps' + os.sep +
                                          'calligrakrita.png')
        self.button = ImageButton(btnImage, btnImage, btnImage, self)
        self.button.move(256 - 64, 256 - 64)
        self.button.pressed.connect(self.buttonPressed)
        self.button.released.connect(self.buttonReleased)
        self.button.setToolTip('Right Click for Context Menu')
        self.button.setContextMenuPolicy(Qt.CustomContextMenu)
        self.button.customContextMenuRequested.connect(self.show_context_menu)

        # Create popup context menu.
        self._context_menu = QMenu(self)
        self._context_menu.addAction('Invert', self.invertTexture)
        self._context_menu.addAction('Mirror Horizontal',
                                     self.mirrorTextureHorizontal)
        self._context_menu.addAction('Mirror Vertical',
                                     self.mirrorTextureVertical)

        # 4 smaller buttons.
        self.btnImage1 = btnImage1 = QImage(lHiColorDir + os.sep + '32x32' +
                                            os.sep + 'apps' + os.sep +
                                            'calligrakrita.png')
        self.button1 = ImageButton(btnImage1, btnImage1, btnImage1, self)
        self.button1.move(192, 320)
        self.button1.released.connect(self.button1Released)
        self.button1.setToolTip('test button')

        self.btnImage2 = btnImage2 = QImage(lHiColorDir + os.sep + '32x32' +
                                            os.sep + 'apps' + os.sep +
                                            'calligrakrita.png')
        self.button2 = ImageButton(btnImage2, btnImage2, btnImage2, self)
        self.button2.move(224, 320)
        self.button2.released.connect(self.button2Released)
        self.button2.setToolTip('rotate_canvas_left')

        self.btnImage3 = btnImage3 = QImage(lHiColorDir + os.sep + '32x32' +
                                            os.sep + 'apps' + os.sep +
                                            'calligrakrita.png')
        self.button3 = ImageButton(btnImage3, btnImage3, btnImage3, self)
        self.button3.move(256, 320)
        self.button3.released.connect(self.button3Released)
        self.button3.setToolTip('rotate_canvas_right')

        self.btnImage4 = btnImage4 = QImage(lHiColorDir + os.sep + '32x32' +
                                            os.sep + 'apps' + os.sep +
                                            'calligrakrita.png')
        self.button4 = ImageButton(btnImage4, btnImage4, btnImage4, self)
        self.button4.move(288, 320)
        self.button4.released.connect(self.button4Released)
        self.button4.setToolTip('Im a painter button')

        # Fore/Back color stuff
        self.arrowsBtn = ImageButton(self.switcharrowsImg,
                                     self.switcharrowsImg,
                                     self.switcharrowsImg,
                                     parent=self)
        self.arrowsBtn.move(464, 453)
        self.arrowsBtn.pressed.connect(self.swapFG_BG_Colors)

        self.defblackwhiteBtn = ImageButton(self.defaultblackwhiteImg,
                                            self.defaultblackwhiteImg,
                                            self.defaultblackwhiteImg,
                                            parent=self)
        self.defblackwhiteBtn.move(436, 486)
        self.defblackwhiteBtn.pressed.connect(self.defaultFG_BG_Colors)

        if not krita:
            quitAction = QAction("E&xit",
                                 self,
                                 shortcut="Z",
                                 triggered=self.close)
            self.button.addAction(quitAction)
            self.button.setContextMenuPolicy(Qt.ActionsContextMenu)

        # Mask - Anything not black (0, 0, 0, 0-255) in this image will not be shown when drawn out.
        self.setMask(QBitmap(gImgDir + os.sep + "pixel_palette_mask512.png"))
        self.drawGUI()

        self.setFocusPolicy(Qt.StrongFocus)
        self.setFocus()

        self.setMouseTracking(True)

    def __del__(self):
        print('__del__ %s' % self.__class__.__name__)

    def mirrorTextureHorizontal(self):
        self.texture = self.texture.mirrored(horizontal=True, vertical=False)
        self.drawGUI()
        self.update()

    def mirrorTextureVertical(self):
        self.texture = self.texture.mirrored(horizontal=False, vertical=True)
        self.drawGUI()
        self.update()

    def invertTexture(self):
        self.texture.invertPixels()
        self.drawGUI()
        self.update()

    def swapFG_BG_Colors(self):
        self.fgcolor, self.bgcolor = self.bgcolor, self.fgcolor
        self.drawGUI()
        self.update()

    def defaultFG_BG_Colors(self):
        self.fgcolor = '#FFFFFF'
        self.bgcolor = '#000000'
        self.drawGUI()
        self.update()

    def show_context_menu(self, point):
        self._context_menu.exec_(self.button.mapToGlobal(point))

    def buttonPressed(self):
        pass

    def buttonReleased(self):
        QApplication.beep()

    def button1Released(self):
        QMessageBox.information(
            QWidget(self), i18n("Test Btn1"),
            i18n("Hello! This is Krita version %s\n\nwidth %s height %s") %
            (Application.version(), self.width(), self.height()))

    def button2Released(self):
        krita.Krita.instance().action('rotate_canvas_left').trigger()

    def button3Released(self):
        krita.Krita.instance().action('rotate_canvas_right').trigger()

    def button4Released(self):
        QMessageBox.information(
            QWidget(self), i18n("Test Btn4"),
            i18n(
                '"The position of the artist is humble. He is essentially a channel." Piet Mondrian'
            ))

    def saveData(self):
        with open(gFileDir + os.sep + 'test.txt', 'w') as f:
            f.write('I Closed Cleanly and wrote out some data')

    def closeEvent(self, event):
        self.saveData()
        event.accept()

    def sizeHint(self):
        return QSize(512, 512)

    def drawGUI(self):
        # Draw everything to an image first so we can have a local copy
        # available for pixel RGBA access.
        qimage = QImage(QSize(512, 512), QImage.Format_ARGB32)
        qp = QPainter(qimage)
        qp.setRenderHint(QPainter.Antialiasing)
        qp.setPen(Qt.NoPen)

        # Draw the texture.
        qp.drawImage(0, 0, self.texture)

        # Draw the graydient bar
        myQRect = QRect(497, 1, 14, 510)
        gradient = QLinearGradient(0, 0, 0, 512)
        gradient.setColorAt(0, QColor(self.fgcolor))
        gradient.setColorAt(1, QColor(self.bgcolor))
        qp.fillRect(myQRect, gradient)
        qp.setPen(QPen(QColor(0, 0, 0, 255), 1))
        qp.setBrush(QBrush(QColor(0, 0, 0, 0)))
        qp.drawRect(497, 1, 14, 510)

        # Draw the fore/back color boxes
        qp.setPen(Qt.NoPen)
        qp.drawImage(464, 453, self.switcharrowsImg)
        qp.drawImage(436, 486, self.defaultblackwhiteImg)
        qp.setBrush(QColor(self.bgcolor))
        qp.drawRect(452, 468, 28, 28)
        qp.drawImage(452, 468, self.colorboxImg)
        qp.setBrush(QColor(self.fgcolor))
        qp.drawRect(436, 452, 28, 28)
        qp.drawImage(436, 452, self.colorboxImg)

        # Save a copy of the finished image locally.
        self.local_image = qimage

    def paintEvent(self, event):
        # Draw the completed image to the widget/window frame.
        qp = QPainter()
        qp.begin(self)
        qp.drawImage(0, 0, self.local_image)
        qp.end()

    def updateKritaForeGroundColor(self, color):
        instance = krita.Krita.instance()
        views = instance.views()
        view0 = views[0]

        managedColor = krita.ManagedColor("RGBA", "U8", "")
        colorComponents = managedColor.components()
        colorComponents[0] = color.blueF()  # b
        colorComponents[1] = color.greenF()  # g
        colorComponents[2] = color.redF()  # r
        colorComponents[3] = color.alphaF()  # a
        managedColor.setComponents(colorComponents)

        view0.setForeGroundColor(managedColor)

    def updateKritaBackGroundColor(self, color):
        instance = krita.Krita.instance()
        views = instance.views()
        view0 = views[0]

        managedColor = krita.ManagedColor("RGBA", "U8", "")
        colorComponents = managedColor.components()
        colorComponents[0] = color.blueF()  # b
        colorComponents[1] = color.greenF()  # g
        colorComponents[2] = color.redF()  # r
        colorComponents[3] = color.alphaF()  # a
        managedColor.setComponents(colorComponents)

        view0.setBackGroundColor(managedColor)

    def mousePressEvent(self, event):
        if event.button() == Qt.MiddleButton:
            self.offset = event.globalPos() - self.frameGeometry().topLeft()
            ## print(self.offset)
        elif event.button() == Qt.LeftButton:
            position = QPoint(event.pos().x(), event.pos().y())
            color = QColor.fromRgb(self.local_image.pixel(position))
            self.fgcolor = color
            ## print(color.redF(), color.greenF(), color.blueF(), color.alphaF())  # print values from a QColor
            if krita:
                self.updateKritaForeGroundColor(color)
            self.drawGUI()
            self.update()  # Refresh the drawn colors.
        elif event.button() == Qt.RightButton:
            position = QPoint(event.pos().x(), event.pos().y())
            color = QColor.fromRgb(self.local_image.pixel(position))
            self.bgcolor = color
            ## print(color.redF(), color.greenF(), color.blueF(), color.alphaF())  # print values from a QColor
            if krita:
                self.updateKritaBackGroundColor(color)
            self.drawGUI()
            self.update()  # Refresh the drawn colors.
        elif event.buttons() == Qt.XButton1:
            QApplication.beep()
            # Do something here... Change a texture/mask/page, call a function, etc....
        elif event.buttons() == Qt.XButton2:
            QApplication.beep()
            # Do something here... Change a texture/mask/page, call a function, etc....

    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.MiddleButton:
            self.move(event.globalPos() - self.offset)
            event.accept()
        self.textbox.setPlainText('x = %s, y = %s' % (event.x(), event.y()))

    ## def wheelEvent(self, event):
    ##     event.accept()

    def enterEvent(self, event):
        self.update()
        ## QApplication.setOverrideCursor(QCursor(Qt.ArrowCursor))
        QApplication.setOverrideCursor(
            QCursor(QPixmap(gImgDir + os.sep +
                            'paperairplane_arrow_white24.png'),
                    hotX=0,
                    hotY=0))

    def leaveEvent(self, event):
        self.update()
        QApplication.restoreOverrideCursor()

    def event(self, event):
        if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Tab:
            ## print("Tab pressed")
            if krita:
                krita.Krita.instance().action(
                    'view_show_canvas_only').trigger()
            return False
        return QWidget.event(self, event)
Example #7
0
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        # self.image = QImage()
        self.dirty = True
        self.filename = None
        self.recentFiles = []
        self.image = None
        self.image_hist = None
        self.cv_img = None
        self.cv_img_gray = None
        self.cv_blur0 = None
        self.cv_blur1 = None
        self.cv_blur2 = None
        self.cv_blur3 = None
        self.cv_blur4 = None
        self.cv_blur5 = None
        self.cv_blur6 = None
        self.vhf = None
        self.hf = None
        self.mhf = None
        self.mf = None
        self.mlf = None
        self.lf = None
        self.vlf = None
        self.vhf_n = None
        self.hf_n = None
        self.mhf_n = None
        self.mf_n = None
        self.mlf_n = None
        self.lf_n = None

        self.mirroredvertically = False
        self.mirroredhorizontally = False

        self.image_label = QLabel()
        self.image_label.setMinimumSize(200, 200)
        self.image_label.setAlignment(Qt.AlignCenter)
        self.image_label.setContextMenuPolicy(Qt.ActionsContextMenu)
        self.setCentralWidget(self.image_label)

        hist_dock_widget = QDockWidget("Histogram", self)
        hist_dock_widget.setObjectName("histDockWidget")
        hist_dock_widget.setAllowedAreas(Qt.LeftDockWidgetArea
                                         | Qt.RightDockWidgetArea)
        self.hist_label = QLabel()
        hist_dock_widget.setWidget(self.hist_label)
        self.addDockWidget(Qt.RightDockWidgetArea, hist_dock_widget)

        # setup normal adjustment sliders
        self.Normal_Dialog = QDialog()
        self.n_dlg = Ui_NormalDialog()
        self.n_dlg.setupUi(self.Normal_Dialog)
        self.n_dlg.hs_vhf.valueChanged.connect(self.normal_change_value)
        self.n_dlg.hs_hf.valueChanged.connect(self.normal_change_value)
        self.n_dlg.hs_mhf.valueChanged.connect(self.normal_change_value)
        self.n_dlg.hs_mf.valueChanged.connect(self.normal_change_value)
        self.n_dlg.hs_mlf.valueChanged.connect(self.normal_change_value)
        self.n_dlg.hs_lf.valueChanged.connect(self.normal_change_value)
        self.n_dlg.hs_vlf.valueChanged.connect(self.normal_change_value)

        self.printer = None

        self.size_label = QLabel()
        self.size_label.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
        status = self.statusBar()
        status.setSizeGripEnabled(False)
        status.addPermanentWidget(self.size_label)
        status.showMessage("Ready", 5000)

        # file menu
        actionNew = self.createAction("&New", "self.file_new",
                                      QKeySequence.New, "filenew",
                                      "Create a new image")
        actionOpen = self.createAction("&Open", "self.file_open",
                                       QKeySequence.Open, "fileopen",
                                       "Open an existing image")
        actionSave = self.createAction("&Save", "self.file_save",
                                       QKeySequence.Save, "filesave",
                                       "Open an existing image")
        actionSaveAs = self.createAction("Save &As", "self.file_save_as",
                                         "Ctrl+Shift+S", "filesaveas",
                                         "Save image under a different name")
        actionPrint = self.createAction("&Print", "self.file_print",
                                        QKeySequence.Print, "fileprint",
                                        "Print the image")
        actionExit = self.createAction("&Exit", "self.close", "Ctrl+Q",
                                       "filequit", "Exit the program")

        # edit menu
        actionInvert = self.createAction("&Invert", "self.edit_invert",
                                         "Ctrl+I", "editinvert",
                                         "Invert the image", True, "toggled")
        actionSwap = self.createAction("&Swap", "self.edit_swap", "Ctrl+A",
                                       "editswap", "Swap Red and Blue", True,
                                       "toggled")
        actionZoom = self.createAction("&Zoom", "self.edit_zoom", "Alt+Z",
                                       "editzoom", "zoom image")
        actionUnmirror = self.createAction("&UnMirror", "self.edit_un_mirror",
                                           "Ctrl+U", "editunmirror",
                                           "Un-mirror the image", True,
                                           "toggled")
        actionUnmirror.setChecked(True)
        actionMirrorH = self.createAction("Mirror &Horizontally",
                                          "self.edit_mirror_h", "Ctrl+H",
                                          "editmirrorhoriz",
                                          "Mirror the image horizontally",
                                          True, "toggled")
        actionMirrorV = self.createAction("Mirror &Vertically",
                                          "self.edit_mirror_v", "Ctrl+V",
                                          "editmirrorvert",
                                          "Mirror the image vertically", True,
                                          "toggled")

        actionBlur = self.createAction("Blur Image", "self.edit_blur",
                                       "Ctrl+B", "editblur",
                                       "Create four levels of frequency",
                                       False)

        # Help menu
        actionAbout = self.createAction("&About", "self.help_about", None,
                                        None, "About this program")
        actionHelp = self.createAction("&Help", "self.help_help", None, None,
                                       "About editing commands")

        # normal menu
        actionNormal = self.createAction("&Make", "self.normal_make", "Ctrl+M",
                                         "", "Make normal for image")
        actionAdjust = self.createAction("&Adjust", "self.normal_adjust",
                                         "Ctrl+A", "", "Adjust frequencies")
        # actionSwap = self.createAction("&Swap", "self.edit_swap", "Ctrl+A", "editswap", "Swap Red and Blue",
        #                                True, "toggled")
        # actionZoom = self.createAction("&Zoom", "self.edit_zoom", "Alt+Z", "editzoom", "zoom image")
        # actionUnmirror = self.createAction("&UnMirror", "self.edit_un_mirror", "Ctrl+U", "editunmirror",
        #                                    "Un-mirror the image", True, "toggled")
        # actionUnmirror.setChecked(True)
        # actionMirrorH = self.createAction("Mirror &Horizontally", "self.edit_mirror_h", "Ctrl+H", "editmirrorhoriz",
        #                                   "Mirror the image horizontally", True, "toggled")
        # actionMirrorV = self.createAction("Mirror &Vertically", "self.edit_mirror_v", "Ctrl+V", "editmirrorvert",
        #                                   "Mirror the image vertically", True, "toggled")
        #
        # actionBlur = self.createAction("Blur Image", "self.edit_blur", "Ctrl+B", "editblur",
        #                                "Create four levels of frequency", False)

        # setup menus
        self.menuFile = self.menuBar().addMenu("&File")
        self.menuFileActions = (actionNew, actionOpen, actionSave,
                                actionSaveAs, None, actionPrint, None,
                                actionExit)
        self.addActions(self.menuFile, self.menuFileActions)

        menuEdit = self.menuBar().addMenu("&Edit")
        self.addActions(menuEdit,
                        (actionInvert, actionSwap, actionBlur, actionZoom))

        mirror_group = QActionGroup(self)
        mirror_group.addAction(actionUnmirror)
        mirror_group.addAction(actionMirrorH)
        mirror_group.addAction(actionMirrorV)

        mirrorMenu = menuEdit.addMenu(QIcon(":/editmirror.png"), "&Mirror")
        self.addActions(mirrorMenu,
                        (actionUnmirror, actionMirrorH, actionMirrorV))

        # Normal menu
        self.menuNormal = self.menuBar().addMenu("&Normal")
        self.addActions(self.menuNormal, (actionNormal, ))
        self.addActions(self.menuNormal, (actionAdjust, ))

        # Help menu
        menuHelp = self.menuBar().addMenu("&Help")
        self.addActions(menuHelp, (actionAbout, actionHelp))

        # toolbars
        fileToolbar = self.addToolBar("File")
        fileToolbar.setObjectName("FileToolBar")
        self.addActions(fileToolbar, (actionNew, actionOpen, actionSaveAs))

        editToolbar = self.addToolBar("Edit")
        editToolbar.setObjectName("EditToolBar")
        self.addActions(editToolbar,
                        (actionInvert, actionSwap, None, actionUnmirror,
                         actionMirrorH, actionMirrorV))
        self.zoomSpinBox = QSpinBox()
        self.zoomSpinBox.setRange(1, 400)
        self.zoomSpinBox.setSuffix(" %")
        self.zoomSpinBox.setValue(100)
        self.zoomSpinBox.setToolTip("Zoom the image")
        self.zoomSpinBox.setStatusTip(self.zoomSpinBox.toolTip())
        self.zoomSpinBox.setFocusPolicy(Qt.NoFocus)
        self.zoomSpinBox.valueChanged.connect(self.show_image)
        editToolbar.addWidget(self.zoomSpinBox)

        # setup context menu for image
        separator = QAction(self)
        separator.setSeparator(True)
        self.addActions(self.image_label,
                        (actionInvert, actionSwap, separator, actionUnmirror,
                         actionMirrorH, actionMirrorV))

        # setup reset-able actions
        self.resetableActions = ((actionInvert, False), (actionSwap, False),
                                 (actionUnmirror, True))

        self.settings = QSettings("Kerr & Associates", "Image Changer")
        self.restoreGeometry(self.settings.value("geometry", ""))
        self.restoreState(self.settings.value("windowState", ""))
        self.recentFiles = self.settings.value("RecentFiles", [])
        print(self.recentFiles)
        self.setWindowTitle("Image Changer")

        self.update_file_menu()
        QTimer.singleShot(0, self.load_initial_file)

    # -------------------------------------------------------------------

    def createAction(self,
                     text,
                     slot=None,
                     shortcut=None,
                     icon=None,
                     tip=None,
                     checkable=False,
                     signal="triggered"):
        print("inside createAction")
        """ Helper method to create actions """
        action = QAction(text, self)
        if icon is not None:
            action.setIcon(QIcon(":/%s.png" % icon))
        if shortcut is not None:
            action.setShortcut(shortcut)
        if tip is not None:
            action.setToolTip(tip)
            action.setStatusTip(tip)
        if slot is not None:
            slot_string = "action." + signal + ".connect(" + slot + ")"
            exec(slot_string)
        if checkable:
            action.setCheckable(True)
        return action

    def addActions(self, target, actions):
        """ Helper method to add list of actions to menus """
        for action in actions:
            if action is None:
                target.addSeparator()
            else:
                target.addAction(action)

    def update_file_menu(self):
        print("inside update_file_menu")
        self.menuFile.clear()
        self.addActions(self.menuFile, self.menuFileActions[:-1])
        current = self.filename if self.filename is not None else None
        recent_files = []
        print("self.recentFiles: ", self.recentFiles, "\ncurrent:", current)
        for fname in self.recentFiles:
            if fname != current and QFile.exists(fname):
                recent_files.append(fname)
        if recent_files:
            self.menuFile.addSeparator()
            for i, fname in enumerate(recent_files):
                action = QAction(
                    QIcon(":/icon.png"),
                    "&%s %s" % (str(i + 1), QFileInfo(fname).fileName()), self)
                action.setData(fname)
                action.triggered.connect(self.load_file)
                self.menuFile.addAction(action)
        self.menuFile.addSeparator()
        self.menuFile.addAction(self.menuFileActions[-1])

    def file_new(self):
        print("inside file new")
        if not self.ok_continue():
            return
        dialog = QDialog()
        dialog_ui = Ui_new_image_dlg()
        dialog_ui.setupUi(dialog)
        col = QColor(64, 64, 64)
        dialog_ui.color_label_pic.pixmap = QImage().fill(col)
        dialog_ui.color_pushButton.clicked.connect(self.show_color_dlg)
        dialog.show()
        dialog.exec_()

        if dialog.exec_():
            print("we got here!!!")
            self.add_recent_file(self.filename)
            self.image = QImage()
            for action, check in self.resetableActions:
                action.setChecked(check)
                dialog_ui.color_label_pic = QImage()
                dialog_ui.color_label_pic.fill(QColor(255, 15, 15))
                self.image = dialog_ui.color_label_pic
                self.filename = None
                self.dirty = True
                self.show_image()
                self.size_label.setText(
                    "%d x %d" % (self.image.width(), self.image.height()))
                self.update_status("Created new image")

    def file_open(self):
        """ Select the file name to be opened """
        if not self.ok_continue():
            return
        dir = os.path.dirname(self.filename)\
            if self.filename is not None else "../images"
        formats = [
            "Images PNG (*.png);;Jpeg (*.jpg *.jpeg);;Targa (*.tga);;SVG (*.svg)"
        ]
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        fname, mask = QFileDialog.getOpenFileName(
            self,
            caption="Choose image file to open",
            directory=dir,
            filter=formats[0],
            options=options)
        if fname:
            self.load_file(fname=fname)

    def file_save(self):
        """ if named, save the file, otherwise get a name then save """
        if self.image.isNull():
            return
        if self.filename is None:
            self.file_save_as()
        else:
            if self.image.save(self.filename, None):
                self.update_status(f"Saved as {self.filename}")
                self.dirty = False
            else:
                self.update_status(f"Failed to save {self.filename}")
        return

    def file_save_as(self):
        """ assign a new filename to save under. """
        if self.image.isNull():
            return
        fname = self.filename if self.filename is not None else "."
        # formats = ['*.bmp', '*.cur', '*.icns', '*.ico', '*.jpeg', '*.jpg', '*.pbm', '*.pgm',
        #            '*.png', '*.ppm', '*.tif', '*.tiff', '*.wbmp', '*.webp', '*.xbm', '*.xpm']

        formats = [
            "Images PNG (*.png);;Jpeg (*.jpg *.jpeg);;Targa (*.tga);;SVG (*.svg)"
        ]
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        fname, mask = QFileDialog.getSaveFileName(
            None,
            caption="Choose name to save as",
            directory=fname,
            filter=formats[0],
            options=options)
        print(fname, mask)
        if fname:
            if "." not in fname:
                fname += ".png"
            self.add_recent_file(fname)
            self.filename = fname
            self.file_save()

    def file_print(self):
        print("filePrint - Not Implemented yet")
        return

    def edit_invert(self, on):
        """ inverts the image channels"""
        if self.image.isNull():
            return
        self.image.invertPixels()
        self.show_image()
        self.dirty = True
        self.update_status("Inverted" if on else "Un-inverted")
        print("Invert image")

    def edit_swap(self, swapped):
        """ swaps red and blue channels"""
        if self.image.isNull():
            return
        self.image = self.image.rgbSwapped()
        self.show_image()
        self.dirty = True
        self.update_status(
            "Blue, Green, Red" if swapped else "Red, Green, Blue")
        print("editSwap not implemented")

    def edit_zoom(self):
        if self.image.isNull():
            return
        percent, ok = QInputDialog.getInt(None,
                                          "Image Changer - Zoom", "Percent: ",
                                          self.zoomSpinBox.value(), 1, 400)
        if ok:
            self.zoomSpinBox.setValue(percent)
        print("OK: ", ok)
        return

    def edit_un_mirror(self, on):
        print(on, "On")
        print("edit_un_mirror called")
        print(self.mirroredhorizontally)
        print('vertically', self.mirroredvertically)
        return
        # if self.image.isNull():
        #     return
        # if self.mirroredhorizontally:
        #     self.edit_mirror_h(False)
        # if self.mirroredvertically:
        #     self.edit_mirror_v(False)

    def edit_mirror_h(self, on):
        if self.image.isNull():
            return
        self.image = self.image.mirrored(True, False)
        self.show_image()
        self.mirroredhorizontally = not self.mirroredhorizontally
        print("h", self.mirroredhorizontally)
        self.dirty = True
        self.update_status(
            "Mirrored Horizontally" if on else "Un-mirrored Horizontally")

    def edit_mirror_v(self, on):
        if self.image.isNull():
            return
        self.image = self.image.mirrored(False, True)
        self.show_image()
        self.mirroredvertically = not self.mirroredvertically
        print("v", self.mirroredvertically)
        self.dirty = True
        self.update_status(
            "Mirrored Vertically" if on else "Un-mirrored Vertically")

    def edit_blur(self):
        self.cv_blur0 = self.cv_img.copy()
        print(self.cv_blur0.shape)
        i = 149
        cv2.bilateralFilter(self.cv_img, i, i * 2, i / 2, self.cv_blur0,
                            cv2.BORDER_WRAP)
        # create a Contrast Limited Adaptive Histogram Equalization object
        # clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(4, 4))
        # cl1 = clahe.apply(self.cv_blur0)
        #
        # cv2.imshow('clahe_2.jpg', cl1)
        cv2.imwrite("..\\images\\blurred_normal.png", self.cv_blur0)
        cv2.imshow('blurred image', self.cv_blur0)
        # cv2.imshow('equalized', cl1)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

    def normal_adjust(self):
        if self.image.isNull():
            return
        self.Normal_Dialog.show()
        self.Normal_Dialog.exec_()
        pass
        # QInputDialog.getInt(None, "Image Changer - Zoom",
        #                                   "Percent: ",
        #                                   self.zoomSpinBox.value(), 1, 400)
        # if ok:
        #     self.zoomSpinBox.setValue(percent)
        # print("OK: ", ok)
        # return

    def normal_change_value(self):
        print("normal_change_value:  not implemented")
        pass

    def help_about(self):
        QMessageBox.about(
            self, "About Image Changer", """<b>Image Changer</b> v %s
                          <p>Copyright &copy; 2019.
                          All rights reserved.
                          <p>This application can be used to perform
                          simple image manipulations.
                          <p>Python %s - Qt %s
                          - PyQt %s on %s """ %
            (__version__, platform.python_version(), QT_VERSION_STR,
             PYQT_VERSION_STR, platform.system()))
        print("about not yet implemented")
        return

    def help_help(self):
        print("Help not yet implemented")
        return

    def load_initial_file(self):
        print("inside loadInitialFile")
        settings = QSettings("Kerr & Associates", "Image Changer")
        files = settings.value('RecentFiles', [])
        fname = ''
        if files:
            fname = files[0]
        if fname and QFile.exists(fname):
            self.load_file(fname)
        print("fname: ", fname)
        print("loadInitialFile implemented")

    def load_file(self, fname=None):
        """ method to actually load the file """
        if fname is None:
            # coming from a recent file action - need to get full file name
            action = self.sender()
            print("filename is None - action: ", action, action.data())
            if isinstance(action, QAction):
                fname = action.data()
                if not self.ok_continue():
                    return
            else:
                return
        if fname:
            self.filename = None
            print(fname)
            image = QImage(fname)
            if image.isNull():
                print("image is null")
                message = "Failed to read %s" % fname
            else:
                self.add_recent_file(fname)
                self.image = QImage()
                for action, check in self.resetableActions:
                    action.setChecked(check)
                    print(action, check)
                self.image = image
                self.filename = fname
                self.create_histogram(self.filename)
                print("calling show_image")
                self.show_image()
                print("returned from show_image")
                self.dirty = False
                self.size_label.setText("%d x %d" %
                                        (image.width(), image.height()))
                print("%d x %d" % (image.width(), image.height()), self.dirty)
                message = "Loaded %s" % os.path.basename(fname)
            self.update_status(message)
        print("exiting loadFile")

    def create_histogram(self, fname):
        self.cv_img = cv2.imread(fname)
        self.cv_img_gray = cv2.imread(fname, 0)
        cv2.imshow('cv image', self.cv_img_gray)

        hist_pics = hist_lines_split(self.cv_img)
        hist_pics[3] = hist_lines(self.cv_img_gray)
        res = np.vstack(
            (hist_pics[0], hist_pics[1], hist_pics[2], hist_pics[3]))
        # cv2.imshow('histogram', res)
        # cv2.imshow('image', self.cv_img)
        self.image_hist = self.convert_cv_to_q_image(res)
        cv2.waitKey()
        cv2.destroyAllWindows()

    def convert_cv_to_q_image(self, cv_image):
        height, width, channel = cv_image.shape
        bytes_per_line = width * 3
        q_image = QImage(cv_image.data, width, height, bytes_per_line,
                         QImage.Format_RGB888).rgbSwapped()
        return q_image

    def add_recent_file(self, fname):
        if fname is None:
            print("no filename - returning")
            return
        print("self.recentFiles: ", self.recentFiles)
        print("\n\nfname: ", fname)
        if fname not in self.recentFiles:
            self.recentFiles.insert(0, fname)
            # don't allow list to contain more than 9 file names
            while len(self.recentFiles) > 9:
                self.recentFiles.pop(-1)
            print("final list of recent files: ", self.recentFiles)

    def update_status(self, message):
        """ shows status message and updates filename in screen title"""
        self.statusBar().showMessage(message, 5000)
        self.hist_label.setText(message)
        self.hist_label.setPixmap(QPixmap.fromImage(self.image_hist))

        if self.filename is not None:
            self.setWindowTitle("Image Changer - %s[*]" %
                                os.path.basename(self.filename))
        elif not self.image.isNull():
            self.setWindowTitle("Image Changer - Unnamed[*]")
        else:
            self.setWindowTitle("Image Changer[*]")
        self.setWindowModified(self.dirty)

    def show_color_dlg(self):
        print("inside show_color_dlg")
        col = QColorDialog.getColor()
        if col.isValid():
            self.dialog.setStyleSheet("QWidget | background-color: %s" %
                                      col.name())
            self.dialog.color_label_pic.pixmap = QImage().fill(col)

            print("color is valid")

    def show_image(self, percent=None):
        print("inside show_image")
        if self.image.isNull():
            print(self.image)
            return
        if percent is None:
            percent = self.zoomSpinBox.value()
            print("percent: ", percent)
        factor = percent / 100
        print("factor: ", factor)
        width = self.image.width() * factor
        height = self.image.height() * factor
        image = self.image.scaled(width, height, Qt.KeepAspectRatio)
        self.image_label.setPixmap(QPixmap.fromImage(image))

    def closeEvent(self, event):
        print("inside closeEvent")
        if self.ok_continue():
            self.settings = QSettings("Kerr & Associates", "Image Changer")
            self.settings.setValue("geometry", self.saveGeometry())
            self.settings.setValue("windowState", self.saveState())
            self.settings.setValue("RecentFiles", self.recentFiles)
            QMainWindow.closeEvent(self, event)
        else:
            event.ignore()

    def normal_make(self):
        """ Create a series of normals """
        print("initializing")
        biblurs.initialize_blurs(self)
        print("blurring images - may take a while")
        biblurs.blurs(self)
        print("creating normals")
        freq_list = [
            self.vhf, self.hf, self.mhf, self.mf, self.mlf, self.lf, self.vlf
        ]
        norm_list = []

        k_list = [3, 5, 7, 11, 25, 55, 75]
        for i, freq in enumerate(freq_list):
            print(type(freq))
            print("i:", i)
            channels = cn.process_image(freq)
            norm_list.append(channels)
            n_img_out = cn.display_normal_image(channels)
            cv2.imwrite("..\\images\\part_norm" + str(i) + ".png", n_img_out)
            n_img_out_blur = n_img_out.copy()
            cv2.GaussianBlur(n_img_out, (k_list[i], k_list[i]), k_list[i],
                             n_img_out_blur, 0)
            # cv2.bilateralFilter(n_img_out, s, s, s, n_img_out_blur, cv2.BORDER_WRAP)
            cv2.imwrite("..\\images\\a_blur" + str(i) + ".png", n_img_out_blur)
        return norm_list

    def ok_continue(self):
        print("inside ok_continue")
        if self.dirty:
            reply = QMessageBox.question(
                self, "Image Changer - Unsaved Changes",
                "Save unsaved changes?",
                QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
            if reply == QMessageBox.Cancel:
                return False
            elif reply == QMessageBox.Yes:
                self.file_save()
        print("Dirty: ", self.dirty)
        new_image = self.image.convertToFormat(4)
        return True
Example #8
0
class MainWindow(QMainWindow):
	"""docstring for MainWindow"""
	def __init__(self, parent=None):

		super(MainWindow, self).__init__(parent)
		self.image = QImage()
		self.dirty = False
		self.filename = None
		self.mirroredVertically = False
		self.mirroredHorizontally = False

		#Central Widgets
		self.imageLabel = QLabel()
		self.imageLabel.setMinimumSize(200, 200)
		self.imageLabel.setAlignment(Qt.AlignCenter)
		self.imageLabel.setContextMenuPolicy(Qt.ActionsContextMenu)
		self.setCentralWidget(self.imageLabel)

		#Docks at side
		logDockWidget =QDockWidget('Log', self)
		logDockWidget.setObjectName('LogDockWidget')
		logDockWidget.setAllowedAreas(Qt.LeftDockWidgetArea|Qt.RightDockWidgetArea)
		self.listWidget = QListWidget()
		logDockWidget.setWidget(self.listWidget)
		self.addDockWidget(Qt.RightDockWidgetArea, logDockWidget)

		self.printer = None

		#Status bar below
		self.sizeLabel = QLabel()
		self.sizeLabel.setFrameStyle(QFrame.StyledPanel|QFrame.Sunken)
		status = self.statusBar()
		status.setSizeGripEnabled(False)
		status.addPermanentWidget(self.sizeLabel)
		status.showMessage('Ready', 5000)

		#file Menu Options
		fileNewAction = self.createAction('&New...', self.fileNew, QKeySequence.New, 'filenew', 'Create a new Image File' )
		fileQuitAction = self.createAction('&Quit...', self.close, 'Ctrl + Q', 'filequit', 'Close the Application' )
		fileOpenAction = self.createAction('&Open...', self.fileOpen, QKeySequence.Open, 'fileopen', 'open an existing image file')
		fileSaveAction = self.createAction('&Save...', self.fileSave, QKeySequence.Save, 'filesave', 'save image file')
		fileSaveAsAction = self.createAction('Save &As...', self.fileSaveAs, QKeySequence.SaveAs, 'filesaveas', 'save image file using a new name')
		filePrintAction = self.createAction('&Print...', self.filePrint, QKeySequence.Print, 'fileprint', 'print image')
		#Edit Menu options
		editZoomAction = self.createAction('&Zoom', self.editZoom, 'Alt + Z', 'editzoom','zoom the image')
		editInvertAction = self.createAction('&Invert', self.editInvert, 'Ctrl + I', 'editinvert','Invert the images Color', True, 'toggled')
		editSwapRedAndBlueAction = self.createAction('S&wap red and blue', self.editSwapRedAndBlue, 'Ctrl + W', 'editswap','swap the images red and blue components', True, 'toggled')
		editResizeAction = self.createAction('&Resize', self.editResize, 'Ctrl + R', 'editresize', 'resizes the image')
		#Mirror Groups
		mirrorGroup = QActionGroup(self)
		editUnMirrorAction = self.createAction('&Unmirror', self.editUnMirror, 'Ctrl + U', 'editunmirror', 'Unmirror the image', True, 'toggled')
		mirrorGroup.addAction(editUnMirrorAction)
		editMirrorHorizontalAction = self.createAction('Mirror &Horizontally', self.editMirrorHorizontal, 'Ctrl + H', 'editmirrorhoriz', 'Horizontally Mirror the image', True, 'toggled')
		mirrorGroup.addAction(editMirrorHorizontalAction)
		editMirrorVerticalAction = self.createAction('Mirror &Vertically', self.editMirrorVertical, 'Ctrl + V', 'editmirrorvert', 'Vertically Mirror the image', True, 'toggled')
		mirrorGroup.addAction(editMirrorVerticalAction)
		editUnMirrorAction.setChecked(True)

		#help Menu Options
		helpAboutAction = self.createAction('&About Image Editor', self.helpAbout)
		helpHelpAction = self.createAction('&Help', self.helpHelp, QKeySequence.HelpContents)

		#add Menus to menu bar
		#fileMenu
		self.fileMenu = self.menuBar().addMenu('&File')
		self.fileMenuActions = (fileNewAction, fileOpenAction, fileSaveAction, fileSaveAsAction, None, filePrintAction, fileQuitAction)
		self.fileMenu.aboutToShow.connect(self.updateFileMenu)

		#edit Menu
		editMenu = self.menuBar().addMenu('&Edit')
		self.addActions(editMenu, (editZoomAction, editInvertAction, editSwapRedAndBlueAction, editResizeAction))
		mirrorMenu = editMenu.addMenu(QIcon(':/editmirror.png'), '&Mirror')
		self.addActions(mirrorMenu, (editUnMirrorAction, editMirrorVerticalAction, editMirrorHorizontalAction))
		#help Menu
		helpMenu = self.menuBar().addMenu('&Help')
		self.addActions(helpMenu, (helpAboutAction, helpHelpAction))

		#Tool bars
		#File Tool Bars
		fileToolBar = self.addToolBar('File')
		fileToolBar.setObjectName('FileToolBar')
		self.addActions(fileToolBar, (fileNewAction, fileOpenAction, fileSaveAction, fileSaveAsAction))

		#Edit Tool Bars
		editToolBar = self.addToolBar('Edit')
		editToolBar.setObjectName('EditToolBar')
		self.addActions(editToolBar, (editInvertAction, editSwapRedAndBlueAction, editUnMirrorAction, editMirrorVerticalAction, editMirrorHorizontalAction, editResizeAction))

		self.zoomSpinBox = QSpinBox()
		self.zoomSpinBox.setRange(1, 400)
		self.zoomSpinBox.setSuffix(' %')
		self.zoomSpinBox.setValue(100)
		self.zoomSpinBox.setToolTip('Zoom the Image')
		self.zoomSpinBox.setStatusTip(self.zoomSpinBox.toolTip())
		self.zoomSpinBox.setFocusPolicy(Qt.NoFocus)
		self.zoomSpinBox.valueChanged.connect(self.showImage)
		editToolBar.addWidget(self.zoomSpinBox)

		self.addActions(self.imageLabel, (editInvertAction, editSwapRedAndBlueAction, editUnMirrorAction, editMirrorVerticalAction, editMirrorHorizontalAction, editResizeAction))
		self.resettableActions = ((editInvertAction, False),
                                 (editSwapRedAndBlueAction, False),
                                 (editUnMirrorAction, True))
		settings = QSettings()
		if settings.value('recentFiles') is not None:
			self.recentFiles = settings.value('recentFiles')
			self.restoreGeometry(settings.value('Geometry'))
			self.restoreState(settings.value('MainWindow/State'))
		else:
			self.recentFiles = list()
		self.updateFileMenu()
		self.setWindowTitle('Image Editor')
		QTimer.singleShot(0, self.loadInitialFile)
		
	def createAction(self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal='triggered'):
		action = QAction(text, self)
		if icon is not None:
			action.setIcon(QIcon(':/%s.png' % icon))
		if shortcut is not None:
			action.setShortcut(shortcut)
		if tip is not None:
			action.setToolTip(tip)
			action.setStatusTip(tip)
		if slot is not None:
			action.triggered.connect(slot)
		if checkable:
			action.setCheckable(True)
		return action

	def addActions(self, target, actions):
		for action in actions:
			if action is None:
				target.addSeparator()
			else:
				target.addAction(action)

	def loadInitialFile(self):
		settings = QSettings()
		if settings.value('LastFile'):
			fname = str(settings.value('LastFile'))
			if fname and QFile.exists(fname):
				self.loadFile(fname)

	def closeEvent(self, event):
		if self.okToContinue():
			settings = QSettings()
			filename = QVariant(str(self.filename)) if self.filename is not  None else QVariant()
			settings.setValue('LastFile', filename)
			recentFiles = QVariant(self.recentFiles) if self.recentFiles else QVariant()
			settings.setValue('recentFiles', recentFiles)
			settings.setValue('Geometry', QVariant(self.saveGeometry()))
			settings.setValue('MainWindow/State', QVariant(self.saveState()))
		else:
			event.ignore()

	def okToContinue(self):
		if self.dirty:
			reply = QMessageBox.question(self, 'Image Editor - Unsaved Changes',
					'Save Unsaved Changes?', QMessageBox.Save|QMessageBox.Discard|QMessageBox.Cancel)
			if reply == QMessageBox.Cancel:
				return False
			elif reply == QMessageBox.Yes:
				self.fileSave()
		return True

	def updateFileMenu(self):
		self.fileMenu.clear()
		self.addActions(self.fileMenu, self.fileMenuActions[:-1])
		current = str(self.filename) if self.filename is not None else None
		recentFiles = []
		if self.recentFiles:
			for fname in self.recentFiles:
				if fname != current and QFile.exists(fname):
					recentFiles.append(fname)
		if recentFiles:
			self.fileMenu.addSeparator()
			for i, fname in enumerate(recentFiles):
				action = QAction(QIcon(":/icon.png"), '&%d %s' % (i + 1, QFileInfo(fname).fileName()), self)
				action.setData(QVariant(fname))
				action.triggered.connect(self.loadFile)
				self.fileMenu.addAction(action)
		self.fileMenu.addSeparator()
		self.fileMenu.addAction(self.fileMenuActions[-1])

	@pyqtSlot(str)
	def on_Select_file(self, value):
		self.loadFile(value)


	def addRecentFiles(self, fname):
		if fname is None:
			return
		if self.recentFiles is not None:
			if not fname in self.recentFiles:
				self.recentFiles.insert(0, fname)
				while len(self.recentFiles) > 9:
					self.recentFiles.pop(len(self.recentFiles)-1)

	def fileNew(self):
		if not self.okToContinue():
			return
		dialog = newImageDlg.NewImageDlg(self)
		if dialog.exec_():
			self.addRecentFiles(self.filename)
			self.image = QImage()
			for action, check in self.resettableActions:
				action.setChecked(check)
			self.image = dialog.image()
			self.filename = None
			self.dirty = True
			self.showImage()
			self.sizeLabel.setText('{0} {1}'.format(self.image.width(), self.image.height()))
			self.updateStatus('Created New Image')

	def updateStatus(self, message):
		self.statusBar().showMessage(message, 5000)
		self.listWidget.addItem(message)
		if self.filename is not None:
			self.setWindowTitle('Image Editor - {0}[*]'.format(os.path.basename(self.filename)))
		elif not self.image.isNull():
			self.setWindowTitle('Image Editor - Unnamed[*]')
		else:
			self.setWindowTitle('Image Editor[*]')
		self.setWindowModified(self.dirty)

	def fileOpen(self):
		if not self.okToContinue():
			return
		dir = os.path.dirname(self.filename) if self.filename is not None else '.'
		formats = ['{0}'.format(str(format).lower()) for format in QImageReader.supportedImageFormats()]
		formats = ['*.{0}'.format(format[2:5]) for format in formats]
		fname,_ = QFileDialog.getOpenFileName(self, 'Image Editor - Choose Image', dir, 'Image File ({0})'.format(" ".join(formats)))
		if fname:
			self.loadFile(fname)

	def loadFile(self, fname):
		#print(fname)
		if fname is None:
			action = self.sender()
			if isinstance(action, QAction):
				fname = str(action.data().toString)
				if not self.okToContinue():
					return
			else:
				return
		if fname:
			self.filename = None
			image = QImage(fname)
			if image.isNull():
				message = 'failed to read {0}'.format(fname)
			else:
				self.addRecentFiles(fname)
				self.image = QImage()
				for action, check in self.resettableActions:
					action.setChecked(check)
				self.image = image
				self.filename = fname
				self.showImage()
				self.dirty = False
				self.sizeLabel.setText('{0} x {1}'.format(image.width(), image.height()))
				message = 'Loaded {0}'.format(os.path.basename(fname))
			self.updateStatus(message)
			
	def fileSave(self):
		if self.image.isNull():
			return
		if self.filename is None:
			self.fileSaveAs()
		else:
			if self.image.save(self.filename, None):
				self.updateStatus('Saved as {0}'.format(self.filename))
				self.dirty = False
			else:
				self.updateStatus('Failed to save {0}'.format(self.filename))

	def fileSaveAs(self):
		if self.image.isNull():
			return
		fname = self.filename if self.filename else '.'
		formats = ['{0}'.format(str(format).lower()) for format in QImageWriter.supportedImageFormats()]
		formats = ['*.{0}'.format(format[2:5]) for format in formats]
		fname,_ = QFileDialog.getSaveFileName(self, 'Image Editor - Save Image', fname, 'Image files ({0})'.format(' '.join(formats)))
		if fname:
			if '.' not in fname:
				fname += '.png'
			self.addRecentFiles(fname)
			self.filename = fname
			self.fileSave()

	def filePrint(self):
		pass

	def editZoom(self):
		if self.image.isNull():
			return
		percent, ok = QInputDialog.getInt(self, 'Image Editor - Zoom', 'Percent', self.zoomSpinBox.value(), 1, 400)
		if ok:
			self.zoomSpinBox.setValue(percent)

	def editInvert(self, on):
		if self.image.isNull():
			return
		self.image.invertPixels()
		self.showImage()
		self.dirty = True
		self.updateStatus('Inverted' if on else 'UnInverted')

	def editUnMirror(self):
		if self.image.isNull():
			return
		if self.mirroredHorizontally:
			self.editMirrorHorizontal(False)
		if self.mirroredVertically:
			self.editMirrorVertical(False)

	def editMirrorVertical(self, on):
		if self.image.isNull():
			return
		self.image = self.image.mirrored(False, True)
		self.showImage()
		self.mirroredVertically = not self.mirroredVertically
		self.dirty = True
		self.updateStatus('Mirrorred Vertically' if on else 'Unmirrored Vertically')


	def editMirrorHorizontal(self, on):
		if self.image.isNull():
			return
		self.image = self.image.mirrored(True, False)
		self.showImage()
		self.mirroredHorizontally = not self.mirroredHorizontally
		self.dirty = True
		self.updateStatus('Mirrorred Horizontally' if on else 'Unmirrored Horizontally')

	def editSwapRedAndBlue(self, on):
		if self.image.isNull():
			return
		self.image = self.image.rgbSwapped()
		self.showImage()
		self.dirty = True
		self.updateStatus('Swapped Red and Blue' if on else 'Unswapped Red and Blue')

	def editResize(self):
		if self.image.isNull():
			return
		form = resizeDlg.ResizeDialog(self.image.width(), self.image.height(), self)
		if form.exec_():
			width, height = form.result()
			if (width == self.image.width() and height == self.image.height()):
				self.updateStatus('Resized to the same Size')
			else:
				self.image = self.image.scaled(width, height)
				self.showImage()
				self.dirty = True 
				size = '{0} x {1}'.format(self.image.width(), self.image.height())
				self.sizeLabel.setText(size)
				self.updateStatus('Resized to {0}'.format(size))

	def helpAbout(self):
		QMessageBox.about(self, 'About Image Editor', '''<b>Image Editor v %s 
			<p>Copyright &copy; 2018 Qtrac Ltd. All rights reserved
			<p>This is an application used to perform simple image manipulations
			<p>Python %s - Qt %s - PyQt %s on %s''' % (__version__, platform.python_version(), QT_VERSION_STR, PYQT_VERSION_STR, platform.system()))

	def helpHelp(self):
		form = hp.HelpForm('index.html', self)
		form.show()

	def showImage(self, percent=None):
		if self.image.isNull():
			return
		if percent is None:
			percent = self.zoomSpinBox.value()
		factor = percent/100.0
		width = self.image.width() * factor
		height = self.image.height() * factor
		image = self.image.scaled(width, height, Qt.KeepAspectRatio)
		self.imageLabel.setPixmap(QPixmap.fromImage(image))
Example #9
0
class MainWindow(QMainWindow):
    """
    Class documentation goes here.
    """
    def __init__(self, parent=None):
        """
        Constructor

        @param parent reference to the parent widget
        @type QWidget
        """
        super(MainWindow, self).__init__(parent)

        self.image = QImage()
        self.dirty = False
        self.filename = None
        self.mirroredvertically = False
        self.mirroredhorizontally = False

        self.imageLabel = QLabel()
        self.imageLabel.setMinimumSize(200, 200)
        self.imageLabel.setAlignment(Qt.AlignCenter)
        self.imageLabel.setContextMenuPolicy(Qt.ActionsContextMenu)
        self.setCentralWidget(self.imageLabel)

        logDockWidget = QDockWidget("Log", self)
        logDockWidget.setObjectName("LogDockWidget")
        logDockWidget.setAllowedAreas(Qt.LeftDockWidgetArea
                                      | Qt.RightDockWidgetArea)
        self.listWidget = QListWidget()
        logDockWidget.setWidget(self.listWidget)
        self.addDockWidget(Qt.RightDockWidgetArea, logDockWidget)

        self.printer = None

        self.sizeLabel = QLabel()
        self.sizeLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
        status = self.statusBar()
        status.setSizeGripEnabled(False)
        status.addPermanentWidget(self.sizeLabel)
        status.showMessage("Ready", 5000)  # clearMessage()

        fileNewAction = self.createAction("&New...", self.fileNew,
                                          QKeySequence.New, "filenew",
                                          "Create an image file")
        fileOpenAction = self.createAction("&Open...", self.fileOpen,
                                           QKeySequence.Open, "fileopen",
                                           "Open an existing image file")
        fileSaveAction = self.createAction("&Save", self.fileSave,
                                           QKeySequence.Save, "filesave",
                                           "Save the image")
        fileSaveAsAction = self.createAction(
            "Save &As...",
            self.fileSaveAs,
            icon="filesaveas",
            tip="Save the image using a new name")
        filePrintAction = self.createAction("&Print", self.filePrint,
                                            QKeySequence.Print, "fileprint",
                                            "Print the image")
        fileQuitAction = self.createAction("&Quit", self.close, "Ctrl+Q",
                                           "filequit", "Close the application")

        editInvertAction = self.createAction("&Invert", self.editInvert,
                                             "Ctrl+I", "editinvert",
                                             "Invert the image's colors", True,
                                             "toggled(bool)")
        editSwapRedAndBlueAction = self.createAction(
            "Sw&ap Red and Blue", self.editSwapRedAndBlue, "Ctrl+A",
            "editswap", "Swap the image's red and blue color components", True,
            "toggled(bool)")
        editZoomAction = self.createAction("&Zoom...", self.editZoom, "Alt+Z",
                                           "editzoom", "Zoom the image")

        editResizeAction = self.createAction("&Resize...", self.editResize,
                                             "Ctrl+R", "editresize",
                                             "Resize the image")
        mirrorGroup = QActionGroup(self)
        editUnMirrorAction = self.createAction("&Unmirror", self.editUnMirror,
                                               "Ctrl+U", "editunmirror",
                                               "Unmirror the image", True,
                                               "toggled(bool)")
        mirrorGroup.addAction(editUnMirrorAction)
        editMirrorHorizontalAction = self.createAction(
            "Mirror &Horizontally", self.editMirrorHorizontal, "Ctrl+H",
            "editmirrorhoriz", "Horizontally mirror the image", True,
            "toggled(bool)")
        mirrorGroup.addAction(editMirrorHorizontalAction)
        editMirrorVerticalAction = self.createAction(
            "Mirror &Vertically", self.editMirrorVertical, "Ctrl+V",
            "editmirrorvert", "Vertically mirror the image", True,
            "toggled(bool)")
        mirrorGroup.addAction(editMirrorVerticalAction)
        editUnMirrorAction.setChecked(True)

        helpAboutAction = self.createAction("&About Image Changer",
                                            self.helpAbout)
        helpHelpAction = self.createAction("&Help", self.helpHelp,
                                           QKeySequence.HelpContents)

        # file Menu
        self.fileMenu = self.menuBar().addMenu("&File")
        self.fileMenuActions = (fileNewAction, fileOpenAction, fileSaveAction,
                                fileSaveAsAction, None, filePrintAction,
                                fileQuitAction)

        self.fileMenu.aboutToShow.connect(self.updateFileMenu)
        # edit Menu
        editMenu = self.menuBar().addMenu("&Edit")
        self.addActions(editMenu, (editInvertAction, editSwapRedAndBlueAction,
                                   editZoomAction, editResizeAction))
        # mirrorMenu
        mirrorMenu = editMenu.addMenu(QIcon(":/editmirror.png"), "&Mirror")
        self.addActions(mirrorMenu,
                        (editUnMirrorAction, editMirrorHorizontalAction,
                         editMirrorVerticalAction))
        # help Menu
        helpMenu = self.menuBar().addMenu("&Help")
        self.addActions(helpMenu, (helpAboutAction, helpHelpAction))

        # tool bar
        fileToolbar = self.addToolBar("File")
        fileToolbar.setObjectName("FileToolBar")
        self.addActions(fileToolbar,
                        (fileNewAction, fileOpenAction, fileSaveAsAction))
        editToolbar = self.addToolBar("Edit")
        editToolbar.setObjectName("EditToolBar")
        self.addActions(
            editToolbar,
            (editInvertAction, editSwapRedAndBlueAction, editUnMirrorAction,
             editMirrorVerticalAction, editMirrorHorizontalAction))
        self.zoomSpinBox = QSpinBox()
        self.zoomSpinBox.setRange(1, 400)
        self.zoomSpinBox.setSuffix(" %")
        self.zoomSpinBox.setValue(100)
        self.zoomSpinBox.setToolTip("Zoom the image")
        self.zoomSpinBox.setStatusTip(self.zoomSpinBox.toolTip())
        self.zoomSpinBox.setFocusPolicy(Qt.NoFocus)
        self.zoomSpinBox.valueChanged[int].connect(self.showImage)
        editToolbar.addWidget(self.zoomSpinBox)

        self.addActions(
            self.imageLabel,
            (editInvertAction, editSwapRedAndBlueAction, editUnMirrorAction,
             editMirrorVerticalAction, editMirrorHorizontalAction))

        self.resetableActions = ((editInvertAction,
                                  False), (editSwapRedAndBlueAction, False),
                                 (editUnMirrorAction, True))

        settings = QSettings("MyCompany", "MyApp")

        self.recentFiles = settings.value("RecentFiles")
        # self.recentFiles = []
        self.restoreGeometry(QByteArray(settings.value("MainWindow/Geometry")))
        self.restoreState(QByteArray(settings.value("MainWindow/State")))
        self.setWindowTitle("Image Changer")
        self.updateFileMenu()
        QTimer.singleShot(0, self.loadInitialFile)

    def createAction(self,
                     text,
                     slot=None,
                     shortcut=None,
                     icon=None,
                     tip=None,
                     checkable=False,
                     signal="triggered()"):
        action = QAction(text, self)
        if icon is not None:
            action.setIcon(QIcon(":/{0}.png".format(icon)))
        if shortcut is not None:
            action.setShortcut(shortcut)
        if tip is not None:
            action.setToolTip(tip)
            action.setStatusTip(tip)
        if slot is not None and signal == "triggered()":
            action.triggered.connect(slot)
        if slot is not None and signal == "toggled(bool)":
            action.toggled[bool].connect(slot)
        if checkable:
            action.setCheckable(True)
        return action

    def addActions(self, target, actions):
        for action in actions:
            if action is None:
                target.addSeparator()
            else:
                target.addAction(action)

    def updateFileMenu(self):
        self.fileMenu.clear()
        self.addActions(self.fileMenu, self.fileMenuActions[:-1])
        current = self.filename
        recentFiles = []
        # self.recentFiles=[]
        for fname in self.recentFiles:
            if fname != current and QFile.exists(fname):
                # recentFiles.insert(0,fname)
                recentFiles.append(fname)
        if recentFiles:
            self.fileMenu.addSeparator()
            for i, fname in enumerate(recentFiles):
                action = QAction(
                    QIcon(":/icon.png"),
                    "&{0} {1}".format(i + 1,
                                      QFileInfo(fname).fileName()), self)
                action.setData(QVariant(fname))
                action.triggered[bool].connect(self.loadFile)
                self.fileMenu.addAction(action)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.fileMenuActions[-1])

    def okToContinue(self):
        if self.dirty:
            reply = QMessageBox.question(
                self, "Image Changer - Unsaved Changes",
                "Save unsaved changes?",
                QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
            if reply == QMessageBox.Cancel:
                return False
            elif reply == QMessageBox.Yes:
                return self.fileSave()
        return True

    def fileNew(self):
        if not self.okToContinue():
            return
        dialog = newimagedlg.NewImageDlg(self)
        if dialog.exec_():
            self.addRecentFile(self.filename)
            self.image = QImage()
            for action, check in self.resetableActions:
                action.setChecked(check)
            self.image = dialog.image()
            self.filename = None
            self.dirty = True
            self.showImage()
            self.sizeLabel.setText("{0} x {1}".format(self.image.width(),
                                                      self.image.height()))
            self.updateStatus("Created new image")

    def fileOpen(self):
        if not self.okToContinue():
            return
        dir = (os.path.dirname(self.filename)
               if self.filename is not None else ".")
        formats = ([
            "*.{0}".format(format.data().decode("ascii").lower())
            for format in QImageReader.supportedImageFormats()
        ])
        fname, tpye = QFileDialog.getOpenFileName(
            self, "Image Changer - Choose Image", dir,
            "Image files ({0})".format(" ".join(formats)))
        if fname:
            self.loadFile(True, fname)

    def fileSave(self):
        if self.image.isNull():
            return True
        if self.filename is None:
            return self.fileSaveAs()
        else:
            if self.image.save(self.filename, None):
                self.updateStatus("Saved as {0}".format(self.filename))
                self.dirty = False
                return True
            else:
                self.updateStatus("Failed to save {0}".format(self.filename))
                return False

    def fileSaveAs(self):
        if self.image.isNull():
            return True
        fname = self.filename if self.filename is not None else "."
        formats = ([
            "*.{0}".format(format.data().decode("ascii").lower())
            for format in QImageWriter.supportedImageFormats()
        ])
        fname, tpye = QFileDialog.getSaveFileName(
            self, "Image Changer - Save Image", fname,
            "Image files ({0})".format(" ".join(formats)))
        if fname:
            if "." not in fname:
                fname += ".png"
            self.addRecentFile(fname)
            self.filename = fname
            return self.fileSave()
        return False

    def filePrint(self):
        if self.image.isNull():
            return
        if self.printer is None:
            self.printer = QPrinter(QPrinter.HighResolution)
            self.printer.setPageSize(QPrinter.Letter)
        form = QPrintDialog(self.printer, self)
        if form.exec_():
            painter = QPainter(self.printer)
            rect = painter.viewport()
            size = self.image.size()
            size.scale(rect.size(), Qt.KeepAspectRatio)
            painter.setViewport(rect.x(), rect.y(), size.width(),
                                size.height())
            painter.drawImage(0, 0, self.image)

    def loadFile(self, actiontrigger=False, fname=None):
        if fname is None:
            action = self.sender()
            if isinstance(action, QAction):
                fname = str(action.data())
                if not self.okToContinue():
                    return
            else:
                return
        print(fname)
        if fname:
            self.filename = None
            image = QImage(fname)
            if image.isNull():
                message = "Failed to read {0}".format(fname)
            else:
                self.addRecentFile(fname)
                self.image = QImage()
                for action, check in self.resetableActions:
                    action.setChecked(check)
                self.image = image
                self.filename = fname
                self.showImage()
                self.dirty = False
                self.sizeLabel.setText("{0} x {1}".format(
                    image.width(), image.height()))
                message = "Loaded {0}".format(os.path.basename(fname))
            self.updateStatus(message)

    def loadInitialFile(self):
        settings = QSettings()
        fname = str(settings.value("LastFile"))
        if fname and QFile.exists(fname):
            self.loadFile(fname)

    def addRecentFile(self, fname):
        if fname is None:
            return
        if fname not in self.recentFiles:
            self.recentFiles.insert(0, fname)
            while len(self.recentFiles) > 9:
                self.recentFiles.pop()

    def updateStatus(self, message):
        self.statusBar().showMessage(message, 5000)
        self.listWidget.addItem(message)
        if self.filename is not None:
            self.setWindowTitle("Image Changer - {0}[*]".format(
                os.path.basename(self.filename)))
        elif not self.image.isNull():
            self.setWindowTitle("Image Changer - Unnamed[*]")
        else:
            self.setWindowTitle("Image Changer[*]")
        self.setWindowModified(self.dirty)

    def editInvert(self, on):
        if self.image.isNull():
            return
        self.image.invertPixels()
        self.showImage()
        self.dirty = True
        self.updateStatus("Inverted" if on else "Uninverted")

    def editSwapRedAndBlue(self, on):
        if self.image.isNull():
            return
        self.image = self.image.rgbSwapped()
        self.showImage()
        self.dirty = True
        self.updateStatus(
            ("Swapped Red and Blue" if on else "Unswapped Red and Blue"))

    def editZoom(self):
        if self.image.isNull():
            return
        percent, ok = QInputDialog.getInt(self,
                                          "Image Changer - Zoom", "Percent:",
                                          self.zoomSpinBox.value(), 1, 400)
        if ok:
            self.zoomSpinBox.setValue(percent)

    def editUnMirror(self, on):
        if self.image.isNull():
            return
        if self.mirroredhorizontally:
            self.editMirrorHorizontal(False)
        if self.mirroredvertically:
            self.editMirrorVertical(False)

    def editMirrorHorizontal(self, on):
        if self.image.isNull():
            return
        self.image = self.image.mirrored(True, False)
        self.showImage()
        self.mirroredhorizontally = not self.mirroredhorizontally
        self.dirty = True
        self.updateStatus(
            ("Mirrored Horizontally" if on else "Unmirrored Horizontally"))

    def editMirrorVertical(self, on):
        if self.image.isNull():
            return
        self.image = self.image.mirrored(False, True)
        self.showImage()
        self.mirroredvertically = not self.mirroredvertically
        self.dirty = True
        self.updateStatus(
            ("Mirrored Vertically" if on else "Unmirrored Vertically"))

    def editResize(self):
        if self.image.isNull():
            return
        form = resizedlg.ResizeDlg(self.image.width(), self.image.height(),
                                   self)
        if form.exec_():
            width, height = form.result()
            if (width == self.image.width() and height == self.image.height()):
                self.statusBar().showMessage("Resized to the same size", 5000)
            else:
                self.image = self.image.scaled(width, height)
                self.showImage()
                self.dirty = True
                size = "{0} x {1}".format(self.image.width(),
                                          self.image.height())
                self.sizeLabel.setText(size)
                self.updateStatus("Resized to {0}".format(size))

    def helpAbout(self):
        QMessageBox.about(
            self, "About Image Changer", """Image Changer v {0}

          Copyright © 2008-9 Qtrac Ltd.
                          All rights reserved.

          This application can be used to perform
                          simple image manipulations.

          Python {1} - Qt {2} - PyQt {3} on {4}""".format(
                __version__, platform.python_version(), QT_VERSION_STR,
                PYQT_VERSION_STR, platform.system()))

    def helpHelp(self):
        form = helpform.HelpForm("index.html", self)
        form.show()

    def showImage(self, percent=None):
        if self.image.isNull():
            return
        if percent is None:
            percent = self.zoomSpinBox.value()
        factor = percent / 100.0
        width = self.image.width() * factor
        height = self.image.height() * factor
        image = self.image.scaled(width, height, Qt.KeepAspectRatio)
        self.imageLabel.setPixmap(QPixmap.fromImage(image))

    def closeEvent(self, event):
        if self.okToContinue():
            settings = QSettings("MyCompany", "MyApp")
            settings.setValue("LastFile", self.filename)
            settings.setValue("RecentFiles", self.recentFiles)
            settings.setValue("MainWindow/Geometry", self.saveGeometry())
            settings.setValue("MainWindow/State", self.saveState())

        else:
            event.ignore()
Example #10
0
class VideoWidgetSurface(QAbstractVideoSurface):
    def __init__(self, widget, parent=None):
        ''' Constructor '''
        super(VideoWidgetSurface, self).__init__(parent)

        self.widget = widget
        self.imageFormat = QImage.Format_Invalid
        self.image = None

    def supportedPixelFormats(self, handleType=QAbstractVideoBuffer.NoHandle):
        ''' Available Frames Format '''
        formats = [QVideoFrame.PixelFormat()]
        if handleType == QAbstractVideoBuffer.NoHandle:
            for f in [
                    QVideoFrame.Format_RGB32, QVideoFrame.Format_ARGB32,
                    QVideoFrame.Format_ARGB32_Premultiplied,
                    QVideoFrame.Format_RGB565, QVideoFrame.Format_RGB555
            ]:
                formats.append(f)
        return formats

    def isFormatSupported(self, _format):
        ''' Check if is supported VideFrame format '''
        imageFormat = QVideoFrame.imageFormatFromPixelFormat(
            _format.pixelFormat())
        size = _format.frameSize()
        _bool = False
        if (imageFormat != QImage.Format_Invalid and not size.isEmpty()
                and _format.handleType() == QAbstractVideoBuffer.NoHandle):
            _bool = True
        return _bool

    def start(self, _format):
        ''' Start QAbstractVideoSurface '''
        imageFormat = QVideoFrame.imageFormatFromPixelFormat(
            _format.pixelFormat())
        size = _format.frameSize()
        if (imageFormat != QImage.Format_Invalid and not size.isEmpty()):
            self.sourceRect = _format.viewport()
            QAbstractVideoSurface.start(self, _format)
            self.imageFormat = imageFormat
            self.imageSize = size
            self.widget.updateGeometry()
            self.updateVideoRect()
            return True
        else:
            return False

    def stop(self):
        ''' Stop Video '''
        self.currentFrame = QVideoFrame()
        self.targetRect = QRect()
        QAbstractVideoSurface.stop(self)
        self.widget.update()

    def present(self, frame):
        ''' Present Frame '''
        if (self.surfaceFormat().pixelFormat() != frame.pixelFormat()
                or self.surfaceFormat().frameSize() != frame.size()):
            self.setError(QAbstractVideoSurface.IncorrectFormatError)
            self.stop()
            return False
        else:
            self.currentFrame = frame
            self.widget.repaint(self.targetRect)
            return True

    def videoRect(self):
        ''' Get Video Rectangle '''
        return self.targetRect

    def GetsourceRect(self):
        ''' Get Source Rectangle '''
        return self.sourceRect

    def updateVideoRect(self):
        ''' Update video rectangle '''
        size = self.surfaceFormat().sizeHint()
        size.scale(self.widget.size().boundedTo(size), Qt.KeepAspectRatio)
        self.targetRect = QRect(QPoint(0, 0), size)
        self.targetRect.moveCenter(self.widget.rect().center())

    def paint(self, painter):
        ''' Paint Frame'''
        if (self.currentFrame.map(QAbstractVideoBuffer.ReadOnly)):
            oldTransform = painter.transform()

        if (self.surfaceFormat().scanLineDirection() ==
                QVideoSurfaceFormat.BottomToTop):
            painter.scale(1, -1)
            painter.translate(0, -self.widget.height())

        self.image = QImage(self.currentFrame.bits(),
                            self.currentFrame.width(),
                            self.currentFrame.height(),
                            self.currentFrame.bytesPerLine(), self.imageFormat)

        if self.widget._filterSatate.grayColorFilter:
            self.image = filter.GrayFilter(self.image)

        if self.widget._filterSatate.MirroredHFilter:
            self.image = filter.MirrredFilter(self.image)

        if self.widget._filterSatate.monoFilter:
            self.image = filter.MonoFilter(self.image)
        # TODO : Probar en un thread distinto
        if self.widget._filterSatate.edgeDetectionFilter:
            try:
                self.image = filter.EdgeFilter(self.image)
            except Exception:
                None
        # TODO : Probar en un thread distinto
        if self.widget._filterSatate.contrastFilter:
            try:
                self.image = filter.AutoContrastFilter(self.image)
            except Exception:
                None
        if self.widget._filterSatate.invertColorFilter:
            self.image.invertPixels()

        painter.drawImage(self.targetRect, self.image, self.sourceRect)

        if self._interaction.objectTracking and self.widget._isinit:
            frame = convertQImageToMat(self.image)
            # Update tracker
            ok, bbox = self.widget.tracker.update(frame)
            # Draw bounding box
            if ok:
                #                 qgsu.showUserAndLogMessage(
                #                     "bbox : ", str(bbox), level=QGis.Warning)
                painter.setPen(Qt.blue)
                painter.drawRect(
                    QRect(int(bbox[0]), int(bbox[1]), int(bbox[2]),
                          int(bbox[3])))
            else:
                qgsu.showUserAndLogMessage("Tracking failure detected ",
                                           "",
                                           level=QGis.Warning)

        painter.setTransform(oldTransform)
        self.currentFrame.unmap()
        return self.painter
Example #11
0
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent=parent)

        self.image = QImage()
        self.dirty = False
        self.filename = None
        self.mirroredvertically = False
        self.mirroredhorizonlly = False

        self.imageLabel = QLabel()
        self.imageLabel.setMinimumSize(200, 200)
        self.imageLabel.setAlignment(Qt.AlignCenter)
        self.imageLabel.setContextMenuPolicy(Qt.ActionsContextMenu)
        self.setCentralWidget(self.imageLabel)

        # 停靠窗体
        # 停靠窗体不能放在布局中,所以在创建的时候除了需要提供窗体标题外,还要对其给定父对象
        # 通过父对象的设置可以保证停靠窗体不会超出作用域
        logDockWidget = QDockWidget('log', self)
        # 每一个Pyqt对象都有一个对象名,对象名称在调试的时候非常有用
        logDockWidget.setObjectName('LogDockWidget')
        # 默认情况下停靠窗体可以移动,停靠,浮动,关闭,并且可以拖拽的任何区域
        # 由于这里的停靠窗体打算保留一个列表,使用setAllowedAreas来限制停靠区域
        # 还有一个setFeatures() 可以控制窗口是否可以移动,浮动,关闭等特性。
        logDockWidget.setAllowedAreas(Qt.LeftDockWidgetArea
                                      | Qt.RightDockWidgetArea)
        # 停靠窗体创建好了之后就可以创建窗体需要包含的部件了
        # 创建列表窗口部件
        self.listWidget = QListWidget()
        logDockWidget.setWidget(self.listWidget)
        self.addDockWidget(Qt.RightDockWidgetArea, logDockWidget)

        # 打印图片,首先创建一个printer(打印机实例变量)
        self.printer = None

        self.sizeLable = QLabel()
        self.sizeLable.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)
        # 创建状态栏,调用showMessage方法显示内容
        status = self.statusBar()
        status.setSizeGripEnabled(False)
        status.addPermanentWidget(self.sizeLable)
        status.showMessage("Ready", 5000)

        # 新建action
        # 显示名称,槽,快捷键,图标名称,显示status内容
        fileNewAction = self.createAction("&New...", self.fileNew,
                                          QKeySequence.New, "filenew",
                                          "Create an image file")
        # 打开文件action
        # 显示名称,槽,快捷键,图标名称,显示status内容
        fileOpenAction = self.createAction("&Open...", self.fileOpen,
                                           QKeySequence.Open, "fileopen",
                                           "Open an existing image file")
        # 保存action
        # 显示名称,槽,快捷键,图标显示,显示status内容
        fileSaveAction = self.createAction("&Save", self.fileSave,
                                           QKeySequence.Save, "filesave",
                                           "Save the image")
        # 另存为action
        # 显示名称,槽,快捷键,图标显示,显示satus内容
        fileSaveAsAction = self.createAction(
            "&Save as", self.fileSaveAs, QKeySequence.SaveAs, "filesaveas",
            "Save the image using a new name")

        # 打印action
        # 显示名称,槽,快捷键,图标显示,显示status内容
        filePrintAction = self.createAction("&Print", self.filePrint,
                                            QKeySequence.Print, "fileprint",
                                            "print the image")

        fileQuitAction = self.createAction("&Quit", self.close, "Ctrl+Q",
                                           "filequit", "Close the application")

        #
        editInvertAction = self.createAction("&Invert", self.editInvert,
                                             "Ctrl+I", "editinvert",
                                             "Invert the Image's colors", True,
                                             True)

        editSwapRedAndBlueAction = self.createAction(
            "&Sw&ap Ren and Blue", self.editSwapRedAndBlue, "Ctrl+A",
            "editswap", "Swap the image's red and blue color components", True,
            True)

        editZoomAction = self.createAction("&Zoom...", self.editZoom, "Alt+Z",
                                           "editzoom", "Zoom the image")

        mirrorGroup = QActionGroup(self)
        editUnMirrorAction = self.createAction("&Unmirror", self.edidUnMirror,
                                               "Ctrl+U", "editunmirror",
                                               "Unmirror the image", True,
                                               True)
        mirrorGroup.addAction(editUnMirrorAction)

        editMirrorHorizontalAction = self.createAction(
            "Mirror &Horizontally", self.editMirrorHorizontal, "Ctrl+H",
            "editmirrorhoriz", "Horizontally mirror the image", True, True)
        mirrorGroup.addAction(editMirrorHorizontalAction)

        editMIrrorVerticalAction = self.createAction(
            "Mirror &Vertically", self.editMirrorVertical, "Ctrl+V",
            "editmirrorvert", "Vertically mirror the image", True, True)
        mirrorGroup.addAction(editMIrrorVerticalAction)

        helpAboutAction = self.createAction("&About", self.about)
        helpHelpAction = self.createAction("&Help", self.help)

        menuBar = self.menuBar()
        newfile = menuBar.addMenu("&File")
        self.addActions(newfile,
                        (fileNewAction, fileOpenAction, fileSaveAction,
                         fileSaveAsAction, filePrintAction, fileQuitAction))

        editMenu = menuBar.addMenu("&Edit")
        self.addActions(
            editMenu,
            (editInvertAction, editSwapRedAndBlueAction, editZoomAction))

        mirrMenu = menuBar.addMenu(QIcon("images/editmirror.png"), "&mirror")
        self.addActions(mirrMenu,
                        (editUnMirrorAction, editMirrorHorizontalAction,
                         editMIrrorVerticalAction))

        helpMenu = menuBar.addMenu("&Help")
        self.addActions(helpMenu, (helpAboutAction, helpHelpAction))

        fileToolBar = self.addToolBar("File")
        fileToolBar.setObjectName("FileToolBar")
        self.addActions(fileToolBar,
                        (fileNewAction, fileOpenAction, fileSaveAction,
                         fileSaveAsAction, filePrintAction))

        editToolBar = self.addToolBar("Edit")
        editToolBar.setObjectName("EditToolBar")
        self.addActions(editToolBar,
                        (editInvertAction, editSwapRedAndBlueAction,
                         editZoomAction, editUnMirrorAction,
                         editMirrorHorizontalAction, editMIrrorVerticalAction))

        self.zoomSpinBox = QSpinBox()
        self.zoomSpinBox.setRange(1, 400)
        self.zoomSpinBox.setValue(100)
        self.zoomSpinBox.setSuffix(" %")
        self.zoomSpinBox.setStatusTip(self.zoomSpinBox.toolTip())
        self.zoomSpinBox.setFocusPolicy(Qt.NoFocus)
        self.zoomSpinBox.valueChanged.connect(self.showImage)
        editToolBar.addSeparator()
        editToolBar.addWidget(self.zoomSpinBox)

        settings = QSettings()
        # self.recentFiles = settings.value("RecentFiles").toStringList()

    def createAction(self,
                     text,
                     slot=None,
                     shortcut=None,
                     icon=None,
                     tip=None,
                     checkable=None,
                     togglesigle=False):
        """
        text:显示内容
        """

        action = QAction(text, self)

        if icon is not None:
            action.setIcon(QIcon("images/%s.png" % icon))
        if shortcut is not None:
            action.setShortcut(shortcut)
        if tip is not None:
            action.setToolTip(tip)
            action.setStatusTip(tip)
        if slot is not None:
            if togglesigle:
                action.toggled[bool].connect(slot)
            else:
                action.triggered.connect(slot)
        if checkable is not None:
            action.setCheckable(True)

        return action

    def addActions(self, target, actions):
        for action in actions:
            if action is None:
                target.addSeparator()
            else:
                target.addAction(action)

    def updateStatus(self, message):
        statusBar = self.statusBar()
        statusBar.showMessage(message, 5000)
        self.listWidget.addItem(message)
        if self.filename is not None:
            self.setWindowTitle("Image Changer - {0}".format(
                os.path.basename(self.filename)))
        elif not self.image.isNull():
            self.setWindowTitle("Image Changer - Unnamed")
        else:
            self.setWindowTitle("Image Changer[*]")
        self.setWindowModality(self.dirty)

    def okToContiune(self):
        """
        判断图片是否修改没有保存
        """
        if self.dirty:
            replay = QMessageBox.question(
                self, "ImageChanger - Unsaved Changed", "Save unsaved changes",
                QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
        # 取消操作
        if replay == QMessageBox.Cancel:
            return
        # 确定保存
        elif replay == QMessageBox.Yes:
            self.fileSave()
        return True

    def fileNew(self):
        pass

    def fileOpen(self):
        dir = os.path.dirname(
            self.filename) if self.filename is not None else "."
        # 构建所有QImageReader支持的图片类型列表
        formats = [
            '*.{0}'.format(format.data().decode("ascii").lower())
            for format in QImageReader.supportedImageFormats()
        ]
        # getOpenFileName返回
        fname, type = QFileDialog.getOpenFileName(
            self, "Image Changer - Choose Image", dir,
            "Image files (%s)" % " ".join(formats))
        # 如果选中了图片,加载图片
        if fname:
            self.loadFile(fname)

    def loadFile(self, fname=None):
        if fname is None:
            action = self.render()
            if isinstance(action, QAction):
                fname = str(action.data())
        if fname:
            self.filename = None
            try:
                image = QImage(fname)
            except Exception as e:
                print(e)
            if image.isNull():
                message = "Failed to read {0}".format(fname)

            else:
                # self.addRecentFile(fname)
                self.image = QImage()
                # for action, check in self.resetableActions:
                #     action.setChecked(check)
                self.image = image
                self.filename = fname
                self.showImage()
                self.dirty = False

    def addRecentFile(self, fname):
        if fname is None:
            return
        if not self.recentFiles.contains(fname):
            self.recentFiles.prepend(fname)
            while self.recentFiles.count() > 9:
                self.recentFiles.takeLast()

    def fileSave(self):
        """
        保存文件
        :return:
        """
        if self.image.isNull():
            return
        if self.filename is None:
            self.fileSaveAs()
        else:
            if self.image.save(self.filename, None):
                self.dirty = False
                self.updateStatus("Save as {0}".format(self.filename))
            else:
                self.updateStatus("Failed to save {0}".format(self.filename))

    def fileSaveAs(self):
        if self.image.isNull():
            return
        fname = self.filename if self.filename is not None else "."
        formats = [
            "*.{0}".format(format.data().decode("ascii").lower())
            for format in QImageWriter.supportedImageFormats()
        ]

        fname, type = QFileDialog.getSaveFileName(
            self, "Image Changer - Save Image", fname,
            "Image files ({0})".format(" ".join(formats)))

        if fname:
            if "." not in fname:
                fname += ".png"
            self.filename = fname
            self.fileSave()

    def filePrint(self):
        if self.image.isNull():
            return
        if self.printer is None:
            self.printer = QPrinter(QPrinter.HighResolution)
            self.printer.setPageSize(QPrinter.Letter)

        form = QPrintDialog(self.printer, self)
        if form.exec_():
            painter = QPainter(self.printer)
            rect = painter.viewport()
            size = self.image.size()
            size.scale(rect.size(), Qt.KeepAspectRatio)
            painter.setViewport(rect.x(), rect.y(), size.width(),
                                size.height())
            painter.drawImage(0, 0, self.image)

    def editInvert(self, on):
        if self.image.isNull():
            return
        self.image.invertPixels()
        self.showImage()
        self.dirty = True

    def editSwapRedAndBlue(self):
        if self.image.isNull():
            return
        self.image = self.image.rgbSwapped()
        self.showImage()
        self.dirty = True

    def editZoom(self):
        if self.image.isNull():
            return
        percent, ok = QInputDialog.getInt(self,
                                          "Image Changer - Zoom", "Percent:",
                                          self.zoomSpinBox.value(), 1, 400)

        if ok:
            self.zoomSpinBox.setValue(percent)

    def edidUnMirror(self):
        if self.image.isNull():
            return
        if self.mirroredhorizonlly:
            self.editMirrorHorizontal(False)
        if self.mirroredvertically:
            self.editMirrorVertical(False)

    def editMirrorHorizontal(self, on):
        if self.image.isNull():
            return
        self.image = self.image.mirrored(False, True)
        self.showImage()
        self.mirroredhorizonlly = not self.mirroredhorizonlly
        self.dirty = True

    def editMirrorVertical(self, on):
        if self.image.isNull():
            return
        self.image = self.image.mirrored(True, False)
        self.showImage()
        self.mirroredvertically = not self.mirroredvertically
        self.dirty = True

    def showImage(self, parcent=None):
        if self.image.isNull():
            return
        if parcent is None:
            parcent = self.zoomSpinBox.value()

        factor = parcent / 100.0
        width = self.image.width() * factor
        height = self.image.height() * factor

        image = self.image.scaled(width, height, Qt.KeepAspectRatio)

        self.imageLabel.setPixmap(QPixmap.fromImage(image))

    def about(self):
        QMessageBox.about(
            self, "about image changer", """<b>Image Changer</b> v {0}
                          <p>Copyright &copy; 2007 Qtrac Ltd. 
                          All rights reserved.
                          <p>This application can be used to perform
                          simple image manipulations.
                          <p>Python {1} - Qt {2} - PyQt {3} on {4}""".format(
                __version__, platform.python_version(), QT_VERSION_STR,
                PYQT_VERSION_STR, platform.system()))

    def help(self):
        print("跳转到网页界面!")
Example #12
0
class VideoWidgetSurface(QAbstractVideoSurface):
    def __init__(self, widget, parent=None):
        ''' Constructor '''
        super(VideoWidgetSurface, self).__init__(parent)

        self.widget = widget
        self.imageFormat = QImage.Format_Invalid
        self.image = None
        self.zoomedrect = None

    def supportedPixelFormats(self, handleType=QAbstractVideoBuffer.NoHandle):
        ''' Available Frames Format '''
        formats = [QVideoFrame.PixelFormat()]
        if handleType == QAbstractVideoBuffer.NoHandle:
            for f in [
                    QVideoFrame.Format_RGB32, QVideoFrame.Format_ARGB32,
                    QVideoFrame.Format_ARGB32_Premultiplied,
                    QVideoFrame.Format_RGB565, QVideoFrame.Format_RGB555
            ]:
                formats.append(f)
        return formats

    def isFormatSupported(self, _format):
        ''' Check if is supported VideFrame format '''
        imageFormat = QVideoFrame.imageFormatFromPixelFormat(
            _format.pixelFormat())
        size = _format.frameSize()
        _bool = False
        if (imageFormat != QImage.Format_Invalid and not size.isEmpty()
                and _format.handleType() == QAbstractVideoBuffer.NoHandle):
            _bool = True
        return _bool

    def start(self, _format):
        ''' Start QAbstractVideoSurface '''
        imageFormat = QVideoFrame.imageFormatFromPixelFormat(
            _format.pixelFormat())
        size = _format.frameSize()
        if (imageFormat != QImage.Format_Invalid and not size.isEmpty()):
            self.imageFormat = imageFormat
            self.imageSize = size
            self.sourceRect = _format.viewport()
            QAbstractVideoSurface.start(self, _format)
            self.widget.updateGeometry()
            self.updateVideoRect()
            return True
        else:
            return False

    def stop(self):
        ''' Stop Video '''
        self.currentFrame = QVideoFrame()
        self.targetRect = QRect()
        QAbstractVideoSurface.stop(self)
        self.widget.update()

    def present(self, frame):
        ''' Present Frame '''
        if (self.surfaceFormat().pixelFormat() != frame.pixelFormat()
                or self.surfaceFormat().frameSize() != frame.size()):
            self.setError(QAbstractVideoSurface.IncorrectFormatError)
            self.stop()
            return False
        else:
            self.currentFrame = frame
            self.widget.repaint(self.targetRect)
            qgsu.showUserAndLogMessage(QCoreApplication.translate(
                "QgsVideo", 'Video : '),
                                       "Repaint Video",
                                       onlyLog=True)
            return True

    def videoRect(self):
        ''' Get Video Rectangle '''
        return self.targetRect

    def GetsourceRect(self):
        ''' Get Source Rectangle '''
        return self.sourceRect

    def updateVideoRect(self):
        ''' Update video rectangle '''
        size = self.surfaceFormat().sizeHint()
        size.scale(self.widget.size().boundedTo(size), Qt.KeepAspectRatio)
        self.targetRect = QRect(QPoint(0, 0), size)
        self.targetRect.moveCenter(self.widget.rect().center())

    def updateVideoZoomRect(self, rect):  # TODO : Make this function
        size = self.surfaceFormat().sizeHint()
        size.scale(rect.width(), rect.height(), Qt.KeepAspectRatio)
        self.zoomedrect = rect
        self.zoomedrect.moveCenter(rect.center())
        self.widget.update()

    def updatetest(self):
        return

    def paint(self, painter):
        ''' Paint Frame'''
        if (self.currentFrame.map(QAbstractVideoBuffer.ReadOnly)):
            oldTransform = painter.transform()

            if (self.surfaceFormat().scanLineDirection() ==
                    QVideoSurfaceFormat.BottomToTop):
                painter.scale(1, -1)
                painter.translate(0, -self.widget.height())

            self.image = QImage(self.currentFrame.bits(),
                                self.currentFrame.width(),
                                self.currentFrame.height(),
                                self.currentFrame.bytesPerLine(),
                                self.imageFormat)

            if grayColorFilter:
                self.image = filter.GrayFilter(self.image)

            if monoFilter:
                self.image = filter.MonoFilter(self.image)

            if edgeDetectionFilter:
                self.image = filter.EdgeFilter(self.image)

            if contrastFilter:
                self.image = filter.AutoContrastFilter(self.image)

            if invertColorFilter:
                self.image.invertPixels()

            if zoomRect and self.zoomedrect is not None:
                painter.drawImage(self.sourceRect, self.image, self.zoomedrect)
                painter.setTransform(oldTransform)
                self.currentFrame.unmap()
                return

            painter.drawImage(self.targetRect, self.image, self.sourceRect)
            painter.setTransform(oldTransform)
            self.currentFrame.unmap()
            self.currentFrame.release()
class DrawingBox(QWidget):
    """
    Allows to draw an image of a letter. After drawing, sends signal containing
    image rescaled to the input size of the neural network.
    """
    # signal emitted when user releases mouse after drawing
    newImage = pyqtSignal(QImage)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.imageSize = GUI_IMAGE_SIZE
        self.setFixedSize(self.imageSize)
        self.inverted = False
        self.pen = QPen(Qt.white if self.inverted else Qt.black,
            int(np.mean(PEN_WIDTH_RANGE)), Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)
        self.image = QImage(self.imageSize, QImage.Format_Grayscale8)
        self.clearImage()
        self.last_pos = None
        self.drawing = False

    @pyqtSlot(int)
    def setPenWidth(self, pen_width):
        self.pen.setWidth(pen_width)

    @pyqtSlot()
    def invert(self):
        self.inverted = not self.inverted
        self.pen.setColor(Qt.white if self.inverted else Qt.black)
        self.image.invertPixels()
        self.update()
        self.pushImage()

    def pushImage(self):
        image = self.image.scaled(NET_IMAGE_SIZE, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
        self.newImage.emit(image)

    @pyqtSlot()
    def clearImage(self):
        self.image.fill(0 if self.inverted else 255)
        self.update()

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.last_pos = event.pos()
            self.drawing = True

    def mouseMoveEvent(self, event):
        dist = (event.pos() - self.last_pos).manhattanLength()
        if (event.buttons() & Qt.LeftButton) and self.drawing and dist > 3:
            self.drawLineTo(event.pos())

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton and self.drawing:
            self.drawLineTo(event.pos())
            self.drawing = False
            self.pushImage()

    def paintEvent(self, event):
        "Paints changes in the image on the widget."
        painter = QPainter(self)
        dirty_rect = event.rect()
        painter.drawImage(dirty_rect, self.image, dirty_rect)

    def drawLineTo(self, end_pos):
        "Draws line on the image and signals which part is dirty."
        painter = QPainter(self.image)
        painter.setPen(self.pen)
        painter.drawLine(self.last_pos, end_pos)
        radius = int((self.pen.widthF() / 2) + 2)
        self.update(QRect(self.last_pos, end_pos).normalized().adjusted(
            -radius, -radius, radius, radius))
        self.last_pos = end_pos
class Spectrogram(QWidget):

    mouseOver = pyqtSignal(float, float)

    def __init__(self, fname, parent, config):
        super().__init__(parent)
        print(f' INFO: processing {fname}')
        self.conf = config
        self.setMouseTracking(True)
        self.setGeometry(parent.contentsRect())
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.data = {
            'name': os.path.basename(fname),
            'path': fname,
            'pex': 0.0,
            'tex': 0.0,
            'fex': 0.0,
            'notes': ''
        }
        self.stored = False
        self.init_fft()
        if self.amp is None:
            parent.next_spectrogram()
            self.close()
            return
        self.init_calculations()
        self.init_image()
        self.fit()
        self.eraser = self.init_eraser(10, 200)
        self.eraser.maxheight = 200
        self.cursorX = self.init_cursor(1, self.height() * 4)
        self.cursorY = self.init_cursor(self.width() * 4, 1)
        self.show()

    def init_fft(self):
        fft = self.conf['fft']
        try:
            with wave.open(self.data['path'], 'rb') as song:
                channels, bs, samprate, nsamp, comp, compnam = song.getparams()
                data = fromstring(song.readframes(nsamp), dtype=uint16)
        except wave.Error:
            print(f' WARN: skipping \"{self.data["path"]}\": not a wave file.')
            return None
        f, t, amp = spectrogram(data,
                                samprate,
                                fft['window-function'],
                                fft['window'],
                                fft['overlap'],
                                detrend=False,
                                mode='psd')
        self.freq = f
        self.time = t
        self.amp = amp

    def init_calculations(self):
        calc = self.conf['calculations']
        bot = argmax(self.freq > calc['high-pass'])
        top = argmin(self.freq < calc['low-pass'])
        self.freq = self.freq[bot:top]
        self.amp = self.amp[bot:top, :]

        self.thresh = self.amp.max() / 10**(calc['threshold'] / 10.0)
        self.thresh = 255.0 - self.thresh * 255.0 / self.amp.max()
        self.amp = 255.0 - self.amp * 255.0 / self.amp.max()

        arr = asarray(self.amp.flat)
        per = 100 * (arr <= self.thresh).sum() / len(arr)
        msg = f'{per:.2}%% of signal is above the threshold.'
        if per > 35.0:
            print(f' WARN: {msg}  Consider using a lower threshold.')
        elif per < 5.0:
            print(f' WARN: {msg}  Consider using a higher threshold.')
        else:
            print(f' INFO: {msg}')

    def init_image(self):
        colors = self.conf['colors']
        self.amp = flipud(self.amp)
        self.amp_back = copy(self.amp)
        self.freq = flipud(self.freq)
        self.imgdata = array(self.amp, uint8)
        h, w = self.imgdata.shape
        self.freq /= 1000.0
        self.image = QImage(self.imgdata.tobytes(), w, h, w,
                            QImage.Format_Grayscale8)
        if colors['invert']:
            self.image.invertPixels()

    def init_cursor(self, w, h):
        cursor = QWidget(self)
        cursor.setAttribute(Qt.WA_TransparentForMouseEvents)
        effect = QGraphicsOpacityEffect(cursor)
        effect.setOpacity(0.4)
        cursor.setGraphicsEffect(effect)
        cursor.setGeometry(0, 0, w, h)
        cursor.setAutoFillBackground(True)
        p = cursor.palette()
        p.setColor(cursor.backgroundRole(),
                   QColor(self.conf['colors']['cursor']))
        cursor.setPalette(p)
        cursor.hide()
        return cursor

    def init_eraser(self, w, h):
        eraser = QWidget(self)
        eraser.setAttribute(Qt.WA_TransparentForMouseEvents)
        effect = QGraphicsOpacityEffect(eraser)
        effect.setOpacity(0.4)
        eraser.setGraphicsEffect(effect)
        eraser.setGeometry(0, 0, w, h)
        eraser.setAutoFillBackground(True)
        p = eraser.palette()
        p.setColor(eraser.backgroundRole(),
                   QColor(self.conf['colors']['eraser']))
        eraser.setPalette(p)
        eraser.hide()
        return eraser

    def fit(self):
        s = self.image.size()
        asp = s.width() / (s.height() * self.conf['scale-ratio'])
        if self.size().height() * asp > self.size().width():
            self.resize(self.width(), self.width() / asp)
        else:
            self.resize(self.height() * asp, self.height())

    def mode_normal(self):
        self.unsetCursor()
        self.eraser.hide()
        self.cursorX.hide()
        self.cursorY.hide()

    def mode_cursor(self):
        self.setCursor(Qt.BlankCursor)
        self.eraser.hide()
        self.cursorX.show()
        self.cursorY.show()

    def mode_eraser(self):
        self.setCursor(Qt.BlankCursor)
        self.cursorX.hide()
        self.cursorY.hide()
        self.eraser.show()

    def undo(self):
        copyto(self.amp, self.amp_back)
        self.update()

    def paintEvent(self, e):
        # TODO refactor this rats nest
        qp = QPainter(self)
        qp.setRenderHints(QPainter.SmoothPixmapTransform
                          | QPainter.Antialiasing)
        qp.drawImage(self.contentsRect(), self.image, self.image.rect())
        r = QRect(0, 0, self.width() - 1, self.height() - 1)
        qp.setPen(QColor(self.conf['colors']['border']))
        qp.drawRect(r)
        path = QPainterPath()
        pfreq, ptime, pex, tex = 0.0, 0.0, 0.0, 0.0
        qp.setPen(QColor(self.conf['colors']['line']))
        for x, y in enumerate(argmin(self.amp, 0)):
            if self.amp[y, x] > self.thresh:
                continue
            t, f = self.time[x], self.freq[y]
            if self.conf['calculations']['log10-freq']:
                f = log10(f)
            if pfreq == 0.0:
                path.moveTo(x * self.sx, y * self.sy)
            else:
                path.lineTo(x * self.sx, y * self.sy)
                pex += sqrt((f - pfreq)**2 + (t - ptime)**2)
                tex += t - ptime
            ptime, pfreq = t, f
        qp.drawPath(path)
        self.data['pex'] = pex
        self.data['tex'] = tex
        try:
            self.data['fex'] = pex / tex
        except ZeroDivisionError:
            self.data['fex'] = 0.0
        #self.fexChanged.emit() # TODO replace this?

    def resizeEvent(self, e):
        self.sx = self.width() / self.image.width()
        self.sy = self.height() / self.image.height()

    def mousePressEvent(self, e):
        if self.cursorX.isVisible():
            pass
        elif self.eraser.isVisible():
            copyto(self.amp_back, self.amp)
            pass
        elif e.button() == Qt.MiddleButton:
            self.fit()
        else:
            self.__pos = self.pos()
            self.__size = self.size()
            self.__epos = self.mapToParent(e.pos())

    def mouseMoveEvent(self, e):
        # TODO refactor this rats nest
        # TODO add eraser size changes
        y = e.pos().y()
        if y < self.eraser.maxheight / 2.0:
            fixed = y * 2.0
        elif y > self.height() - self.eraser.maxheight / 2.0:
            fixed = (self.height() - y) * 2.0
        else:
            fixed = self.eraser.maxheight
        self.eraser.setFixedHeight(int(clip(fixed, 0, self.eraser.maxheight)))
        x = e.pos().x() - self.eraser.width() / 2.0
        y = e.pos().y() - self.eraser.height() / 2.0
        self.eraser.move(x, y)

        x = e.pos().x()
        y = e.pos().y()
        self.cursorX.move(x, 0)
        self.cursorY.move(0, y)
        s = self.imgdata.shape
        x = clip(int(s[1] * e.pos().x() / self.width()), 0, self.time.size)
        y = clip(int(s[0] * e.pos().y() / self.height()), 0, self.freq.size)
        self.mouseOver.emit(self.time[x], self.time[y])
        if e.buttons() and self.cursorX.isVisible():
            self.mouseCursorEvent(e)
        elif e.buttons() and self.eraser.isVisible():
            self.mouseEraseEvent(e)
        elif e.buttons():
            self.mouseNormalEvent(e)

    def mouseCursorEvent(self, e):
        pass

    def mapToImgdata(self, x, y):
        s = self.imgdata.shape
        x *= s[1] / self.width()
        y *= s[0] / self.height()
        x = int(clip(x, 0, s[1]))
        y = int(clip(y, 0, s[0]))
        return x, y

    def mouseEraseEvent(self, e):
        w = self.eraser.width()
        h = self.eraser.height()
        x = e.pos().x() - w / 2.0
        y = e.pos().y() - h / 2.0
        x1, y1 = self.mapToImgdata(x, y)
        x2, y2 = self.mapToImgdata(x + w, y + h)
        self.amp[y1:y2, x1:x2] = 255.0
        self.update(x, 0, w, self.height())
        self.stored = False

    def mouseNormalEvent(self, e):
        if e.buttons() == Qt.LeftButton:
            diff = self.mapToParent(e.pos()) - self.__epos + self.__pos
            self.move(diff)
        elif e.buttons() == Qt.RightButton:
            diff = self.mapToParent(e.pos()) - self.__epos
            w = self.__size.width() + diff.x()
            h = self.__size.height() + diff.y()
            self.resize(w, h)

    def mouseReleaseEvent(self, e):
        self.cursorX.resize(1, self.height() * 4)
        self.cursorY.resize(self.width() * 4, 1)
        self.update(self.contentsRect())