Panda in PyQt

Return to Code Snippets

Panda in PyQt

Postby ZeroByte » Sat May 24, 2008 11:58 am

Here's early code on getting Panda integrated with PyQt as a widget. Its really slow since converting the frame buffer into images PyQt can use is really slow, theres probably a better way though.

To run the demo you'll need PyQt4

Code: Select all
from pandac.PandaModules import loadPrcFileData
#loadPrcFileData("", "window-type offscreen") # Set Panda to draw its main window in an offscreen buffer
# PyQt imports
from PyQt4 import QtGui,  QtCore
import sys
# Panda imports
from direct.showbase.DirectObject import DirectObject
from pandac.PandaModules import GraphicsOutput,  Texture,  StringStream,  PNMImage
from direct.interval.LerpInterval import LerpHprInterval
from pandac.PandaModules import Point3
# Set up Panda environment
import direct.directbase.DirectStart

class QtPandaWidget(QtGui.QWidget):
    """
    This takes a texture from Panda and draws it as a QWidget
    """
   
    def __init__(self, texture,  parent=None):
        QtGui.QWidget.__init__(self,  parent)
       
        self.setGeometry(50, 50, 800, 600)
        self.setWindowTitle("PandaQt")
        self.pandaTexture = texture
       
        # Setup a timer in Qt that runs taskMgr.step() to simulate Panda's own main loop
        pandaTimer = QtCore.QTimer(self)
        self.connect(pandaTimer,  QtCore.SIGNAL("timeout()"), taskMgr.step)
        pandaTimer.start(0)
       
        # Setup another timer that redraws this widget in a specific FPS
        redrawTimer = QtCore.QTimer(self)
        self.connect(redrawTimer,  QtCore.SIGNAL("timeout()"), self, QtCore.SLOT("update()"))
        redrawTimer.start(1000/60)
       
        # Setup a QLabel to paint the pixmap on
        self.paintSurface = QtGui.QLabel(self)
        self.paintSurface.setGeometry(0, 0, 800, 600)
        self.paintSurface.show()
       
        self.paintPixmap = QtGui.QPixmap(800, 600)
   
    # Use the paint event to pull the contents of the panda texture to the widget
    def paintEvent(self,  event):
       
        screenData = StringStream() # Used to pass the data as a string
        screenImage = PNMImage() # Converts the texture data into a format usable with Qt
       
        if self.pandaTexture.hasRamImage():
            print "Should draw yes?"
            self.pandaTexture.store(screenImage)
            screenImage.write(screenData, "test.ppm")
            self.paintPixmap.loadFromData(screenData.getData())
            self.paintSurface.setPixmap(self.paintPixmap)

class PandaHandler(DirectObject):
   
    def __init__(self):
        base.disableMouse()
        base.cam.setPos(0, -28, 6)
        self.testModel = loader.loadModel('panda')
        self.testModel.reparentTo(render)
        print self.testModel.getPos()
        self.rotateInterval = LerpHprInterval(self.testModel, 3, Point3(360, 0, 0))
        self.rotateInterval.loop()
       
        self.screenTexture = Texture()
        self.screenTexture.setMinfilter(Texture.FTLinear)
        base.win.addRenderTexture(self.screenTexture, GraphicsOutput.RTMCopyRam)

if __name__ == "__main__":   
    #lol teh hobo
    panHandler = PandaHandler()
   
    app = QtGui.QApplication(sys.argv)
    pandaWidget = QtPandaWidget(panHandler.screenTexture)
    pandaWidget.show()
   
    sys.exit(app.exec_())
User avatar
ZeroByte
 
Posts: 308
Joined: Wed Feb 28, 2007 7:10 am

Postby ZeroByte » Sat May 24, 2008 6:15 pm

Ooh, made some minor changes to the code and got masked windows in. Probably can't go by this route to get decent speed though. You can see the redraws on it, nasty.

Code changes, copy/paste friendly
Code: Select all
...

class QtPandaWidget(QtGui.QWidget):
    """
    This takes a texture from Panda and draws it as a QWidget
    """
   
    def __init__(self, texture,  parent=None):
        QtGui.QWidget.__init__(self,  parent)
       
        self.setGeometry(50, 50, 800, 600)
        self.setWindowTitle("PandaQt")
        self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint) # Setup the window so its frameless
        self.pandaTexture = texture

...

    # Use the paint event to pull the contents of the panda texture to the widget
    def paintEvent(self,  event):
       
        screenData = StringStream() # Used to pass the data as a string
        screenImage = PNMImage() # Converts the texture data into a format usable with Qt
       
        if self.pandaTexture.mightHaveRamImage(): #ambigious!
            print "Should draw yes?"
            self.pandaTexture.store(screenImage)
            print "Has alpha?",screenImage.hasAlpha()
            screenImage.write(screenData, "test.png")
            self.paintPixmap.loadFromData(screenData.getData())
            self.paintSurface.setPixmap(self.paintPixmap)
            self.setMask(self.paintPixmap.mask())
...


Picture!
Image
User avatar
ZeroByte
 
Posts: 308
Joined: Wed Feb 28, 2007 7:10 am

Postby ZeroByte » Tue Jun 17, 2008 4:09 pm

Current state:

Drawing into PyQt windows is faster now without image conversion getting in the way. If you wished to make a PyQt app with a native panda component (for GUI's maybe?) this is a pretty good way to do it. Still not satisfied though as the alpha masking for the window still has to go through conversion and is therefore slow.

Still not satisfied though

Code: Select all
from pandac.PandaModules import loadPrcFileData
#loadPrcFileData("", "window-type offscreen") # Set Panda to draw its main window in an offscreen buffer
# PyQt imports
from PyQt4 import QtGui,  QtCore
import sys
# Panda imports
from direct.showbase.DirectObject import DirectObject
from pandac.PandaModules import GraphicsOutput,  Texture,  StringStream,  PNMImage
from direct.interval.LerpInterval import LerpHprInterval
from pandac.PandaModules import Point3
# Set up Panda environment
import direct.directbase.DirectStart
from struct import *

class QtPandaWidget(QtGui.QWidget):
    """
    This takes a texture from Panda and draws it as a QWidget
    """
   
    def __init__(self, texture,  parent=None):
        QtGui.QWidget.__init__(self,  parent)
       
        self.setGeometry(50, 50, 800, 600)
        self.setWindowTitle("PandaQt")
        self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint) # Setup the window so its frameless
        self.pandaTexture = texture
       
        # Setup a timer in Qt that runs taskMgr.step() to simulate Panda's own main loop
        pandaTimer = QtCore.QTimer(self)
        self.connect(pandaTimer,  QtCore.SIGNAL("timeout()"), taskMgr.step)
        pandaTimer.start(0)
       
        # Setup another timer that redraws this widget in a specific FPS
        redrawTimer = QtCore.QTimer(self)
        self.connect(redrawTimer,  QtCore.SIGNAL("timeout()"), self, QtCore.SLOT("update()"))
        redrawTimer.start(1000/60)
       
        self.paintSurface = QtGui.QPainter()
        self.rotate = QtGui.QTransform()
        self.rotate.rotate(180)
       
        self.out_image = QtGui.QImage()
   
    def showEvent(self, event):
        self.desktopBg = QtGui.QPixmap.grabWindow(QtGui.QApplication.desktop ().winId(), \
                self.geometry().x(),self.geometry().y(), self.rect().width(), self.rect().height())
   
    # Use the paint event to pull the contents of the panda texture to the widget
    def paintEvent(self,  event):
        if self.pandaTexture.mightHaveRamImage():
            self.pandaTexture.setFormat(Texture.FRgba32)
            #print "Should draw yes?"
            data = self.pandaTexture.getRamImage().getData()
            img = QtGui.QImage(data, self.pandaTexture.getXSize(), self.pandaTexture.getYSize(), QtGui.QImage.Format_ARGB32).mirrored()
            self.paintSurface.begin(self)
            self.paintSurface.drawPixmap(0, 0, self.desktopBg)
            self.paintSurface.drawImage(0, 0, img)
            self.paintSurface.end()
            pixmap = QtGui.QPixmap.fromImage(img)
            self.setMask(pixmap.mask())

class PandaHandler(DirectObject):
   
    def __init__(self):
        base.disableMouse()
        base.cam.setPos(0, -28, 6)
        self.testModel = loader.loadModel('panda')
        self.testModel.reparentTo(render)
        self.rotateInterval = LerpHprInterval(self.testModel, 3, Point3(360, 0, 0))
        self.rotateInterval.loop()
       
        self.screenTexture = Texture()
        self.screenTexture.setMinfilter(Texture.FTLinear)
        self.screenTexture.setFormat(Texture.FRgba32)
        print "Format is", self.screenTexture.getFormat()
        base.win.addRenderTexture(self.screenTexture, GraphicsOutput.RTMCopyRam)

if __name__ == "__main__":   
    #lol teh hobo
    panHandler = PandaHandler()
   
    app = QtGui.QApplication(sys.argv)
    pandaWidget = QtPandaWidget(panHandler.screenTexture)
    pandaWidget.show()
   
    sys.exit(app.exec_())
User avatar
ZeroByte
 
Posts: 308
Joined: Wed Feb 28, 2007 7:10 am

Postby rdb » Wed Jun 18, 2008 2:29 am

Wow. This is so interesting! Good job! :)
rdb
 
Posts: 10808
Joined: Mon Dec 04, 2006 5:58 am
Location: Netherlands

Postby ThomasEgi » Tue Aug 19, 2008 2:00 pm

digging out this thread. i just wondered if it would be possible to let a shader do the masking somehow , let it be a buffer or something. i dunno a thing about qt but it seems like a stupid conversion shaders would be fast enough for. just in case it's actually possible.
User avatar
ThomasEgi
 
Posts: 2160
Joined: Fri Jul 28, 2006 10:43 am
Location: Germany,Koblenz

Postby ZeroByte » Tue Aug 19, 2008 3:24 pm

I don't know, never really tried shaders before. It would also seem to me like a conversion shader would be much more cross compatible. One of the things that I've found about the code below is the differences between how the frames are stored in the graphics card makes the code very specific to the kind of card you're running.
User avatar
ZeroByte
 
Posts: 308
Joined: Wed Feb 28, 2007 7:10 am

Postby ThomasEgi » Wed Oct 29, 2008 6:19 pm

just thought about using panda for a window-sitter.
given qt4 there might be a nice way to speed alpha-masking up. thought i dunno if its possible but it looks promising to somone who has no idea about qt (->me)
http://doc.trolltech.com/4.3/qtopengl.html
anyone experienced enough with the topic can tell me if its possible to render a transparent window on top of the screen? (without going through all-to-slow-software-conversions , and assuming a linux system , if you like with working hw-acc desktop)
thx for the brain-capacity you take to anwer the question :)
User avatar
ThomasEgi
 
Posts: 2160
Joined: Fri Jul 28, 2006 10:43 am
Location: Germany,Koblenz

Postby treeform » Wed Oct 29, 2008 6:59 pm

no, i dont think so.
User avatar
treeform
 
Posts: 2106
Joined: Sat May 05, 2007 5:15 pm
Location: SF, CA

Postby ZeroByte » Thu Oct 30, 2008 3:11 am

Qt has an OpenGL component that is free to use for open source projects I think. Doing a partially transparent window might be much more faster there and less hardware/OS specific. I haven't really looked into the OpenGL part of Qt though and I'm not even sure if PyQT has access to that.
User avatar
ZeroByte
 
Posts: 308
Joined: Wed Feb 28, 2007 7:10 am

Postby ThomasEgi » Thu Oct 30, 2008 9:38 am

there is a PyQt-GL binding.thought i havent found any usefull documentation on that one.
any other techniques to draw a partly transparent window ontop of the screen? video-overlays or something like that?
User avatar
ThomasEgi
 
Posts: 2160
Joined: Fri Jul 28, 2006 10:43 am
Location: Germany,Koblenz

Postby ZeroByte » Thu Oct 30, 2008 10:31 am

I don't really know, I've asked before on the PyQT mailing list if there's a faster way to do semi-transparent windows and didn't really get a reply. I didn't ask about PyQT-GL though.
User avatar
ZeroByte
 
Posts: 308
Joined: Wed Feb 28, 2007 7:10 am

Postby benchang » Wed Nov 19, 2008 12:02 am

Hey, I'm really interested in this - Qt is my other recent addiction, along with Panda, so it's my two current favorite things :)

When panda creates its main window, is there a way to parent it to another window? I don't even know what I'm talking about completely, I just gathered this from reading other peoples' Ogre+QT code. that might be more efficient than rendering to a texture and then converting it to a qimage.

Wonder if you could also use the QGLPixelBuffer for this?
User avatar
benchang
 
Posts: 202
Joined: Wed May 30, 2007 3:16 pm
Location: Troy, NY

Postby dinoint » Mon Jan 19, 2009 8:48 am

I don't know if this wasn't available before, but I managed to embed a Panda window in QT simply by passing it the window handle of the container.

I wrote this small script with PyQT to test if the keyboard focus was working correctly as with wxPython on Ubuntu there are problems:

Code: Select all
# -*- coding: utf-8-*-

from pandac.PandaModules import loadPrcFileData
loadPrcFileData("", "window-type none")

import direct.directbase.DirectStart
from direct.showbase.DirectObject import DirectObject
from pandac.PandaModules import WindowProperties

from PyQt4.QtCore import *
from PyQt4.QtGui import *

import sys

P3D_WIN_WIDTH = 400
P3D_WIN_HEIGHT = 240

class QTTest(QDialog):
    def __init__(self, pandaCallback, parent=None):
        super(QDialog, self).__init__(parent)
        self.setWindowTitle("Test")
        self.setGeometry(0,0,400,300)
       
        self.pandaContainer = QWidget(self)
        self.pandaContainer.setGeometry(0,0,P3D_WIN_WIDTH,P3D_WIN_HEIGHT)

        self.lineedit = QLineEdit("Write something...does it work?")
       
        layout = QVBoxLayout()
        layout.addWidget(self.pandaContainer)
        layout.addWidget(self.lineedit)
       
        self.setLayout(layout)
       
        # this basically creates an idle task
        timer =  QTimer(self)
        self.connect( timer, SIGNAL("timeout()"), pandaCallback )
        timer.start(0)

   
class World(DirectObject):   
    def __init__(self):
        self.accept("a", self.pressedA)
        self.accept("escape", sys.exit)
   
    def pressedA(self):
        print "a pressed, keyboard focus ok"
       
    def step(self):
        taskMgr.step()
   
    def bindToWindow(self, windowHandle):
        wp = WindowProperties().getDefault()
        wp.setOrigin(0,0)
        wp.setSize(P3D_WIN_WIDTH, P3D_WIN_HEIGHT)
        wp.setParentWindow(windowHandle)
        base.openDefaultWindow(props=wp)
        self.wp = wp
       
   
if __name__ == '__main__':
    world = World()

    app = QApplication(sys.argv)
    form = QTTest(world.step)
    world.bindToWindow(int(form.winId()))
   
    form.show()
    app.exec_()

Last edited by dinoint on Thu Jan 22, 2009 12:03 pm, edited 1 time in total.
dinoint
 
Posts: 18
Joined: Mon Jan 12, 2009 9:30 am
Location: France

Postby zzarko » Thu Jan 22, 2009 11:25 am

dinoint wrote:I don't know if this wasn't available before, but I managed to embed a Panda window in QT simply by passing it the window handle of the container.

I wrote this small script with PyQT to test if the keyboard focus was working correctly as with wxPython on Ubuntu there are problems:
...


I tried it in Ubuntu and it simply works! Thanks! I also tried it in Windows, but without much success. The controls in Panda3D scene are working, but as soon as you move focus to edit control, it cannot be changed back to Panda3D panel (or I didn't found a way). Any solutions for this?
zzarko
 
Posts: 14
Joined: Mon Jun 30, 2008 11:25 am

Postby dinoint » Thu Jan 22, 2009 11:38 am

zzarko wrote:I tried it in Ubuntu and it simply works! Thanks! I also tried it in Windows, but without much success. The controls in Panda3D scene are working, but as soon as you move focus to edit control, it cannot be changed back to Panda3D panel (or I didn't found a way). Any solutions for this?


On Ubuntu I just click inside the panda window in order to regain focus, on Windows I can't test, maybe try to set the focus policy on the pandaContainer widget explicitely:

Code: Select all
self.pandaContainer.setFocusPolicy(Qt.StrongFocus)


It's just a wild guess I don't know if it'll works.
dinoint
 
Posts: 18
Joined: Mon Jan 12, 2009 9:30 am
Location: France

Postby zzarko » Thu Jan 22, 2009 11:54 am

I'll try that tonight (my machine at work is Ubuntu :) ), thanks. BTW, for this embedding to work in Windows, bindToWindow must be called with casting:
Code: Select all
world.bindToWindow(int(form.winId()))
zzarko
 
Posts: 14
Joined: Mon Jun 30, 2008 11:25 am

Postby dinoint » Thu Jan 22, 2009 12:03 pm

zzarko wrote:I'll try that tonight (my machine at work is Ubuntu :) ), thanks. BTW, for this embedding to work in Windows, bindToWindow must be called with casting:
Code: Select all
world.bindToWindow(int(form.winId()))


I added your fix to the code above, thanks.
dinoint
 
Posts: 18
Joined: Mon Jan 12, 2009 9:30 am
Location: France

Postby zzarko » Fri Jan 23, 2009 2:27 am

dinoint wrote:On Ubuntu I just click inside the panda window in order to regain focus, on Windows I can't test, maybe try to set the focus policy on the pandaContainer widget explicitely:

Code: Select all
self.pandaContainer.setFocusPolicy(Qt.StrongFocus)


It's just a wild guess I don't know if it'll works.


Unfortunately, it didn't worked. Regaining focus is (for now) a no-no. On Ubuntu everything is working fine from the beginning. Maybe I should post the question at PyQt forum (maybe it isn't Panda-related). Anyway, thanks for trying. BTW, I'm using your code for a little project of mine (http://panda3d.org/phpbb2/viewtopic.php?t=5521)
zzarko
 
Posts: 14
Joined: Mon Jun 30, 2008 11:25 am

Postby zzarko » Sun Feb 22, 2009 7:23 pm

By adding (modified version of drwr's answer for wxWidgets):
Code: Select all
class QTPandaWidget(QWidget):
   def __init__(self, parent=None):
      super(QWidget, self).__init__(parent)
      
   def resizeEvent(self, evt):
      wp = WindowProperties()
      wp.setSize(self.width(), self.height())
      wp.setOrigin(self.x(),self.y())
      base.win.requestProperties(wp)

and replacing
Code: Select all
self.pandaContainer = QWidget(self)

with
Code: Select all
self.pandaContainer = QTPandaWidget(self)

you can make panda window resizeable in PyQt.

Edit: Solved positioning issues (I hope...)
Edit2: It doesn't work with QHBoxLayout, for example... :(
zzarko
 
Posts: 14
Joined: Mon Jun 30, 2008 11:25 am

Postby zzarko » Mon Feb 23, 2009 11:46 am

Now it works with QHBoxLayout...
Code: Select all
class QTPandaWidget(QWidget):
   def __init__(self, parent=None):
      super(QWidget, self).__init__(parent)
      self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
      
   def resizeEvent(self, evt):
      wp = WindowProperties()
      wp.setSize(self.width(), self.height())
      wp.setOrigin(self.x(),self.y())
      base.win.requestProperties(wp)
   
   def minimumSizeHint(self):
      return QSize(400,300)
zzarko
 
Posts: 14
Joined: Mon Jun 30, 2008 11:25 am

Postby benchang » Sat May 30, 2009 8:55 pm

Thanks - this is great!
User avatar
benchang
 
Posts: 202
Joined: Wed May 30, 2007 3:16 pm
Location: Troy, NY

Postby benelgiac » Sat Jul 04, 2009 4:30 am

I've been using your hints to create a Panda+PyQt-based viewer for a non-standard 3d geometry file format. However, I'm facing the problem of not being able to control the scene with mouse and keyboard, in Windows, as reported by others.

Has anyone of you ever wondered some workaround for it? I was wondering about intercepting mouse and keboard controls over the viewer widget in Qt's main loop and then passing them to Panda... what do you think about it? Does Panda provide some way to do this?
benelgiac
 
Posts: 3
Joined: Sat Mar 21, 2009 10:38 am

Postby rdb » Sat Jul 04, 2009 4:39 am

Dunno if this is related, but a few weeks ago someone drwr a fix to Panda CVS related to not being able to receive events correctly in embedded windows or so. So maybe it's a now-fixed Panda bug...
rdb
 
Posts: 10808
Joined: Mon Dec 04, 2006 5:58 am
Location: Netherlands

Postby drwr » Sat Jul 04, 2009 12:59 pm

Yes, it's fixed in Panda CVS now. A workaround until then is to use the Python win32 or ctypes module to call SetFocus(window) whenever you get a mouse event in the Panda window. (The problem is that Panda wasn't resetting the keyboard focus to itself.)

David
drwr
 
Posts: 11425
Joined: Fri Feb 13, 2004 12:42 pm
Location: Glendale, CA

Postby benelgiac » Thu Oct 29, 2009 11:59 am

Good news! It's a long time I don't check the forums but I'm still using Panda so I'm happy for this.

Is the next version scheduled to be released soon? Just so I don't try to compile from source and I get a new binary the next day :D

Thanks
benelgiac
 
Posts: 3
Joined: Sat Mar 21, 2009 10:38 am

Postby rdb » Thu Oct 29, 2009 12:45 pm

1.7.0 will be released before the end of the year. But I could email you a CVS snapshot build (contact me on my email address).
rdb
 
Posts: 10808
Joined: Mon Dec 04, 2006 5:58 am
Location: Netherlands

Postby benelgiac » Mon Mar 08, 2010 4:36 pm

Hi all again. After some time, I get to tinker again with my Panda3d/Pyqt visualizer.

rdb, thanks for your snapshot. Now I'm using the official 1.7.0 but I'm still facing the same problem of not having mouse and keyboard input on windows.

I've tried several tricks, including using ctypes to call SetFocus on the window handle obtained by GetWindow. No luck.

Is there any other thing I could try?

P.S. Unrelated, but could be useful for someone. I had to start the QTimer hooked to world.step with timer.start(1). In fact, timer.start(0) failed to produce a running panda main loop. strange.
benelgiac
 
Posts: 3
Joined: Sat Mar 21, 2009 10:38 am

Postby detectedstealth » Sun Dec 19, 2010 10:11 am

I was able to get this code working with the latest version of Panda3d/Qt4 on Ubuntu 10.10. However watching the prompt I am getting the follow:
display:x11display(error): BadMatch (invalid parameter attributes)

Which looks to be caused by the windowHandle being passed into openDefaultWindow(windowHandle)

Is anyone else having this issue? Or know how to fix it?
Warply Designed - A new level of game development.
http://www.warplydesigned.com
detectedstealth
 
Posts: 9
Joined: Thu Nov 25, 2010 7:38 pm

Postby detectedstealth » Thu Dec 23, 2010 10:50 pm

I found out what the error is:

Instead of
Code: Select all
base.cam.setPos(0, -28, 6)


It should be
Code: Select all
base.camera.setPos(0, -28, 6)


The error was very miss leading but once I changed from cam to camera the error went away.
Warply Designed - A new level of game development.
http://www.warplydesigned.com
detectedstealth
 
Posts: 9
Joined: Thu Nov 25, 2010 7:38 pm

Postby Anon » Fri Dec 24, 2010 3:46 am

The manual says:
The actual camera is defined in ShowBase as a NodePath named base.cam. There is also a plain node above the camera, which is a NodePath called base.camera. Generally you want to control the base.camera NodePath with your code.

This doesnt make much sense if you ask me.
Anon
 
Posts: 1561
Joined: Thu Oct 29, 2009 3:07 am

Next

Return to Code Snippets

Who is online

Users browsing this forum: No registered users and 0 guests