Using PyQt and Qt4, is this the proper way to get a throbber in a QTabWidget tab? - qt

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)

Related

Qt-Application is killing characters accidentally (drawText produces bug)

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!

Work with widgets inside Qtabwidget

I see variant how program can work with QWidget,for example QLabel,QEdit,etc on C++. But i don't understand how can i work with other widgets on tab of tabwidget if i write programs with python & pyside?
I see variant with metaobject,read about variant with QObject::child,but i don't know right way for solving problem on python. I see variant with qobject_cast,but this function specific for C++ as i think. Sample code on C++:
try:
QTextEdit* edit = qobject_cast<QTextEdit*>(tabWidget->widget(index));
Well, I am not an expert of python, but did you try to use this:
yourObject = tabWidget.widget(index)
I guess it works with any type of object.

In Tkinter how do def work?

I made a Tkinter book on a grid, the navergation buttons scroll down the left row and the photos are displayed on the right column spaning many rows so buttons display as they should. When making this Tkinter book.
I made a button on a grid
left1 = Button(win, text=" Captian Scarlet ")# win, is root master
left1.configure(command=but1)# but1 is my first def
left1.grid(row=1, column=0)# all the buttons are on the left list
This displays and works like a button without a def
Then I made a def
def but1():
img = Image.open("captain_scarlett.gif")# loads the gif file
intro = ImageTk.PhotoImage(img)# loads image drivers I belevie
right1 = Label(win, image=intro)# I think Lable is used the same as html <span>
right1.grid(row=0, column=1, rowspan=13)# image formatting to display correctly with buttons
Because I had a lack of education at the time, I could only get the image to displaplay outside a def. So in frustration I posted
"this code works purfect when not put into a def".
When I settled down I needed knowledge that I couldn't find online so I asked the question:
So How do I get this code to work inside a def ?
What makes you think it doesn't work? There's nothing special about Tkinter in this regard; anything that works outside a def definitely works inside a def. The only caveats are the same for all python code. For example, any variables you create inside the def are local unless declared otherwise, and objects (but not widgets) may get garbage collected after the def executes.
Probably what is happening is that you're creating the image and storing a reference to it in a local variable. When the def stops executing the image object is garbage collected. You'll need to keep a reference to the image that can persist. One simple solution might be to do right1.image=intro.
This code works purfecty. Now my production can go full steam ahead
Here is the finished code
def but1():
img = Image.open("captain_scarlett.gif")
intro = ImageTk.PhotoImage(img)
right = Label(win, image=intro)
right.grid(row=0, column=1, rowspan=14)
right.image=intro

Phonon's VolumeSlider doesn't change the volume

I'm trying to create a VolumeSlider widget that changes the volume of my audio output.
log.debug("Starting audio player (%s)..." % url)
mediaSource = Phonon.MediaSource(url)
mediaSource.setAutoDelete(True)
self.player = Phonon.createPlayer(Phonon.VideoCategory, mediaSource)
self.player.setTickInterval(100)
self.player.tick.connect(self.updatePlayerLength)
self.mediaSlider.setMediaObject(self.player)
audioOutput = Phonon.AudioOutput(Phonon.MusicCategory)
Phonon.createPath(self.player, audioOutput)
self.mediaVolumeSlider.setAudioOutput(audioOutput)
self.player.play()
However even though I can move the volume slider, the actual volume doesn't change. What did I miss?
I have never used Phonon.createPlayer, because the API seems totally baffling. Apparently, it is supposed to be a "convenience" function that creates a path between an media object and an audio output. But it only gives a reference to the media object. There appears to be no access to the audio output object, which would seem to make it completely useless (but I may well be missing something).
Anyway, I think it is much more convenient to create the paths explicitly, so that it is clear how all the parts are connected together.
The following code works for me on Linux and WinXP:
self.media = Phonon.MediaObject(self)
self.video = Phonon.VideoWidget(self)
self.audio = Phonon.AudioOutput(Phonon.VideoCategory, self)
Phonon.createPath(self.media, self.audio)
Phonon.createPath(self.media, self.video)
self.slider = Phonon.VolumeSlider(self)
self.slider.setAudioOutput(self.audio)

QTest: It's possible to test drag&drop?

I try to test drag&drop with simple sequince: mousePress + mouseMove + mouseRelease. But it's not work.
I investigate qtest source and found, that move event tested through main dispatcher processEvent(). Also I found some bugs in qt bug-tracker: 1, 2
So, I think, that it's not possible to test drag&drop under latest stable Qt4. Have anybody success story with this?
I have had no luck simulating drag and drop via the QTest mouse functions, and digia says
they're not adding that functionality to QT4. I implemented drag/drop testing via a method similar to the one suggested in the above link:
create your mime_data, something like:
mime_data = widget_model.mimeData(indexes)
or
mime_data = QMimeData()
mime_data.setText(widget.text())
then use a function like this to drop the data:
def dropOnto(self, widget, mime_data):
action = Qt.CopyAction|Qt.MoveAction
pt = widget.rect().center()
drag_drop = QDropEvent(pt, action, mime_data, Qt.LeftButton, Qt.NoModifier)
drag_drop.acceptProposedAction()
widget.dropEvent(drag_drop)
mouseMoveEvent() handler starts and blocks at QDrag.exec(). In order to simulate the drop, you have to schedule it e.g. with QTimer:
def complete_qdrag_exec():
QTest.mouseMove(drop_target)
QTest.qWait(50)
QTest.mouseClick(drop_target, Qt.MouseButton.LeftButton)
QTimer.singleShot(1000, complete_qdrag_exec)
QTest.mousePress(drag_source, Qt.MouseButton.LeftButton, Qt.KeyboardModifier.NoModifier, QPoint(10, 10))
QTest.mouseMove(drag_source, QPoint(50, 50)) # Ensure distance sufficient for DND start threshol

Resources