I'm going to draw a QLine in a QGraphicsScene and display it via a QGraphicsView.
The line is drawn during mainwindow construction, but deleted after leaving the MainWindow constructor shortly after been drawn and before reaching any slot. (I noticed this behaviour during debugging.)
The most relevant code lines are:
MainWindow::MainWindow(QWidget* parent):
ui{new Ui::MainWindow},
scene{new QGraphicsScene(this)},
view{new ClickableMap(scene)}, / ... */
{
ui->setupUi(this);
ui->view->setScene(scene);
for (/* ... */ ) {
QGraphicsItem* edgeDrawing= scene->addLine(x1, y1, x2, y2);
edgeDrawing->setZValue(1);
}
ui->view->show();
}
Why exactly is the drawing hidden? It's no problem to draw by using the signal-slot concept (mouse click on the QGraphicsView), but I would like to display the drawing at program start.
Thanks to #hyde , I found the answer: I executed scene->clear() in a slot called after loading some pictures. Apparently, the execution was delayed by Qt. (Some debug output was output before the scene was deleted in reality despite these output instructions were placed after the call of QGraphicsView::clear() in the source code.)
Related
At QT, I made the ui with QWidget. And I want to print the ui with my printer. But, The ui is too long to print on one page.(height is greater than the A4 size) I want continuous prints without newPage() function.
I use QPrinter and QPainter. But, The result I received was a cropped widget image. I don't want to use newPage() function.
QPrinter printer;
QPainter painter;
QPrintDialog printDialog(&printer);
if (printDialog.exec() == QDialog::Accepted) {
painter.begin(&printer);
ui->scrollArea->widget()->render(&painter);
}
I do not like to apply printer.newPage() manually to every ui.
Is there any option or code that prints automatically on the next page like MS Word?
I have to print on plotter world map with a lot of vector data on it from Qt based app. Plotter has 80 GB hard drive, to print high resolution images. All data is loaded in QGraphicsScene.
QGraphicsScene *scene;
.....
QPrinter *printer = new QPrinter(QPrinter::ScreenResolution);
printer->setPaperSize(QPrinter::A4);
QPainter painter(printer);
painter.setRenderHint(QPainter::Antialiasing);
A4 is using for improve speed during tests.
forever
{
if(printedHeight >= sceneHeight)
{
isPrintingRunning = false;
qDebug() << "Printing finished";
break;
}
...loading next tile strip
scene->render(&painter, toPage(sceneRect), tileStripRect);
scene->clearStrip;
printedHeight += stripHeight;
}
After loading vector data, i'm loading tiles(256x256 px) by horizontal strips, render strip to painter, clear tiles from scene, than again. Because i'm rendering by strips scene doesn't exceed available memory, but QPainter doesn't send data to printer until it will be deleted, that's why my app very quickly run out of memory. There is an opportunity to call QPainter::end() each cycle step, but in this way each strip is printed on differrent page on ordinary printer, so I suppose plotter will cut each strip, as soon as i can't control plotter's knife. So this is not a solution. How can I force QPainter to send data and clear its cache, but stay in the same print session?
I have found that QTabWidget exhibits a really strange behaviour when it comes to repainting its children. The following code is the minimal example that I created to observe this problem.
Suppose I have a custom widget (a drawing) that has costly paint event hadler. So I need to minimize the number of times it is repainted. But the problem is that if this drawing widget is inside QTabWidget it receives many unnecesary repaint requests as a response to changes in some other totally unrelated widgets (in the example below - the button and label).
At the end of the example you can change variable withTabWidget. If it is set to True, the drawing is repainted (i.e. the number in the drawing is increased) every time the button is pressed. If it is set to False, the drawing is not repainted (the number in the drawing stays the same) when the button is pressed, which is the correct behaviour I need. But obviously, I also need to use the tab widget in my app layout...
Any idea how to make the tab widget work correctly, without unnecessary repaints?
Note: Qt 4.8.4 (tested on both 32 and 64-bits), PySide 1.2.1., Windows 7
import sys
from PySide import QtGui
class MyDrawing(QtGui.QLabel):
# drawing is a widget which has user defined paint event handler
# the painting can be costly so we must avoid unnecessary repainting
repaintCount = 0
def paintEvent(self, event):
# a very simple example which count ...
# the number of times the paintEvent was executed
self.repaintCount += 1
painter = QtGui.QPainter(self)
painter.drawText(10, 10, str(self.repaintCount))
class MyContainer(QtGui.QWidget):
def __init__(self, parent=None):
super(MyContainer, self).__init__(parent)
layout = QtGui.QHBoxLayout(self)
# there is a button ...
self.button = QtGui.QPushButton("Press me.")
self.button.setCheckable(True)
self.button.pressed.connect(self.onButtonPressed)
layout.addWidget(self.button)
# .. and a label ...
self.label = QtGui.QLabel()
layout.addWidget(self.label)
# ... and a drawing
self.drawing = MyDrawing()
layout.addWidget(self.drawing)
def onButtonPressed(self):
# pressing button only changes the label
# it does not change the drawing
# so drawing should not be repainted
# but when the container is inside QTabWidget it IS repainted
self.label.setText(str(self.button.isChecked()))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
form = QtGui.QMainWindow()
# change this True/False!!!
withTabWidget = True
# True = problem: MyDrawing IS repainted when button1 is pressed
# False = OK: MyDrawing IS NOT repainted when button1 is pressed
if withTabWidget:
tabWidget = QtGui.QTabWidget()
tabWidget.addTab(MyContainer(), "Tab1")
form.setCentralWidget(tabWidget)
else:
form.setCentralWidget(MyContainer())
form.show()
result = app.exec_()
sys.exit(result)
Update: By tracking the events on QTabWidget, I discovered, that when the label is changed, some animation object is inserted (child is added) into the QTabWidget. And the QTabWidget responds by repainting the whole area it covers. This is different with other widget types, when the animation object is inserted, it does not repaint its whole area. Unfortunately I still have no workaround to solve it. Maybe will have make my own TabWidget which I would like to avoid at all costs.
Update2: another interesting aspect is that when I start the example application above with the QTabWidget, the number which is drawn in the drawing is 2. When I start it without QTabWidget, the number is 1. So it is another example of unnecessary repainting.
I've got a really simple application for adding watermarks to pictures. So you can drop your pictures in a QListWidget which shows you a thumbnail and the path, adjust some things like the text, the transparency, the output format and so on.. and after pressing start it saves the copyrighted picture in a destination of your choice. This works with a QPainter which paints the logo and text on the picture.
Everything is able to work fine. But here's the misterious bug:
The application kills random letters. It's really strange, because I can't reproduce it. With every execution and combination of options it's different. For example:
Sometimes I can't write some letters in the QLineEdit of my interface (like E, 4 and 0 doesnt exist, or he changes the letters so some special signs).
The text of the items in the QListWidget aren't completly displayed, sometimes completley missing. But I can extract the text normally and use the path.
While execution I have a QTextBrowser as a log to display some interesting things like the font size. Although, the font is shown normaly on the resulting picture, it says " 4" or "6" instead of much higher and correct sizes. Betimes strange white blocks appear between some letters
When drawing text on the picture with a QPainter, there also letters missing. Sometimes, all the letters are printed over each other. It seems like this bug appears more often when using small pixelsizes (like 12):
//Text//
int fontSize = (watermarkHeight-(4*frame));
int fontX = 2*frame;
int fontY = (result.height()-(watermarkHeight-2*frame));
int fontWidth = watermarkWidth;
QRect place(fontX,fontY,fontWidth,fontSize);
QFont font("Helvetica Neue", QFont::Light);
font.setPixelSize(fontSize);
emit log(QString::number(fontSize));
pixPaint.setFont(font);
pixPaint.setPen(QColor(255,255,255,textOpacity));
pixPaint.drawText(place,text);
Not all of these bugs appear at once! Sometimes I haven't got any bugs...
Perhaps someone had a similar bug before. Unfortunately I didn't found something like this in the internet. I didn't post a lot of code snippets because I think (and hope) that this is a gerneral problem. If you need something specific to help me, please let me know =)
I've added a example picture:
In the lineEdit I simply wrote ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 (look what he has done with the 7 and 9)
This small square in the lower corner of the picture should be the "ABC..." thing
The "62" looks very strange in the textBrowser
I'm using Qt 5.0.1 on a Windows 7 64Bit computer.
EDIT: Everytime after adding the first picture to the list, he's finding these warnings:
QFontEngine::loadEngine: GetTextMetrics failed ()
QWindowsFontEngine: GetTextMetrics failed ()
But when I change the height (and with it the pointSize of the font) its not emitted anymore, even with the start-parameters.
EDIT 2: Thank you for your help! I corrected my code so that he only uses correct fonts and correct sizes, but it still doesn't work. When I remove the QPainter::drawText() function it works fine (without the text). But as soon as I am adding text everything is bugged. I have something like this now:
//Text//
QList<int> smoothSizes = fontDatabase->smoothSizes("Verdana","Standard");
int fontSize = (watermarkHeight-(4*frame))*0.75;
emit log("Requested: "+QString::number(fontSize));
if(!smoothSizes.contains(fontSize)){
for(int i = 0; i<smoothSizes.length(); i++){
if(smoothSizes.at(i) > fontSize && i>0){
fontSize = smoothSizes.at(i-1);
break;
}
}
}
int fontX = 2*frame;
int fontY = (result.height()-(watermarkHeight/2)+frame);
QFont font = fontDatabase->font("Verdana","Standard",fontSize);
QFontInfo info(font);
emit log("Corrected: "+QString::number(fontSize));
emit log("Okay?: "+QString::number(info.exactMatch()));
pixPaint.setFont(font);
const QFontMetrics fontMetrics = pixPaint.fontMetrics();
if(info.exactMatch()){
pixPaint.setPen(QColor(255,255,255,textOpacity));
pixPaint.drawText(fontX,fontY+(fontMetrics.height()-fontMetrics.ascent()),text);
}
It almost sounds like you are corrupting random memory in your process, or you have a badly broken Windows install. Possibly your font request is matched by a very poorly chosen system font.
Whatever is set on a QFont is merely a request. To obtain the parameters of the actual font that was selected, you must create a QFontInfo, and get your information from there.
Imagine that you request a QFont that doesn't exist on a system, or that can't be scaled to a particular size. At some point the font object would need to morph to reflect what really happened - this would be very confusing. Thus, the QFontInfo provides the information about the font that was actually used. Think of QFontInfo as a response, and QFont as a request.
I finally found a solution: I simply updated Qt from 5.0.1 to 5.2.1, now it works. Perhaps someone has a similar bug and this post helps him. Thank you for your help!
I have some code creating a QTabWidget from Python using PyQt4. I want to get a 'throbber' animated gif in the tab. The /only way/ I have found how to do this is the following convoluted method.
tabBar = self.tabReports.tabBar()
lbl = QtGui.QLabel(self.tabReports)
movie = QtGui.QMovie(os.path.join(self.basedir, "images\\throbber.gif"))
lbl.setMovie(movie)
QtCore.QObject.connect(movie, QtCore.SIGNAL("frameChanged(int)"), lambda i: movie.jumpToFrame(i))
movie.start()
log.debug("valid = %s"%(movie.isValid()))
tabBar.setTabButton(idxtab, QtGui.QTabBar.LeftSide, lbl)
The debugging call always returns true, but the throbber sometimes works, sometimes is blank, and sometimes has a large ugly delay between frames. In particular, I can't help but think connecting the frameChanged signal from the movie to a function that simply calls jumpToFrame on the same movie is not correct.
Even more distressing, if I simply drop the lambda (That is, make the line say QtCore.QObject.connect(movie, QtCore.SIGNAL("frameChanged(int)"), movie.jumpToFrame) it never renders even the first frame.
So, what am I doing wrong?
PS: I realize .tabBar() is a protected member, but I assumed (apparently correctly) that PyQt unprotects protected members :). I'm new to Qt, and i'd rather not subclass QTabWidget if I can help it.
I believe the problem with the code I initially posted was that the QMovie didn't have a parent, and thus scoping issues allowed the underlying C++ issue to be destroyed. It is also possible I had had threading issues - threading.thread and QThread do not play nice together. The working code I have now is below - no messing with signals nor slots needed.
def animateTab(self, tab_widget, enable):
tw = tab_widget
tabBar = tw.tabBar()
if enable:
lbl = QtGui.QLabel(tw)
movie = QtGui.QMovie("images\\throbber.gif"), parent=lbl)
movie.setScaledSize(QtCore.QSize(16, 16))
lbl.setMovie(movie)
movie.start()
else:
lbl = QtGui.QLabel(tw)
lbl.setMinimumSize(QtCore.QSize(16, 16))
tabBar.setTabButton(tab_section.index, QtGui.QTabBar.LeftSide, lbl)
I faced the same problem and this posting helped to make it work:
http://www.daniweb.com/forums/printthread.php?t=191210&pp=40
For me this seems to make the difference: QMovie("image.gif", QByteArray(), self)