how to right click to save picture or file? - python

I wirte a pyqt5 code to show picture or file from our local computer. After this, I have no idea to save the picture or file showed in window on other path. The only way I figured out is copy them. Any suggestion or tips would be appreciated.
Here is the code to show picture or file:
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
class fileDialogdemo(QWidget):
def __init__(self,parent=None):
super(fileDialogdemo, self).__init__(parent)
layout=QVBoxLayout()
self.btn=QPushButton("Load Picture")
self.btn.clicked.connect(self.getimage)
layout.addWidget(self.btn)
self.le=QLabel('')
layout.addWidget(self.le)
self.btn1=QPushButton('Load text file')
self.btn1.clicked.connect(self.getFiles)
layout.addWidget(self.btn1)
self.contents=QTextEdit()
layout.addWidget(self.contents)
self.setLayout(layout)
self.setWindowTitle('File Dialog ')
def getimage(self):
image_file,_=QFileDialog.getOpenFileName(self,'Open file','C:\\','Image files (*.jpg *.gif *.png *.jpeg)')
self.le.setPixmap(QPixmap(image_file))
def getFiles(self):
dig=QFileDialog()
dig.setFileMode(QFileDialog.AnyFile)
dig.setFilter(QDir.Files)
if dig.exec_():
filenames=dig.selectedFiles()
f=open(filenames[0],'r')
with f:
data=f.read()
self.contents.setText(data)
if __name__ == '__main__':
app=QApplication(sys.argv)
ex=fileDialogdemo()
ex.show()
sys.exit(app.exec_())
Here is the code to save I am trying, but there is something wrong:
def contextMenuEvent(self, event):
cmenu = QMenu(self)
saveAct = cmenu.addAction("Save as")
action = cmenu.exec_(self.mapToGlobal(event.pos()))
if action == saveAct:
filename = QFileDialog.getSaveFileName(self)
shutil.copyfile(self.image_file, filename)
Except for the copy idea, is there any better way ?

Do not copy the image as nobody guarantees that after loading the image the original image is in the same location or still exists. Instead, retrieve the QPixmap from the QLabel and save it:
def contextMenuEvent(self, event):
cmenu = QMenu(self)
saveAct = cmenu.addAction("Save as")
action = cmenu.exec_(self.mapToGlobal(event.pos()))
if action == saveAct:
filename, _ = QFileDialog.getSaveFileName(self)
pixmap = self.le.pixmap()
if pixmap is not None and filename:
pixmap.save(filename)

Related

How can i pass a modified image from a tool window to the main window?

In the main window i can open, save or create an image. If i modify this image in a second window, how can i send the result in the main window when i close the second windows?
import sys
import cv2
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.uic import loadUi
This is the second windows where i pass the image loaded in the main window and i made some modification.
class toolWindows(QtWidgets.QMainWindow):
def __init__(self, image):
super(toolWindows, self).__init__()
self._image = image
self._result = None
menubar = self.menuBar()
modifyImage = QtWidgets.QAction('&modifyTheImage', self)
modifyImage.setShortcut('Ctrl+M')
modifyImage.setStatusTip('modify the image image')
modifyImage.triggered.connect(self.doSomething)
fileMenu = menubar.addMenu('&modify')
fileMenu.addAction(modifyImage)
self.show()
def doSomething(self):
self._result = cv2.Canny(self._image,100,200)
cv2.imshow('doSomething',self._result)
return self._result
def getResult(self):
return self._result
this is the main windows
class mainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super(mainWindow, self).__init__(*args, **kwargs)
self.mainWindowImageSource = None
self.mainWindowImageResult = None
menubar = self.menuBar()
LoadImage = QtWidgets.QAction('&LoadImage', self)
LoadImage.setShortcut('Ctrl+L')
LoadImage.setStatusTip('Load an image')
LoadImage.triggered.connect(self.loadImage)
toolWin = QtWidgets.QAction('&tool', self)
toolWin.setShortcut('Ctrl+T')
toolWin.setStatusTip('open the tool win')
toolWin.triggered.connect(self.openToolWindow)
checkResult = QtWidgets.QAction('&Check', self)
checkResult.setShortcut('Ctrl+K')
checkResult.setStatusTip('check that tool win pass the image on close')
checkResult.triggered.connect(self.checkResultImage)
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(LoadImage)
fileMenu.addAction(toolWin)
fileMenu.addAction(checkResult)
self.show()
def loadImage(self):
fileName, filter = QtWidgets.QFileDialog.getOpenFileName(self, 'Open File', 'C:\\', "Image Files (*.jpg)")
if fileName:
self.mainWindowImageSource = cv2.imread(fileName, cv2.IMREAD_COLOR)
else:
print('Invalid Image')
def openToolWindow(self):
self.tw = toolWindows(self.mainWindowImageSource)
self.tw.show()
self.mainWindowImageResult = self.tw.getResult()
def checkResultImage(self):
print("ol")
cv2.imshow('test', self.mainWindowImageResult)
cv2.waitKey(0)
cv2.destroyAllWindows()
##### APP MAIN ####
app = QtWidgets.QApplication(sys.argv)
window = mainWindow()
# this start a loop till exit
sys.exit(app.exec_())
I found a solution accordingly with varius post i found.
To get a result from the second windows the simplest way is probably to use a QDialog.
in the main windows i change the code in this way:
def openToolWindow(self):
self.tw = toolWindows(self.mainWindowImageSource)
self.tw.show()
check = True
if self.tw.exec_() == QtWidgets.QDialog.Accepted:
self.mainWindowImageResult = self.tw.getResult()
cv2.imshow('test', self.mainWindowImageResult)
cv2.waitKey(0)
cv2.destroyAllWindows()
In this way wheh it open the second windows (the tool windows), the main wait for the accept button and take the result with getResult() -a function that simply return the image-

Scribble pad over an image in pyQt4

suppose I want to load an image and scribble on it. But If the image is large then it could not fit into the image viewer. Hence I want a scrollbar. How do I do this?
For example: lets say the image is:
and when i load it into my program it becomes:
Now I want to put it into the scrollbar widget(so that the image can be accessed fully) and the most important thing is I want to make any kind of drawing(scribble) on it. How do I do this using PyQt4? Is there any python code available for that?
If you want to place an image with QScrollbar a possible option is to use QScrollArea, but if you indicate that you want to draw the correct thing is to use QGraphicsView since it allows you to add items. For example to make the scribble you could use QGraphicsPathItem and for the image QGraphicsPixmapItem as I show below:
from PyQt4 import QtCore, QtGui
class ScribbleView(QtGui.QGraphicsView):
def __init__(self, parent=None):
super(ScribbleView, self).__init__(parent)
self.setScene(QtGui.QGraphicsScene(self))
self._image_item = QtGui.QGraphicsPixmapItem()
self.scene().addItem(self._image_item)
self.initial_path()
def initial_path(self):
self._path = QtGui.QPainterPath()
pen = QtGui.QPen(QtGui.QColor("green"), 4, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap)
self._path_item = self.scene().addPath(self._path, pen)
#QtCore.pyqtSlot()
def load_image(self):
filename = QtGui.QFileDialog.getOpenFileName(self,
"Open Image", QtCore.QDir.currentPath(),
"Image Files (*.png *.jpg *.bmp)")
if filename:
self._image_item.setPixmap(QtGui.QPixmap(filename))
def mousePressEvent(self, event):
if not self._image_item.pixmap().isNull():
self._path.moveTo(self.mapToScene(event.pos()))
self._path_item.setPath(self._path)
super(ScribbleView, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if not self._image_item.pixmap().isNull():
self._path.lineTo(self.mapToScene(event.pos()))
self._path_item.setPath(self._path)
super(ScribbleView, self).mousePressEvent(event)
def mouseReleaseEvent(self, event):
if not self._image_item.pixmap().isNull():
self._path.lineTo(self.mapToScene(event.pos()))
self._path_item.setPath(self._path)
self.initial_path()
super(ScribbleView, self).mouseReleaseEvent(event)
class ScribbleWidget(QtGui.QMainWindow):
def __init__(self, parent=None):
super(ScribbleWidget, self).__init__(parent)
view = ScribbleView()
self.setCentralWidget(view)
menubar = self.menuBar()
file_menu = menubar.addMenu("&File")
image_action = file_menu.addAction("Load Image")
image_action.triggered.connect(view.load_image)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = ScribbleWidget()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())

Is it possible to save a complete state of a QApplication and reload it afterward?

I'm facing an issue to save a complete state of a Qt window application.
What I'd like to di is save a file which contains everything about the window and then, if I want, I could reload it to obtain the same window with all previous variable in it (reload their values)
For instance, I've this code
import pickle
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class SurfViewer(QMainWindow):
def __init__(self, parent=None):
super(SurfViewer, self).__init__()
self.parent = parent
self.centralWidget = QWidget()
self.color = self.centralWidget.palette().color(QPalette.Background)
self.setCentralWidget(self.centralWidget)
self.plotview = QGroupBox(" ")
self.layout_plotview = QVBoxLayout()
self.test = QLineEdit('0.0')
self.Button_Loadstate = QPushButton('Load state')
self.Button_Savestate = QPushButton('Save state')
self.layout_plotview.addWidget(self.test)
self.layout_plotview.addWidget(self.Button_Savestate)
self.layout_plotview.addWidget(self.Button_Loadstate)
self.centralWidget.setLayout(self.layout_plotview)
self.Button_Savestate.clicked.connect(self.Savestate)
self.Button_Loadstate.clicked.connect(self.Loadstate)
def Savestate(self,):
fileName = QFileDialog.getSaveFileName(self,'Save State')
if (fileName[0] == '') :
return
file_pi = open(fileName[0] + '.state', 'wb')
pickle.dump(self, file_pi)
file_pi.close()
return
def Loadstate(self,):
fileName = QFileDialog.getOpenFileName(self,'Load State' , "", "data files (*.state)")
if (fileName[0] == '') :
return
filehandler = open(fileName[0], 'rb')
self = pickle.load(filehandler)
filehandler.close()
return
def main():
app = QApplication(sys.argv)
ex = SurfViewer(app)
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
which give me this :
I create a window with a QLineEdit and two buttons (one for saving and one for loading). Then I use pickle to save the self of the SurfViewer class instance in a Savestatefunction. Then I try to reload the previous state with pickle self = pickle.load(filehandler). It seems that it will not be so simple because I cannot assign the self variable.
If anyone could give some insights to perform that without saving every variable by hand.

QGraphicsPixmapItem won't show over the other QGraphicsPixmapItem

What am I doing wrong here? I expect that "image1.jpg" is shown over "image.jpg" ,at position where I've clicked, but it does not. Here is my code (image1.jpg is 10 times smaller then image.jpg):
import sys
from PyQt4 import QtGui, QtCore
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class DrawImage(QMainWindow):
def __init__(self, parent=None):
super(QMainWindow, self).__init__(parent)
self.setWindowTitle('Select Window')
self.local_image = QImage('image.JPG')
self.local_grview = QGraphicsView()
self.setCentralWidget( self.local_grview )
self.local_scene = QGraphicsScene()
self.image_format = self.local_image.format()
self.pixMapItem = QGraphicsPixmapItem(QPixmap(self.local_image), None, self.local_scene)
self.pixMapItem.setZValue(10.0)
self.local_grview.setScene( self.local_scene )
self.pixMapItem.mousePressEvent = self.pixelSelect
def pixelSelect( self, event ):
position = QPoint( event.pos().x(), event.pos().y())
local_image = QImage('image1.JPG')
pixMapItem = QGraphicsPixmapItem(QPixmap(local_image), self.pixMapItem, self.local_scene)
pixMapItem.setZValue(100.0)
pixMapItem.setPos(position.x(), position.y());
print position, self.pixMapItem.zValue(), pixMapItem.zValue()
def main():
app = QtGui.QApplication(sys.argv)
form = DrawImage()
form.show()
app.exec_()
if __name__ == '__main__':
main()
Edit 1 I've tried self.local_grview.setUpdatesEnabled(True) and updating scene at the end of pixelSelect method: self.local_grview.update() , nothing changed
Your code looks correct and works as expected for me ie. the second smaller image displays over the first.
Have you tried displaying just the second image? Perhaps you have an incorrect path which is causing your second image not to show.

Pasting QMimeData to another window's QTextEdit

I'm still quite a newbie with Python and PyQt so I have a really basic question. I have some text and images in a parent window inside a QTextEdit widget and I'm trying to copy all the content to a child window's QTextEdit. But for some reason I can't get it to copy the image - only the text is copied not the image. Here's a snippet of the code that's giving me trouble:
self.textEdit.selectAll()
data = self.textEdit.createMimeDataFromSelection()
self.child_window.textEdit.insertFromMimeData(data) # doesn't work with images
Here's is the small program that I'm trying to run:
import sys
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class MyWindow(QtGui.QWidget):
def __init__(self,parent=None):
super(MyWindow,self).__init__(parent)
self.textEdit = QtGui.QTextEdit(self)
self.textEdit.setText("Hello World\n")
self.pushButton = QtGui.QPushButton(self)
self.pushButton.setText("Copy and paste to Child Window")
self.pushButton.clicked.connect(self.click_copy_data)
self.pushButton2 = QtGui.QPushButton(self)
self.pushButton2.setText("Insert Image")
self.pushButton2.clicked.connect(self.click_file_dialog)
self.layoutVertical = QtGui.QVBoxLayout(self)
self.layoutVertical.addWidget(self.textEdit)
self.layoutVertical.addWidget(self.pushButton2)
self.layoutVertical.addWidget(self.pushButton)
self.setGeometry(150, 150,640, 480)
self.child_window = CustomWindow(self)
self.child_window.show()
def click_copy_data(self):
self.textEdit.selectAll()
data = self.textEdit.createMimeDataFromSelection()
self.child_window.textEdit.insertFromMimeData(data)
def click_file_dialog(self):
filePath = QtGui.QFileDialog.getOpenFileName(
self,
"Select an image",
".",
"Image Files(*.png *.gif *.jpg *jpeg *.bmp)"
)
if not filePath.isEmpty():
self.insertImage(filePath)
def insertImage(self,filePath):
imageUri = QtCore.QUrl(QtCore.QString("file://{0}".format(filePath)))
image = QtGui.QImage(QtGui.QImageReader(filePath).read())
self.textEdit.document().addResource(
QtGui.QTextDocument.ImageResource,
imageUri,
QtCore.QVariant(image)
)
imageFormat = QtGui.QTextImageFormat()
imageFormat.setWidth(image.width())
imageFormat.setHeight(image.height())
imageFormat.setName(imageUri.toString())
textCursor = self.textEdit.textCursor()
textCursor.movePosition(
QtGui.QTextCursor.End,
QtGui.QTextCursor.MoveAnchor
)
textCursor.insertImage(imageFormat)
# This will hide the cursor
blankCursor = QtGui.QCursor(QtCore.Qt.BlankCursor)
self.textEdit.setCursor(blankCursor)
class CustomWindow(QtGui.QDialog):
def __init__(self,parent=None):
super(CustomWindow,self).__init__(parent)
self.textEdit = QtGui.QTextEdit(self)
self.layoutVertical = QtGui.QVBoxLayout(self)
self.layoutVertical.addWidget(self.textEdit)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
app.setApplicationName('MyWindow')
main = MyWindow()
main.show()
sys.exit(app.exec_())
The way the program works is that you have some text inside the main window and then you insert an image. Then you click "Copy and paste to Child Window" button and it should paste all the contents to the child, including the image - but that doesn't work that way it's supposed to - the text is copied but I get a little file icon where the image should be.
I would appreciate your help on this.
Paul
QTextEdit doesn't decode image MIME types by default, so just subclass it to add support, you'll need to reimplement canInsertFromMimeData and insertFromMimeData, also try QTextBrowser instead. Just add this to your script:
class MyTextBrowser(QtGui.QTextBrowser):
def __init__(self, parent=None):
super(MyTextBrowser, self).__init__(parent)
self.setReadOnly(False)
def canInsertFromMimeData(self, source):
if source.hasImage():
return True
else:
return super(MyTextBrowser, self).canInsertFromMimeData(source)
def insertFromMimeData(self, source):
if source.hasImage():
image = QtCore.QVariant(source.imageData())
document = self.document()
document.addResource(
QtGui.QTextDocument.ImageResource,
QtCore.QUrl("image"),
image
)
cursor = self.textCursor()
cursor.insertImage("image")
super(MyTextBrowser, self).insertFromMimeData(source)
And change self.textEdit = QtGui.QTextEdit(self) into self.textEdit = MyTextBrowser(self) on both widgets.
This is the solution I ended using as suggested by X.Jacobs.
html = parent_textEdit.toHtml()
child_textEdit.setHtml(html)
I was making things more complicated. When I realized that QTextEdit keeps track of where the image is stored as a url inside the html generated by toHtml() then it all made sense.

Categories