From f2701522c11ce319059186c485cd4dbaf84c0660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolthera=20van=20H=C3=B6vell=20tot=20Westerflier?= Date: Thu, 17 May 2018 15:25:19 +0200 Subject: [PATCH 1/2] Replace focal point selection gui with a more precise one. This makes a new widget that the user can click/drag their mouse on to change the focalpoint. Also replaces the two hardcoded buttons with a QButtonBox as that'll handle all the boring gnome/apple/windows/kde/other desktop UX differences. --- kritatoot/UploadTab.py | 283 +++++++++++++++++++++-------------------- 1 file changed, 142 insertions(+), 141 deletions(-) diff --git a/kritatoot/UploadTab.py b/kritatoot/UploadTab.py index b51c8ad..11379f0 100644 --- a/kritatoot/UploadTab.py +++ b/kritatoot/UploadTab.py @@ -12,7 +12,9 @@ from PyQt5.QtGui import * from PyQt5.QtWidgets import * from PyQt5.QtCore import * - + +from krita import * + from .Toot import uploadmedia, postmedia, post from .TempMedia import saveTempMedia, removeTempMedia @@ -72,170 +74,159 @@ def addtext(self): self.alttext = text self.accept() - - -class FocalPointDialog(QDialog): + +class focalPointWidget(QWidget): """ - A modal dialogbox for users to supply a focal point: a location that acts - like an achor or pivot. + A widget which draws an image and you can select a focal point. """ - def __init__(self, parent=None, focal=None): - """ - focal - (tuple) or None - """ + def __init__(self, parent = None): + super(focalPointWidget, self).__init__(parent) + self.focalPoint = [0.0, 0.0] + self.image = QImage() + self.mouseIn = False + self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) - super(FocalPointDialog, self).__init__(parent) # Py2 + def setImage(self, image = QImage()): + self.image = image + self.update() + + def paintEvent(self, event): - self.setModal(True) + painter = QPainter(self) - self.MAXROW = 3 - self.MAXCOL = 3 + image = self.image.scaled(self.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation) + imageOffsetLeft = (self.width()-image.width())/2 + imageOffsetTop = (self.height()-image.height())/2 - # holds the (row,col) of a currently selected, but uncommitted, focal point. - # a sel focal point is commited when user clicks add. - self.tempidx = focal + painter.setBrush(Qt.black) + painter.setPen(QPen(Qt.NoPen)) + painter.drawRect(0, 0, self.width(), self.height()) + painter.drawImage(imageOffsetLeft, imageOffsetTop, image) - # (row,col) of the last commited focal point - self.focalidx = focal + painter.save() + # Let's make this super fancy and setup an area of interest! - # the focalidx, (row, col), converted to focal point coordinates (x, y) - self.focalcoords = (0.0, 0.0) + focalXY = QPointF(((self.focalPoint[0] + 1.0) / 2), ((self.focalPoint[1] + 1.0) / 2)) + aoi = 0.333 # The area of interest is ~ a third of the image. + offset = QPointF(aoi/2, aoi/2) + topleft = focalXY - offset + bottomright = focalXY + offset + - if self.focalidx: - self.focalcoords = self.indexToCoordinates(self.focalidx[0], self.focalidx[1]) + # First let's draw the whiteish square in the middle. + # This is necessary for very dark images. - self.focallabel = QLabel('Choose a focal point (anchor)') + white = QRect( QPoint( (topleft.x()*image.width()) + imageOffsetLeft, (topleft.y() * image.height())+imageOffsetTop), QPoint((bottomright.x()*image.width())+imageOffsetLeft, (bottomright.y()*image.height())+imageOffsetTop)) + painter.setBrush(Qt.white) + painter.setOpacity(0.3) + painter.drawRect(white) - gridLayout = QGridLayout() - gridLayout.setHorizontalSpacing(0) - gridLayout.setVerticalSpacing(0) + # Then draw 4 black sections to indicate the non-visible area. + # This is for very bright images. + painter.setBrush(Qt.black) + painter.drawRect(QRect(QPoint(0, 0), white.topLeft())) + painter.drawRect(QRect(white.bottomRight(), QPoint(self.width(), self.height()))) + painter.drawRect(QRect(QPoint(0, white.bottom()), QPoint(white.left(), self.height()))) + painter.drawRect(QRect(QPoint(white.right(), 0), QPoint(self.width(), white.top()))) + painter.restore() - self.focalbuttons = [None] * self.MAXROW - for i in range(self.MAXROW): - self.focalbuttons[i] = [None] * self.MAXCOL - - for i in range(self.MAXROW): - - for j in range(self.MAXCOL): - button = QToolButton() - button.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) - - if self.focalidx and i == self.focalidx[0] and j == self.focalidx[1]: - button.setStyleSheet("background-color: #2588d0;") - - gridLayout.addWidget(button, i, j) - - button.clicked.connect(partial(self.toggleFocal, i, j)) - - self.focalbuttons[i][j] = button - + def setFocalPointFromMousePos(self, pos = QPoint()): + image = QSize(self.width(), self.height()) + if self.image.width() != 0 and self.image.height() != 0: + if self.image.width() > self.image.height(): + image = QSize(self.width(), (self.image.height()/self.image.width()) * self.height()) + else: + image = QSize((self.image.width()/self.image.height()) * self.width(), self.height()) + + imageOffsetLeft = (self.width() - image.width() ) / 2 + imageOffsetTop = (self.height() - image.height()) / 2 - centerLayout = QHBoxLayout() - centerLayout.addStretch(1) - centerLayout.addLayout(gridLayout); - centerLayout.addStretch(1) + x = (((pos.x() - imageOffsetLeft) / image.width() ) * 2 ) - 1.0 + y = (((pos.y() - imageOffsetTop) / image.height() ) * 2 ) - 1.0 + self.focalPoint = [x, y] + self.update() + + def mousePressEvent(self, event): - # controls - self.addbutton = QToolButton() - self.addbutton.setText('Add') + self.mouseIn = True + self.setFocalPointFromMousePos(event.pos()) - self.exitbutton = QToolButton() - self.exitbutton.setText('Cancel') + event.accept() - controlsLayout = QHBoxLayout() - controlsLayout.addStretch(1) - controlsLayout.addWidget(self.exitbutton) - controlsLayout.addWidget(self.addbutton) + def mouseReleaseEvent(self, event): + self.mouseIn = False + self.setFocalPointFromMousePos(event.pos()) - mainLayout = QVBoxLayout() + event.accept() - mainLayout.addWidget(self.focallabel) - mainLayout.addLayout(centerLayout) - mainLayout.addLayout(controlsLayout) - - self.setLayout(mainLayout) + def mouseMoveEvent(self, event): + if (self.mouseIn == True): + self.setFocalPointFromMousePos(event.pos()) + event.accept() + + def sizeHint(self): + return QSize(256, 256) + + def setFocalPoint(self, x, y): + self.focalPoint = [x, y] + self.update() - self.defaultStyleSheet = self.addbutton.styleSheet() + def getFocalPoint(self): + return self.focalPoint - # slots - self.addbutton.clicked.connect(self.addfocal) - self.exitbutton.clicked.connect(self.accept) + +class FocalPointDialog(QDialog): + """ + A modal dialogbox for users to supply a focal point: a location that acts + like an achor or pivot. + """ - def indexToCoordinates(self, row, col): + def __init__(self, parent = None, focal = (0, 0), image = QImage()): """ - Converts a row & col into a corresponding (x,y) tuple in - focal point space. In focal point space, x = -1.0 to 1.0 - and y = -1.0 to 1.0 + focal - (tuple) or None + image - (QImage) or None """ + super(FocalPointDialog, self).__init__(parent) # Py2 - # Note - hard coded values. assuming 3x3 grid - # using perimiter values except for center - if row == 0: - if col == 0: - return (-1.0, 1.0) - elif col == 1: - return (0.0, 1.0) - elif col == 2: - return (1.0, 1.0) - elif row == 1: - if col == 0: - return (-1.0, 0.0) - elif col == 1: - return (0.0, 0.0) - elif col == 2: - return (1.0, 0.0) - elif row == 2: - if col == 0: - return (-1.0, -1.0) - elif col == 1: - return (0.0, -1.0) - elif col == 2: - return (1.0, -1.0) + self.setModal(True) + self.setWindowTitle('Set Image Focalpoint') + self.focalcoords = focal - - def toggleFocal(self, row, column): - """ - """ - #print("CLICKED (" + str(row) + "," + str(column) + ")") - - # clear all - for i in range(self.MAXROW): - for j in range(self.MAXCOL): - self.focalbuttons[i][j].setStyleSheet(self.defaultStyleSheet) - - if self.focalidx: - # already selected? - if row == self.focalidx[0] and column == self.focalidx[1]: - self.focalbuttons[row][column].setStyleSheet(self.defaultStyleSheet) - self.tempidx = None - else: - self.focalbuttons[row][column].setStyleSheet("background-color: #2588d0;") - self.tempidx = (row, column) - else: - # first time a selection has been made - self.focalbuttons[row][column].setStyleSheet("background-color: #2588d0;") - self.tempidx = (row, column) - + self.focallabel = QLabel('Choose a focal point (anchor)') + + mainLayout = QVBoxLayout() + mainLayout.addWidget(self.focallabel) + + self.imageWidget = focalPointWidget() + self.imageWidget.setImage(image) + self.imageWidget.setFocalPoint(self.focalcoords[0], self.focalcoords[1]) + mainLayout.addWidget(self.imageWidget) + + + # controls + # Let's replace this with a QDialogButtonBox, then that sorts itself for differences between Desktop Enviroments :) + + buttons = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel) + buttons.accepted.connect(self.addfocal) + buttons.rejected.connect(self.accept) + + mainLayout.addWidget(buttons) + + self.setLayout(mainLayout) def addfocal(self): """ """ - - self.focalidx = self.tempidx - - if self.focalidx: - # convert the row/col to actual coordinates - self.focalcoords = self.indexToCoordinates(self.focalidx[0], self.focalidx[1]) - else: - self.focalcoords = (0.0, 0.0) + focal = self.imageWidget.getFocalPoint() + self.focalcoords = (focal[0], focal[1]) self.accept() @@ -282,7 +273,6 @@ def __init__(self, parent=None): self.alttext = None # the selected row and column, if any (otherwise None) - self.selfocalidx = None self.focalcoords = (0.0, 0.0) # list of sites where the app is registered and authorized @@ -492,20 +482,32 @@ def toggleVisibility(self): def addFocalPoint(self): """ """ - - focalui = FocalPointDialog(self, self.selfocalidx); + image = QImage() + doc = Krita.instance().activeDocument() + if doc is not None: + if doc.width() < 512 and doc.height() < 512: + # This is probably pixel art. + # We will not have to request a thumbnail for this. + image = doc.projection(doc.width(), doc.height()) + elif doc.width() > doc.height(): + h = (doc.height()/doc.width())*512 + image = doc.thumbnail(512, h) + else : + w = (doc.width()/doc.height())*512 + image = doc.thumbnail(w, 512) + + focalui = FocalPointDialog(self, self.focalcoords, image); focalui.exec_() - self.selfocalidx = focalui.focalidx + self.focalcoords = focalui.focalcoords + #if self.selfocalidx: + # focalicon = self.icons['focal'] + # self.focalpoint.setIcon(focalicon) - if self.selfocalidx: - focalicon = self.icons['focal'] - self.focalpoint.setIcon(focalicon) - self.focalcoords = focalui.focalcoords - else: - focalicon = self.icons['nofocal'] - self.focalpoint.setIcon(focalicon) - self.focalcoords = (0.0, 0.0) + #else: + # focalicon = self.icons['nofocal'] + # self.focalpoint.setIcon(focalicon) + # self.focalcoords = (0.0, 0.0) def upload(self): @@ -628,7 +630,6 @@ def upload(self): visibleicon = self.icons['nohide'] self.hidden.setIcon(visibleicon) - self.selfocalidx = None self.focalcoords = (0.0, 0.0) focalicon = self.icons['nofocal'] self.focalpoint.setIcon(focalicon) From 7da16f08b67f3259fc3821d2c99237a0219e3bbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolthera=20van=20H=C3=B6vell=20tot=20Westerflier?= Date: Sun, 15 Nov 2020 17:09:20 +0100 Subject: [PATCH 2/2] Give better feedback on focal point, fix the range, and limit to range instead of reset. --- kritatoot/Toot.py | 8 ++++---- kritatoot/UploadTab.py | 41 ++++++++++++++++++++--------------------- 2 files changed, 24 insertions(+), 25 deletions(-) mode change 100644 => 100755 kritatoot/Toot.py mode change 100644 => 100755 kritatoot/UploadTab.py diff --git a/kritatoot/Toot.py b/kritatoot/Toot.py old mode 100644 new mode 100755 index 09ba763..f98554c --- a/kritatoot/Toot.py +++ b/kritatoot/Toot.py @@ -92,12 +92,12 @@ def uploadmedia(url, access_token, filename, description=None, focus=(0.0,0.0)): focus = (0.0,0.0) if focus[0] < -1.0 or focus[0] > 1.0: - print('Focal point out of range. Setting to (0,0)') - focus = (0.0,0.0) + print('Warning: Focal point out of range, bounding.') + focus = (min(1.0, max(-1.0, focus[0])), focus[1]) if focus[1] < -1.0 or focus[1] > 1.0: - print('Focal point out of range. Setting to (0,0)') - focus = (0.0,0.0) + print('Warning: Focal point out of range, bounding.') + focus = (focus[0], min(1.0, max(-1.0, focus[1]))) # Add a focal point, default is dead-center: (0,0) data_focus = '''\r\n--------------------------d74496d66958873e diff --git a/kritatoot/UploadTab.py b/kritatoot/UploadTab.py old mode 100644 new mode 100755 index 11379f0..a2e3e6a --- a/kritatoot/UploadTab.py +++ b/kritatoot/UploadTab.py @@ -107,7 +107,7 @@ def paintEvent(self, event): painter.save() # Let's make this super fancy and setup an area of interest! - focalXY = QPointF(((self.focalPoint[0] + 1.0) / 2), ((self.focalPoint[1] + 1.0) / 2)) + focalXY = QPointF(((self.focalPoint[0] + 1.0) / 2), (((-1.0*self.focalPoint[1]) + 1.0) / 2)) aoi = 0.333 # The area of interest is ~ a third of the image. offset = QPointF(aoi/2, aoi/2) topleft = focalXY - offset @@ -131,6 +131,11 @@ def paintEvent(self, event): painter.drawRect(QRect(QPoint(0, white.bottom()), QPoint(white.left(), self.height()))) painter.drawRect(QRect(QPoint(white.right(), 0), QPoint(self.width(), white.top()))) + painter.setBrush(Qt.white) + painter.setPen(Qt.white) + painter.setOpacity(1.0) + painter.drawText(5, self.height()-10, str(", ").join(format(x, "1.2f") for x in self.focalPoint)) + painter.restore() @@ -148,7 +153,8 @@ def setFocalPointFromMousePos(self, pos = QPoint()): x = (((pos.x() - imageOffsetLeft) / image.width() ) * 2 ) - 1.0 y = (((pos.y() - imageOffsetTop) / image.height() ) * 2 ) - 1.0 - self.focalPoint = [x, y] + self.focalPoint = [x, y * -1.0] + print(self.focalPoint) self.update() def mousePressEvent(self, event): @@ -485,29 +491,22 @@ def addFocalPoint(self): image = QImage() doc = Krita.instance().activeDocument() if doc is not None: - if doc.width() < 512 and doc.height() < 512: - # This is probably pixel art. - # We will not have to request a thumbnail for this. - image = doc.projection(doc.width(), doc.height()) - elif doc.width() > doc.height(): - h = (doc.height()/doc.width())*512 - image = doc.thumbnail(512, h) - else : - w = (doc.width()/doc.height())*512 - image = doc.thumbnail(w, 512) - + image = doc.projection(0, 0, doc.width(), doc.height()) + if doc.width() > 512 and doc.height() > 512: + # This is probably not pixel art. + image = image.scaled(512, 512, Qt.KeepAspectRatio, Qt.SmoothTransformation) + focalui = FocalPointDialog(self, self.focalcoords, image); focalui.exec_() self.focalcoords = focalui.focalcoords - #if self.selfocalidx: - # focalicon = self.icons['focal'] - # self.focalpoint.setIcon(focalicon) - - #else: - # focalicon = self.icons['nofocal'] - # self.focalpoint.setIcon(focalicon) - # self.focalcoords = (0.0, 0.0) + if self.focalcoords[0] != 0.0 and self.focalcoords[1] != 0.0: + focalicon = self.icons['focal'] + self.focalpoint.setIcon(focalicon) + else: + focalicon = self.icons['nofocal'] + self.focalpoint.setIcon(focalicon) + self.focalcoords = (0.0, 0.0) def upload(self):