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.
Related
In my QMainWindow i have a button which opens a new QDialog in the bottom right monitorcorner with a successmessage when i click it.
Now, if i move the QMainWindow to another monitor (i have 3 monitor) and click the button the successmessage popup appears in the monitor where the QMainWindow was opened. What i want is that the popup message appears in the monitor where my QMainWindow actually is. So if i move the QMainWindow to Monitor 1 and click the button, the successpopup should opens in monitor 1. If the QMainWindow is in monitor 2, the successpopup should open in monitor 2 an same for monitor 3.
with
screenNumber = QDesktopWidget().screenNumber(self)
i can read the screennumber where the mainwindow is. and this works fine. Evertime i click the button i read out the screennumber. But i don't found a way, to set the screennumber to my notification.
Any ideas?
Edit:
maybe it helps if i show my notify class
notes.py
from UIs.UI_notify import Ui_Notification
from PyQt5.QtWidgets import QDialog, QApplication, QDesktopWidget
from PyQt5 import QtCore
from PyQt5.QtCore import QRect, QPropertyAnimation, QTimer
import sys
class icon():
checked = "check-circle"
alert = "times-circle"
question = "question-circle"
class notify(QDialog, Ui_Notification):
def __init__(self, parent=None):
super(notify,self).__init__(parent)
self.setupUi(self)
self.setWindowFlag(QtCore.Qt.WindowType.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WidgetAttribute.WA_TranslucentBackground)
## Some helping stuff
############################################################
parent_sSize = QDesktopWidget().screenGeometry(parent)
parent_screenNumber = QDesktopWidget().screenNumber(parent)
sSize = QDesktopWidget().screenGeometry()
screenNumber = QDesktopWidget().screenNumber()
print("notification ScreenNumber = " + str(screenNumber))
print(sSize.width())
print(sSize.height())
print("Parents ScreenNumber = " + str(parent_screenNumber))
print(parent_sSize.width())
print(parent_sSize.height())
self.Note_Exit.clicked.connect(self.close)
## ScreenSize from parent
############################################################
self.hidedPos = QRect(parent_sSize.width()-self.width()-10,
parent_sSize.height()-self.height()+200,
self.frameGeometry().width(),
self.frameGeometry().height())
self.showPos = QRect(parent_sSize.width()-self.width()-10,
parent_sSize.height()-self.height()-50,
self.frameGeometry().width(),
self.frameGeometry().height())
def setNote(self, icon=icon.checked, headline="Headline", text="Text"):
self.icon = icon
self.headline = headline
self.text = text
self.noty_Label_Icon.setText(self.icon)
self.noty_Label_Headline.setText(self.headline)
self.noty_Label_Text.setText(self.text)
self.setGeometry(self.hidedPos)
self.anim = QPropertyAnimation(self,b"geometry")
self.anim.setDuration(700)
self.anim.setEasingCurve(QtCore.QEasingCurve.OutBack)
self.anim.setEndValue(self.showPos)
self.anim.start()
self.notyTimer = QTimer()
self.notyTimer.singleShot(4000,self.hideNote)
def hideNote(self):
self.anim = QPropertyAnimation(self,b"geometry")
self.anim.setDuration(700)
self.anim.setEasingCurve(QtCore.QEasingCurve.InOutBack)
self.anim.setEndValue(self.hidedPos)
self.anim.start()
self.anim.finished.connect(self.close)
if __name__ == "__main__":
notes = QApplication(sys.argv)
dialog = notify()
dialog.show()
sys.exit(notes.exec())
You cannot use the size of the widget during its construction, as at that moment it has a default size (640x480 for top level widgets, 100x30 for widgets created with a parent, including dialogs): the only reliable option is to use the sizeHint() or ensure that the layout has been properly activated with adjustSize().
Then, you don't need the screen to get the target position, as the parent geometry will suffice, but you do need it for the start position, otherwise the dialog will "pop up" at an arbitrary point below the window. Note that QDesktopWidget is considered obsolete, and you should use QScreen instead.
Finally, since you might want to reuse the notification, the start position should be set when the popup is actually being shown, not before. The same goes for the position when hiding (in case the notification could be moved).
class Notify(QDialog, Ui_Notification):
def __init__(self, parent):
super().__init__(parent)
self.setupUi(self)
self.setWindowFlag(QtCore.Qt.WindowType.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
self.showAnim = QPropertyAnimation(self, b'geometry')
self.showAnim.setDuration(700)
self.showAnim.setEasingCurve(QtCore.QEasingCurve.OutBack)
self.hideAnim = QPropertyAnimation(self, b'geometry')
self.hideAnim.setDuration(700)
self.hideAnim.setEasingCurve(QtCore.QEasingCurve.InOutBack)
self.hideTimer = QTimer(self, singleShot=True)
self.hideTimer.setInterval(4000)
self.hideTimer.timeout.connect(self.hideNote)
self.showAnim.finished.connect(self.hideTimer.start)
self.hideAnim.finished.connect(self.close)
def setNote(self, icon=icon.checked, headline="Headline", text="Text"):
self.icon = icon
self.headline = headline
self.text = text
self.noty_Label_Icon.setText(self.icon)
self.noty_Label_Headline.setText(self.headline)
self.noty_Label_Text.setText(self.text)
self.adjustSize() # important!
endRect = self.rect()
center = self.parent().geometry().center()
endRect.moveCenter(center)
screen = QApplication.screenAt(center)
startRect = QRect(endRect)
startRect.moveTop(screen.geometry().bottom())
self.setGeometry(startRect)
self.showAnim.setStartValue(startRect)
self.showAnim.setEndValue(endRect)
self.showAnim.start()
self.show()
def hideNote(self):
rect = self.geometry()
self.hideAnim.setStartValue(rect)
screen = QApplication.screenAt(rect.center())
rect.moveTop(screen.geometry().bottom())
self.hideAnim.setEndValue(rect)
self.hideAnim.start()
if __name__ == "__main__":
notes = QApplication(sys.argv)
notes.setStyle('fusion')
w = QMainWindow()
b = QPushButton('click!')
w.setCentralWidget(b)
w.show()
notify = Notify(w)
b.clicked.connect(lambda: notify.setNote())
sys.exit(notes.exec())
Be aware that if you're going to create the popup every time, you should also set the WA_DeleteOnClose attribute in order to destroy it when closed, otherwise it will be kept in memory.
Note: QTimer.singleShot() is a static function, creating an instance of QTimer to use it is pointless, as that instance won't be used and a new QTimer would be created anyway.
Thank you.
In the meantime i found a solution which works for me.
Look a little dirty but works.
could you tell me, whats the difference between my code an yours?
Why should i use your code, except the fact that you are a better programmer ;-)
I set the WA_DeleteOnClose Attribute.
Good to know that. Thanks
notes.py
from UIs.UI_notify import Ui_Notification
from PyQt5.QtWidgets import QDialog, QApplication
from PyQt5 import QtCore
from PyQt5.QtCore import QRect, QPropertyAnimation, QTimer
import sys
class icon():
checked = "check-circle"
alert = "times-circle"
question = "question-circle"
clock = "clock"
class notify(QDialog, Ui_Notification):
def __init__(self, parent=None):
super(notify,self).__init__(parent)
self.setupUi(self)
self.setWindowFlag(QtCore.Qt.WindowType.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WidgetAttribute.WA_TranslucentBackground)
self.setAttribute(QtCore.Qt.WidgetAttribute.WA_DeleteOnClose)
self.setWindowModality(QtCore.Qt.NonModal)
self.Note_Exit.clicked.connect(self.close)
self.parent_h = self.parent().geometry().height()
self.parent_w = self.parent().geometry().width()
self.parent_x = self.parent().geometry().x()
self.parent_y = self.parent().geometry().y()
self.dialog_w = self.width()
self.dialog_h = self.height()
self.setGeometry(self.parent_x+self.parent_w-self.dialog_w-10, self.parent_y+self.parent_h-self.dialog_h+120, self.dialog_w, self.dialog_h)
## ScreenSize from parent
############################################################
def setNote(self, icon=icon.checked, headline="Headline", text="Text"):
self.icon = icon
self.headline = headline
self.text = text
self.noty_Label_Icon.setText(self.icon)
self.noty_Label_Headline.setText(self.headline)
self.noty_Label_Text.setText(self.text)
self.anim = QPropertyAnimation(self,b"geometry")
self.anim.setDuration(700)
self.anim.setEasingCurve(QtCore.QEasingCurve.OutBack)
self.anim.setEndValue(QRect(self.parent_x+self.parent_w-self.dialog_w-10, self.parent_y+self.parent_h-self.dialog_h-20, self.dialog_w, self.dialog_h))
self.anim.start()
self.notyTimer = QTimer()
self.notyTimer.singleShot(4000,self.hideNote)
def hideNote(self):
self.anim = QPropertyAnimation(self,b"geometry")
self.anim.setDuration(700)
self.anim.setEasingCurve(QtCore.QEasingCurve.InOutBack)
self.anim.setEndValue(QRect(self.parent_x+self.parent_w-self.dialog_w-10, self.parent_y+self.parent_h-self.dialog_h+120, self.dialog_w, self.dialog_h))
self.anim.start()
self.anim.finished.connect(self.close)
if __name__ == "__main__":
notes = QApplication(sys.argv)
dialog = notify()
dialog.show()
sys.exit(notes.exec())
I made a QGraphicsTextItem and set plain text as its content. After that, I set the QGraphicsTextItem inside the QGraphicsWidget.
My question is, Is it possible to resize the QGraphicsTextItem including its text/contents like in this picture:
This is the video of the resizing that I'm asking for.
If this is possible, how can I apply it to the QGraphicsTextItem?
The first picture is the image of the QGraphicsTextItem but I have no idea how to implement the resizing in the video.
Things I've tried:
I tried using the setTextWidth() and setting it to 0.5 but it's not working.
I also tried using adjustSize() to the QGraphicsTextItem but it's also not working.
Code to reproduce the issue:
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.view = QGraphicsView()
scene = QGraphicsScene()
#before resizing
item = QGraphicsTextItem("Line 1 Line 2 Line 3")
item.setFlags(QGraphicsWidget.ItemIsSelectable)
item.setPos(self.view.mapToScene(2, 2))
scene.addItem(item)
#after resizing
item_1 = QGraphicsTextItem("Line 1\nLine 2\nLine 3")
item_1.setFlags(QGraphicsWidget.ItemIsSelectable)
item_1.setPos(self.view.mapToScene(2, 30))
scene.addItem(item_1)
self.view.setScene(scene)
self.setCentralWidget(self.view)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()
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)
I am trying to display a loading gif while other code executes. I am very unfamiliar with PyQt and have tried following the code at this link, which seems to be the standard way of executing a gif. I only want the gif playing and do not want a button. Here is the code I am currently at, but it is very poor.
self.movie = QMovie(coffeeloading.gif, self)
size = self.movie.scaledSize()
self.movie_screen.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.movie_screen.setAlignment(Qt.AlignCenter)
self.movie_screen = QLabel()
main_layout = QVBoxLayout()
main_layout.addWidget(self.movie_screen)
self.setLayout(main_layout)
self.movie.setCacheMode(QMovie.CacheAll)
self.movie.setSpeed(100)
self.movie_screen.setMovie(self.movie)
self.movie.start()
This is supposed to execute after a button press and fill up the whole screen of 240x320, but I dont have any idea how to do it. I have already read through many of the other stackoverflow and other links, but none of them seem to address how to complete this.
As an alternative, you can use this class here.
QtWaitingSpinner extends the QtWidgets.QWidget class and you can start your spinner by simply running
your_QtWaitingSpinnerObject.start()
You can check this answer as well.
# -*- coding: utf-8 -*-
import sys
from PySide.QtGui import *
from PySide.QtCore import *
class test_widget(QWidget):
m_play_state = False
def __init__(self):
super(test_widget, self).__init__()
self.__ui__()
def __ui__(self):
t_lay_parent = QVBoxLayout()
self.m_label_gif = QLabel()
self.m_button_play = QPushButton("Play")
t_lay_parent.addWidget(self.m_label_gif)
t_lay_parent.addWidget(self.m_button_play)
self.m_movie_gif = QMovie("loding.gif")
self.m_label_gif.setMovie(self.m_movie_gif)
self.m_label_gif.setScaledContents(True)
self.m_label_gif.hide()
self.setLayout(t_lay_parent)
self.m_button_play.clicked.connect(self.slt_play)
def slt_play(self):
if self.m_play_state:
self.m_label_gif.hide()
self.m_movie_gif.stop()
self.m_play_state = False
else:
self.m_label_gif.show()
self.m_movie_gif.start()
self.m_play_state = True
if __name__ == "__main__":
app = QApplication(sys.argv)
win = test_widget()
win.show()
sys.exit(app.exec_())
Consider the minimal example below. It works perfectly until I uncomment the following lines:
# self.mainwi = QtGui.QWidget(self)
# self.lineEdit1 = QtGui.QLineEdit(self.mainwi)
# self.setCentralWidget(self.lineEdit1)
If those lines are uncommented, I can write text in the LineEdit-field, but the buttons don't react. Any idea what's wrong with it, how to fix this?
I should add that I am an absolute beginner in programming python.
#!/usr/bin/python
import mpylayer
import sys
from PyQt4 import QtCore
from PyQt4 import QtGui
class DmplayerGUI(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.dirty = False
self.mp = mpylayer.MPlayerControl()
#Toolbar
## items
### Play
self.play = QtGui.QAction(QtGui.QIcon('icons/play_32.png'), 'Play', self)
self.play.setShortcut('Ctrl+A')
self.connect(self.play, QtCore.SIGNAL('triggered()'), self.DPlay)
### Pause
self.pause = QtGui.QAction(QtGui.QIcon('icons/pause_32.png'), 'Pause', self)
self.pause.setShortcut('Ctrl+P')
self.connect(self.pause, QtCore.SIGNAL('triggered()'), self.DPause)
## toolbar
self.toolbar = self.addToolBar('Toolbar')
self.toolbar.addAction(self.play)
self.toolbar.addAction(self.pause)
# self.mainwi = QtGui.QWidget(self)
# self.lineEdit1 = QtGui.QLineEdit(self.mainwi)
# self.setCentralWidget(self.lineEdit1)
# play
def DPlay(self):
self.mp.loadfile('video.mp4')
# pause
def DPause(self):
self.mp.pause(self)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
dp = DmplayerGUI()
dp.show()
sys.exit(app.exec_())
You do not need the mainwi at all in this simple example. Just do
self.lineEdit1 = QtGui.QLineEdit(self)
self.setCentralWidget(self.lineEdit1)
In case you really wanted it, then you need to set the mainwi as the centralwidget
self.mainwi = QtGui.QWidget(self)
self.lineEdit1 = QtGui.QLineEdit(self.mainwi)
self.setCentralWidget(self.mainwi)
do not forget to add some layout for mainwi, since this looks ugly :-)
Anyway, I have to admit, that I do not know why exactly does it "disable" the buttons. But the central widget has to be a child of the window as far as I know.