I'm writing a cross platform app in PyQt4. For a particular feature, I would like to access the QTextHtmlImporter class of Qt4. There is no direct python adapter class available in PyQt4. The class is part of the src/gui/text/qtextdocumentfragment_p.h file. Is there any way I can access that in Python?
I would like to modify QTextDocument.setHtml(), which code is:
void QTextDocument::setHtml(const QString &html) {
Q_D(QTextDocument); setUndoRedoEnabled(false);
d->clear();
QTextHtmlImporter(this, html).import();
setUndoRedoEnabled(true);
}
to
void QTextDocument::setHtml(const QString &html) {
Q_D(QTextDocument);
QTextHtmlImporter(this, html).import();
}
Basically setting the HTML without clearing the history. I planned to do this by using a derived class of PyQt4's QTextDocument overriding the setHtml function. Is there any other way to do this?
QTextHtmlImporter isn't even part of the Qt4 API, so the short answer is: no, there's no way to access it in PyQt4.
You could, of course, attempt to port the code to PyQt4, but I'm guessing that would be a non-trivial task.
The question is: why do you think you need to do this?
Why can't you use QTextCursor.insertHtml or QTextDocumentFragment.fromHtml?
EDIT
Here's an example of how to set the html in a text document without clearing the undo history:
from PyQt4 import QtGui, QtCore
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
layout = QtGui.QVBoxLayout(self)
self.edit = QtGui.QTextEdit(self)
self.undo = QtGui.QPushButton('Undo')
self.redo = QtGui.QPushButton('Redo')
self.insert = QtGui.QPushButton('Set Html')
layout.addWidget(self.edit)
layout.addWidget(self.undo)
layout.addWidget(self.redo)
layout.addWidget(self.insert)
self.undo.clicked.connect(self.edit.undo)
self.redo.clicked.connect(self.edit.redo)
self.insert.clicked.connect(self.handleInsert)
self.edit.append('One')
self.edit.append('Two')
self.edit.append('Three')
def handleInsert(self):
cursor = QtGui.QTextCursor(self.edit.document())
cursor.select(QtGui.QTextCursor.Document)
cursor.insertHtml("""<p>Some <b>HTML</b> text</p>""")
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
Related
I want to design my QWizardPages in Qt Designer and I want to load them into my Python program with PySide2. Previously I have been using PyQt5 without any problems but making the switch to PySide2 seems harder then expected.
The problem I am facing is that when I am adding a QWizardPage to my QWizard , the page is indeed added to the Wizard, but also an other (empty) page is added. I'm not able to find what I'm doing wrong so I was wondering if someone can have a look.
I have tried to add the pages with both the functions addPage() and setPage(), but they give the same results. What I also noticed is that when I explicitely set the Title of the page with setTitle(), the empty (unwanted) page gets this title, but not the page I designed in Qt Designer.
import os
import sys
from PySide2.QtWidgets import QWizard, QWizardPage, QApplication
from PySide2.QtCore import QFile
from PySide2.QtUiTools import QUiLoader
from enum import Enum
class MyWizard(QWizard):
def __init__(self):
super().__init__()
self.setPage(PageNumbers.page_one.value, PageOne(self))
class PageOne(QWizardPage):
def __init__(self, parent):
super().__init__(parent)
ui_file = os.path.join(__file__, '..', 'pageOne.ui')
file = QFile(ui_file)
file.open(QFile.ReadOnly)
loader = QUiLoader()
loader.load(file, parent)
file.close()
self.setTitle("This is another test Title")
class PageNumbers(Enum):
page_one = 1
if __name__ == '__main__':
app = QApplication(sys.argv)
wizard = MyWizard()
wizard.show()
app.exec_()
What I would expect is to have just one QWizardPage showing up with directly the Finish button. Instead I get two QWizardPages as shown in this image:
Can someone tell me what's going on?
(I get the expected result using PyQt5 with the following code: https://pastebin.com/6W2sx9M1)
The developers of PyQt implement functions to be able to create classes based on the .ui that is not implemented in Qt by default (Qt/C++ uses the MOC to do this work), but in the case of PySide2-Qt for python it does not implement it, only has the QUiLoader class that allows to create a widget based on the .ui unlike PyQt that allows filling a class.
In conclusion there is no equivalent in PySide2 of the loadUi function so you can not implement the same logic. PySide2 is not PyQt5, there are own equivalences since they use the same base but they have implementations, limitations and advantages.
Going to the practical problem, considering that the .ui is next to the .py the solution is the following:
import os
import sys
from PySide2 import QtCore, QtWidgets, QtUiTools
from enum import Enum
class PageNumbers(Enum):
page_one = 0
class MyWizard(QtWidgets.QWizard):
def __init__(self):
super().__init__()
ui_file = os.path.join(os.path.dirname(os.path.abspath(__file__)) ,'PageOne.ui')
page_one = create_widget(ui_file, self)
self.setPage(PageNumbers.page_one.value, page_one)
def create_widget(filename, parent=None):
file = QtCore.QFile(filename)
if not file.open(QtCore.QFile.ReadOnly):
return
loader = QtUiTools.QUiLoader()
widget = loader.load(file, parent)
return widget
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
wizard = MyWizard()
wizard.show()
sys.exit(app.exec_())
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_())
I have a problem. I am writing a simple app in Pyqt5. I am trying to do this block of code in PyQt:
QNetworkAccessManager manager;
QNetworkReply *response = manager.get(QNetworkRequest(QUrl(url)));
QEventLoop event;
connect(response,SIGNAL(finished()),&event,SLOT(quit()));
event.exec();
QString html = response->readAll();
But when I am trying to use "connect" IDE tells me that "MainWindow" don't have method. How can I do it ?? Please help
This is my code:
class MainWindow(QtWidgets.QWidget):
def __init__(self, parent = None):
super(MainWindow, self).__init__()
# window settings
self.setWindowTitle("Hello world app")
# main layout
self.lay = QtWidgets.QVBoxLayout()
# main widgets
self.label = QtWidgets.QLabel("Enter URL:")
self.line = QtWidgets.QLineEdit()
self.label_conn = QtWidgets.QLabel("")
self.btn = QtWidgets.QPushButton("Connect")
self.btn.clicked.connect(self.btn_click)
# adding widgets to layout
self.lay.addWidget(self.label, alignment=QtCore.Qt.AlignBottom)
self.lay.addWidget(self.line)
self.lay.addWidget(self.btn)
self.lay.addWidget(self.label_conn, alignment=QtCore.Qt.AlignTop | QtCore.Qt.AlignCenter)
self.setLayout(self.lay)
self.connect()
The connect method belongs to the signal that you wish to connect to a specific slot, not to the MainWindow widget itself. (BTW, you should consider inheriting from QMainWindow instead.)
In your code, the MainWindow widget is not a signal, so does not have a connect method. Also, even if it did, you need to specify the slot to which you're trying to connect the signal, which is also missing.
In other words, you must declare a pyqtSignal, if you're not using a pre-existing one, and then connect it to the pyqtSlot of your choice. Whether this slot is pre-defined or a custom one is up to you.
Consider the following code snippet, which I tested in Python3:
#!/usr/bin/python3 -B
import sys
from PyQt5.QtWidgets import QApplication, QDialog, QPushButton
if __name__ == '__main__':
app = QApplication(sys.argv)
diag = QDialog()
diag.setWindowTitle('Signal Demo')
diag.resize(200,50)
btn = QPushButton(diag)
btn.setText('Close Dialog')
# connect button's clicked signal to dialog's close slot
btn.clicked.connect(diag.close)
diag.show()
diag.exec_()
Notice that the button's clicked signal, not the button, is what gets connected to the dialog's close slot, not the dialog itself.
EDIT 1:
Just noticed that the very code you've posted has an example of how to properly perform a connection.
If your code has not simply been copy-pasted from some other place, you should've noticed that you seem to know how to properly connect signals and slots already. This line plainly gives it away:
self.btn.clicked.connect(self.btn_click)
If your MainWindow does have a btn_click method, then it should get invoked after the QPushButton named btn gets clicked.
EDIT 2:
Based on your recent comment, you seem to simply be trying to translate a snippet for a larger application, so consider the following code:
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
from PyQt5.QtCore import QEventLoop, QUrl
app = QApplication(sys.argv)
url = 'https://stackoverflow.com'
manager = QNetworkAccessManager()
response = manager.get(QNetworkRequest(QUrl(url)))
event = QEventLoop()
response.finished.connect(event.quit)
event.exec()
html = str(response.readAll()) # in Python3 all strings are unicode, so QString is not defined
print(html)
The code above was tested to work as expected.
PS: I did notice that some seemingly valid URLs were returning an empty response (e.g. http://sourceforge.net/), but others, such as the one above, worked fine. It seems to be unrelated to the code snippet itself.
I want to use a QTranslator to be able to use English text labels and still have the software showing German labels.
Unfortunately my app does not translate, except when I specify the context.
The following static function instanciates a QApplication and adds the desired translators.
The first print translates 'Apple2' correctly to 'Apfel2'. The context in Qt Linguist also has the context 'app'.
The second print does not translate though. tr() calls in classes (defined in the same python file) don't translate either.
def load_application():
app = QApplication()
qt_translator = QTranslator()
qt_translator.load('qt_' + QLocale.system().name(), QLibraryInfo.location(QLibraryInfo.TranslationsPath))
app.installTranslator(qt_translator)
app_translator = QTranslator()
r = app_translator.load('i18n/' + QLocale.system().name())
app.installTranslator(app_translator)
print(app.translate('app', 'Apple2'))
print(app.tr('Apple'))
return app
EDIT:
The part for the static function was correct. The context for the application was QApplication. This did not help with the QMainWindow subclass though. I updated the code accordingly. The context generated by pyside-lupdate for the class is MainWindow:
view
class MainWindow(QMainWindow):
add_model_widget = None
def __init__(self):
QMainWindow.__init__(self)
# Create menu bar
menu_bar = QMenuBar(self)
m_file = QMenu(self.tr('File'), menu_bar)
a_add_model = QAction(QIcon('add.png'), self.tr('Add Jewel'), self)
m_file.addAction(a_add_model)
menu_bar.addMenu(m_file)
self.setMenuBar(menu_bar)
def load_application():
app = QApplication()
app_translator = QTranslator()
app_translator.load(QLocale.system().name(), 'i18n')
app.installTranslator(app_translator)
return app
controller
def initiate():
model.initiate_mongodb()
app = view.load_application()
main_window = view.MainWindow()
main_window.show()
sys.exit(app.exec_())
Solution:
The solution to my problem was that the QTranslator didn't have any parent. QTranslator(app) solved my problem.
This seems to happen because, unlike Qt, PySide/PyQt determines the context at runtime.
In your example, the context will (I think) resolve to QApplication at runtime, whereas the pyside/pyqt lupdate tools will hardcode it as app. The tools only do static analysis of the source code, and so I suppose they are not smart enough to figure out what the correct class should be.
The example code should work if you do something like this, though:
class App(QtGui.QApplication):
def __init__(self):
super(App, self).__init__()
message = self.tr('Apple')
...
app = App()
...
print(app.tr('Apple'))
(Obviously you will need to update the translation files first).
EDIT:
Here's a simplified demo that works for me:
test.py:
import sys, os
from PySide import QtCore, QtGui
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
menu = self.menuBar().addMenu(self.tr('File'))
menu.addAction(self.tr('Hello World'))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
translator = QtCore.QTranslator(app)
translator.load('i18n/tr_de', os.path.dirname(__file__))
app.installTranslator(translator)
window = MainWindow()
window.show()
sys.exit(app.exec_())
i18n/tr.pro:
SOURCES = ../test.py
TRANSLATIONS = tr_de.ts
i18n/tr_de.ts:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS><TS version="1.1" language="de_DE">
<context>
<name>MainWindow</name>
<message>
<location filename="../test.py" line="7"/>
<source>File</source>
<translation>Datei</translation>
</message>
<message>
<location filename="../test.py" line="8"/>
<source>Hello World</source>
<translation>Hallo Welt</translation>
</message>
</context>
</TS>
command output:
$ pyside-lupdate -verbose -noobsolete i18n/tr.pro
Updating 'tr_de.ts'...
Found 2 source texts (2 new and 0 already existing)
$ lrelease-qt4 i18n/tr.pro
Updating './i18n/tr_de.qm'...
Generated 2 translation(s) (2 finished and 0 unfinished)
If you got here like me, wanting to understand how you should do translation using PySide.
Use translate(), not tr()
The annoying part about using tr() is that you have to manually edit the generated .ts files every time you edit your source with the method he proposes, this editing takes a lot of time especially when you update your .ts file, because it puts all .tr() calls in the "Unknown context" context, you have to manually move all the translations back to the correct contexts again, this gets out of hand really quickly.
Instead, there's a very simple solution.
I read the official pyqt4-lupdate documentation, and it says
The PyQt4 behaviour is unsatisfactory and may be changed in the
future. It is recommended that QCoreApplication.translate() be used in
preference to tr() (and trUtf8()). This is guaranteed to work with
current and future versions of PyQt4 and makes it much easier to share
message files between Python and C++ code. Below is the alternative
implementation of A that uses QCoreApplication.translate():
from QtCore import QCoreApplication
translate = QCoreApplication.translate
class A(QtCore.QObject):
def hello(self):
return translate("A", "Hello")
Where "A" is the hard-coded context, which pyside-lupdate will find. A lot better!
I want to do some things with the PyQt4 framework. So I decided to do some browser like thing. Here is the code. Its just simple:
import sys
from PyQt4 import QtGui, QtCore, QtWebKit
class MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.resize(250, 150)
self.setWindowTitle('Testbrowser')
exit = QtGui.QAction(QtGui.QIcon('icons/exit.png'), 'Exit', self)
exit.setShortcut('Ctrl+Q')
exit.setStatusTip('Exit application')
self.connect(exit, QtCore.SIGNAL('triggered()'), QtCore.SLOT('close()'))
self.statusBar()
menubar = self.menuBar()
datei = menubar.addMenu('&Datei')
datei.addAction(exit)
tools = menubar.addMenu('&Tools')
app = QtGui.QApplication(sys.argv)
main = MainWindow()
web = QWebView()
web.load(QUrl("http://google.de"))
main.show()
sys.exit(app.exec_())
I think I understood some of the things here. But what I do not understand is, how can I work with new modules here? The MainWindow class inherits from the QtGui.QMainWindow class, thats ok. But what now? Should I create a whole new class which inherits from QWebView?? Kind of like :
class newclass(QWebView.QtWebKit):
def __init__(self):
QWebView.QtWebkit.__init__(self)
ect
Or how can I do this without a new class? Or how do I do this in general? I saw a webpage on which the author made a simple browser too. But he imported it in a new programm and made an object out of it and then did some stuff. Do I have to do this, or is there a simpler way? How is this all done in PyQt4?
Greets
some reference
http://pyqt.sourceforge.net/Docs/PyQt4/qwebview.html
Accessing elements of a module follows a dot notation convention in Python -- e.g. to access QWebView, you should use
web = QtWebkit.QWebView()
the URL would be
QtCore.QUrl("http://google.de")
If you want to have all of the names available to you without dot notation, you have to import everything:
from PyQt4.QtWebkit import *