PyQt: Trouble with asterisk on modification in QPlainTextEdit - python

I'm having a problem with a QPlainTextEdit. I want the "contents have been modified" asterisk to appear in the title bar whenever the contents have been modified.
In the example below, type a few letters. The asterisk appears as it should. Hit Ctrl+S, the asterisk disappears as it should. But then if you type a few more letters... why doesn't the asterisk appear again?
import os, sys
from PyQt4 import QtGui, QtCore
class MyTextEdit(QtGui.QPlainTextEdit):
def __init__(self):
QtGui.QPlainTextEdit.__init__(self)
save_seq = QtGui.QKeySequence.Save
self.save_shortcut = QtGui.QShortcut(save_seq, self, self.save)
QtCore.QObject.connect(self,
QtCore.SIGNAL("modificationChanged(bool)"),
self.on_change)
def on_change(self, is_modified):
print "on_change"
window.setWindowModified(is_modified)
def save(self):
window.setWindowModified(False)
#
app = QtGui.QApplication(sys.argv)
window = QtGui.QMainWindow()
edit = MyTextEdit()
window.setCentralWidget(edit)
window.setWindowTitle("None [*]")
window.show()
app.exec_()

Never mind, figured it out. The problem was that in the save method I should've been calling self.document().setModified(False) instead of window.setWindowModified(False)

Related

Detect Ctrl+S ion QTextedit?

So, I'm making a QTextEdit that edits a text file. I got the loading and saving working fine with buttons. But I got the habit of pressing Ctrl+S to save every time I paste something into the textedit because I used that in Notepad before. So I've been trying to implement it. But I can't wrap my head around how to detect and execute my save function. Lets call it savetext.
I've been going around trying to get keyPressEvent to work, but I just don't understand how it works. So I've been pretty helpless in trying to learn it.
My heavily simplified code looks like this:
class GUI(QProcess):
def init etc...
"Button creations and connect to save/load function"
self.textedit=QTextEdit()
def savetext(self):
code
def loadtext(self):
code
Now, how do I detect a key combination being detected in the QTextEdit, or anywhere in my program for that matter, and cause it to do savetext? In my case, Ctrl+S, though I'd just love a general explanation so I could apply it to any combo.
Use QShortcut and QKeySequence
from PyQt5.QtWidgets import QApplication, QTextEdit, QShortcut
from PyQt5.QtGui import QKeySequence
import sys
def slot():
print("Ctrl+S")
app = QApplication(sys.argv)
textedit=QTextEdit()
shortcut = QShortcut(QKeySequence("Ctrl+S"), textedit)
shortcut.activated.connect(slot)
textedit.show()
sys.exit(app.exec_())
You can probably use QShortcut, and right now it will activate only when textedit in focus. If you want to change the behavior please take a look here
Here is a example
import sys
from PyQt4 import QtGui, QtCore
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
layout = QtGui.QVBoxLayout(self)
self.edit = QtGui.QTextEdit()
layout.addWidget(self.edit)
self.button = QtGui.QPushButton('Test')
layout.addWidget(self.button)
foo = QtGui.QShortcut(QtGui.QKeySequence("Ctrl+S"), self.edit, self.saveCall, context=QtCore.Qt.WidgetShortcut)
def saveCall(self):
self.edit.append('Please save me')
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())

How can I self hide and show QDialog() in PyQT5?

I have a GUI that was generated using Qt Designer, I used pyuic5 to generate a .py file. In a separate py (program.py) file I import my UI a do all my work there.
program.py
import sys, os, time
from subprocess import call
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyCred_GUI import Ui_Dialog
class MyGUI(Ui_Dialog):
def __init__(self, dialog):
Ui_Dialog.__init__(self)
self.setupUi(dialog)
self.pushButton_2.clicked.connect(self.cancelbutton)
def cancelbutton(self):
exit()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
dialog = QtWidgets.QDialog()
dialog.setWindowFlags(QtCore.Qt.WindowSystemMenuHint)
prog = MyGUI(dialog)
dialog.show()
sys.exit(app.exec_())
I pulled a lot out just to focus on the issue here. When I click my Cancel button, I want the window to hide, set a timer, and then reappear after so many seconds. I have tried every combination of self.close() self.hide() self.destroy() and none of them hide my window. I get an error that says
"AttributeError: 'MyGUI' object has no attribute 'hide'"
Which makes sense because MyGUI doesn't have a hide() function. I am at a complete loss on how to hide this window.
EDIT (Solved)
For future people, as suggested by Hi Im Frogatto dialog.hide() worked.
In your code snippet, dialog is of type QDialog and thereby having hide method. However instances of MyGUI class seem to not have such a method. So, if you write dialog.hide() in that __init__() function, you can hide it.

PyQt: how do I replace a widget using a button?

I'm trying to write a simple code which will change existing text widget to another one when user clicks a button.
So I have
title1=QtGui.QLabel('Hello')
title2=QtGui.QLabel('bye')
abutton=QtGui.QPushButton('Click me')
grid.addWidget(title1,1,5)
grid.addWidget(abutton,3,5)
and I have a function:
def myfunc(self):
self.grid.removeWidget(self.title1)
self.grid.addWidget(title2,1,5)
which I expect to change my "hello" to "bye" after I do this:
abutton.clicked.connect(self.myfunc)
but apparently that doesn't work. And I've checked: removeWidged works perfectly outside the function (my first thought was, maybe, i was doing something wrong in the function), and also the function does work itself(i checked it by making in print stuff and it did once I clicked the button, but the widget was still there)
what might I be doing wrong? thanks.
You had typo and you trying local vars like so many issues in your code. Here is a working example
from PyQt4 import QtGui, QtCore
import sys
class BASEGUICLS(QtGui.QDialog):
def __init__(self,parent=None):
super(BASEGUICLS, self).__init__(parent)
self.gridLayout = QtGui.QGridLayout()
self.title1=QtGui.QLabel('Hello')
self.title2=QtGui.QLabel('bye')
abutton=QtGui.QPushButton('Click me')
self.gridLayout.addWidget(self.title1,1,5)
self.gridLayout.addWidget(abutton,3,5)
self.setLayout(self.gridLayout)
abutton.clicked.connect(self.myfunc)
def myfunc(self):
self.gridLayout.removeWidget(self.title1)
self.title1.deleteLater()
self.gridLayout.addWidget(self.title2,1,5)
def main():
app = QtGui.QApplication(sys.argv)
ex = BASEGUICLS(None)
ex.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

QPlainTextEdit thinks it's modified if it has an empty text

I'm using PyQt to build a simple IDE and getting weird errors if you load an empty file. A small example script is posted below:
#!/usr/bin/env python
import sys
from PyQt4 import QtGui
class TestApp(QtGui.QMainWindow):
def __init__(self, filename=None):
super(TestApp, self).__init__()
self._editor = QtGui.QPlainTextEdit()
self._editor.modificationChanged.connect(self._change_modified)
self.setCentralWidget(self._editor)
self._editor.setPlainText('a')
def _change_modified(self, have_change):
print(have_change)
if __name__ == '__main__':
a = QtGui.QApplication([])
app = TestApp()
app.show()
sys.exit(a.exec_())
As expected, this shows a window with a plain text editor. As soon as the setPlainText method is called, the editor emits two events: One modificationChanged event with changes=True, a second with changes=False.
A bit weird, but fine.
However, if you change setPlainText('a') to setPlainText(''), only a single event is emitted, this time with changes=True. Even worse, after telling the editor it's not modified with setModified(False), it insists it's been changed somehow.
Does anyone know what's causing this and how I can work around this issue?
Update: It seems to be a bug & also affects QPlainTextEdit.clear().
The workaround below places a wrapper around the QPlainTextEdit to fix clear() and setPlainText('').
#!/usr/bin/env python
import sys
from PyQt4 import QtGui
class TestApp(QtGui.QMainWindow):
def __init__(self, filename=None):
super(TestApp, self).__init__()
self._editor = PlainTextEdit()
self._editor.modificationChanged.connect(self._change_modified)
self.setCentralWidget(self._editor)
self._editor.setPlainText('')
def _change_modified(self, have_change):
print(have_change)
class PlainTextEdit(QtGui.QPlainTextEdit):
def clear(self):
self.selectAll()
cursor = self.textCursor()
cursor.removeSelectedText()
doc = self.document()
doc.clearUndoRedoStacks()
doc.setModified(False)
self.modificationChanged.emit(False)
def setPlainText(self, text):
if text:
super(PlainTextEdit, self).setPlainText(text)
else:
self.clear()
if __name__ == '__main__':
a = QtGui.QApplication([])
app = TestApp()
app.show()
sys.exit(a.exec_())
It's a Qt bug, and the straightforward workaround is to check for empty contents if modifications are indicated.

Query regarding Pyside

In the below mentioned example when I click on 'Help' submenu under 'View' menu multiple times its creating multiple windows. Can anyone tell me how to resolve this issue?
import sys
from PySide import Qt Gui
from PySide.QtCore import Qt
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.menu_bar()
def menu_bar(self):
helpAction = QtGui.QAction('&Help', self)
helpAction.setShortcut('Ctrl+H')
helpAction.triggered.connect(self.add_helpWindow)
menu = self.menuBar().addMenu('View')
menu.addAction(helpAction)
def add_helpWindow(self):
window = QtGui.QMainWindow(self)
window.setWindowTitle('New Window')
window.show()
if __name__ == '__main__':
import sys
app=QtGui.QApplication.instance()
if not app:
app = QtGui.QApplication(sys.argv)
window = Window()
window.resize(300, 300)
window.show()
sys.exit(app.exec_())
You help window is just a QMainWindow, which is not modal and there are no restrictions on the number that can exist. Hence why if you select the help option multiple times, you get multiple windows.
You likely want to use a QMessageBox which has its modal property set. While there is nothing forcing only one dialog to exist at a time, being modal means that the use can only interact with that window so long as it is open. Example:
from Pyside.QtGui import QMessageBox
def add_helpWindow(self):
help_dialog = QMessageBox.information(self, 'Help', 'Some Help Text Here')
help_dialog.setModal(True)
return help_dialog.exec_()
You can also get a more generic dialog box using QDialog, which is the parent class of QMessageBox.
If that's not the behavior you want, you'll need to manually track whether the user has opened that window before, and then connect a signal that is emitted when the user closes the help window to a slot that reset the existence tracker. Here is an example using a non-modal QDialog:
from Pyside.QtGui import QDialog
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.menu_bar()
self.help_open = False # Tracks if the help dialog is already open
def help_closed(self):
self.help_open = False
...
def add_helpWindow(self):
if not self.help_open:
self.help_open = True
help_dialog = QDialog(self)
# Any other setup code here
help_dialog.setModal(False)
help_dialog.accepted.connect(self.help_closed)
help_dialog.rejected.connect(self.help_closed)
help_dialog.show()

Categories

Resources