PDA

View Full Version : PyQT in LW



xchrisx
07-02-2012, 01:14 PM
Im starting to make the transition over to Python from Lscript (slowly :)) and was curious if anyone has got a UI from PyQT inside of LW working and if so what their workflow was. Any info would be greatly appreciated.

thanks,
Chris

evenflcw
07-02-2012, 02:09 PM
PyQT... are you sure that is necessary? Both XPanels and LWPanels are available in the Python SDK (and I assume they work). Unless you are doing something totally new that goes beyond current LW gui paradigm you shouldn't need anything more. With LWPanels you should also have enough to build your own variants of LW controls similar to how you can build your own controls in lscript by drawing and processing mouse inputs... but with less limits and cleaner code (I believe the New Scene Editor would be mostly custom controls created using LWPanels). The only other reason I can think of for not using XPanels or LWPanels is simply not liking their syntax or whatnot.

(Check the C SDK documentation to see what you can do with XPanels and LWPanels.)

xchrisx
07-02-2012, 04:26 PM
I haven't yet dug too deep into the LW SDK as far as interface gui goes but if it is anything like the quirky Lscript interface I would prefer to avoid it. Also, I am branching out into other 3d software as well as my own custom non-3d tools (where pyqt can be utilized for interfaces) and I was hoping to reduce the amount of application specific interface code like LWPanels and find something more uniform. That was my main reason for wanting to see if pyqt is possible. If that sounds like a pipe dream please feel free to let me know :).

evenflcw
07-02-2012, 05:04 PM
I haven't ever used QT. But I would think building your own gui with PyQT would be a bigger task than to use the gui toolkits available in the SDK of each package. But perhaps the reuse will offset that. Personally not a fan of customized guis that are an ill fit to the overall theme of the host program. I like 3rd party tools to seem as integrated as everything else. So beware, I am abit negative from the start! And to also mimic the style for each host program in QT would reasonably take longer than using what's available.

If I had to do this, I'd use each package sdk for the gui (to get the 100% native controls, look and feel; assuming they are sufficient), but make sure to add an extra layer to abstract/extract the common gui interactions needed across the different implementations. Call it MVC, MVVM or whatnot.

Looking forward to hear what someone with actual QT experience has to say.

xchrisx
07-02-2012, 05:35 PM
Hey I don't mind the negativity as long as its constructive. Especially if it makes me think about something I may have overlooked :). As far as the whole "not fitting" into the program, I prefer added functionality rather than making it match the look of the program. But yes, anyone with QT experience please chime in.

Red_Oddity
07-18-2012, 07:36 AM
Hey Chris, as promised, here's a simplified PyQt UI test that works in LW.

As you can see, it is hardly optimal, nor is my code clean, but it shows what is possible.

Another nice experiment is looking into interactive QT UI plugins (should be possible when you create your own exec loop for Qt by calling processEvents on the QApplication)



import lwsdk
from PyQt4.QtCore import Qt, QString, SIGNAL
from PyQt4.QtGui import QApplication, QVBoxLayout, QPushButton, QLineEdit, QHBoxLayout, QFileDialog, QMainWindow, QWidget, QDialogButtonBox

__author__ = "Sven Neve"
__date__ = "July 17 2012"
__copyright__ = "House of Secrets"
__version__ = "1.0"
__maintainer__ = "Sven Neve"
__email__ = "sven"
__status__ = "Test"
__lwver__ = "11"

class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.filename = None
self.addWidgets()
#connect signals to UI elements
self.btn.clicked.connect(self.choose_file)
#attach event listener to MainWindow
self.connect(self, SIGNAL('filenamechanged'), self.show_file)
#show the MainWindow
self.show()

def addWidgets(self):
#create some UI elements
self.btn = QPushButton(QString("Choose File"))
self.text = QLineEdit()
self.btnbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
cw = QWidget()
hbox = QHBoxLayout()
hbox.addWidget(self.text)
hbox.addWidget(self.btn)
vbox = QVBoxLayout()
vbox.setMargin(0)
vbox.addLayout(hbox)
hbox2 = QHBoxLayout()
hbox2.addWidget(self.btnbox)
vbox.addLayout(hbox2)
cw.setLayout(vbox)
self.setCentralWidget(cw)
flags = self.windowFlags()
#resize MainWindow
self.setMaximumHeight(24)
self.setMinimumWidth(400)
self.setWindowTitle("PyQt Dialog Test")
self.setWindowFlags(flags | Qt.WindowStaysOnTopHint )

def choose_file(self):
filename = QFileDialog.getOpenFileName(self, 'Open File', '.')
self.filename = filename
# emit signal, notifying the MainWindow that the filename has changed
self.emit(SIGNAL('filenamechanged'))

def show_file(self):
self.text.setText(self.filename)

class QtDialogTest(lwsdk.IGeneric):
def __init__(self, context):
super(QtDialogTest, self).__init__()
self.app = QApplication([])
self.do_it()

def do_it(self):
self.app.main = MainWindow()
# connect signals to Ok Cancel buttonbox
self.app.main.btnbox.accepted.connect(self.do_acce pt)
self.app.main.btnbox.rejected.connect(self.do_canc el)

# Start the main loop.
# Or, you can optionally tie it into your own main loop or the main
# loop of your host app and then update the Qt UI by calling
# app.processEvents() rather than app.exec_()
self.app.exec_()

#clean up the app after the window closes
del self.app
return lwsdk.AFUNC_OK

def do_accept(self):
#check if there's a file name in the line edit
if self.app.main.filename :
print("do something with %s" % self.app.main.filename)
else:
print("Guess no file was selected or we canceled")
self.app.main.close()

def do_cancel(self):
print("Canceled")
self.app.main.close()

ServerTagInfo = [
( "Python QtDialog Test", lwsdk.SRVTAG_USERNAME | lwsdk.LANGID_USENGLISH ),
( "QtDialog Test", lwsdk.SRVTAG_BUTTONNAME | lwsdk.LANGID_USENGLISH ),
( "Utilities/Python", lwsdk.SRVTAG_MENU | lwsdk.LANGID_USENGLISH )
]

ServerRecord = { lwsdk.GenericFactory("LW_PyQtDialogTest", QtDialogTest) : ServerTagInfo }

Red_Oddity
07-18-2012, 08:32 AM
Here's an example that uses it's own loop (tied to a timer/wait loop), which during the UI refresh also refreshes LW.
Note: don't run this plugin again when it is still running (it WILL crash LW) as i didn't build in a function that checks for existing instances.

Again, not the best code example, there's probably much much better and safer ways to do this, but this might help people in the right direction when using 3rd party UI frameworks.



import lwsdk
from PyQt4.QtCore import Qt, QString, SIGNAL
from PyQt4.QtGui import QApplication, QVBoxLayout, QPushButton, QLineEdit, QHBoxLayout, QFileDialog, QMainWindow, QWidget, QDialogButtonBox
import time

__author__ = "Sven Neve"
__date__ = "July 17 2012"
__copyright__ = "House of Secrets"
__version__ = "1.0"
__maintainer__ = "Sven Neve"
__email__ = "sven"
__status__ = "Test"
__lwver__ = "11"

class MainWindow(QMainWindow):
APP_RUNNING = False

def __init__(self):
QMainWindow.__init__(self)
self.APP_RUNNING = True
self.filename = None
self.addWidgets()
#connect signals to UI elements
self.btn.clicked.connect(self.choose_file)
#attach event listener to MainWindow
self.connect(self, SIGNAL('filenamechanged'), self.show_file)
#show the MainWindow
self.show()

def addWidgets(self):
#create some UI elements
self.btn = QPushButton(QString("Choose File"))
self.text = QLineEdit()
self.btnbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
cw = QWidget()
hbox = QHBoxLayout()
hbox.addWidget(self.text)
hbox.addWidget(self.btn)
vbox = QVBoxLayout()
vbox.setMargin(0)
vbox.addLayout(hbox)
hbox2 = QHBoxLayout()
hbox2.addWidget(self.btnbox)
vbox.addLayout(hbox2)
cw.setLayout(vbox)
self.setCentralWidget(cw)
flags = self.windowFlags()
#resize MainWindow
self.setMaximumHeight(24)
self.setMinimumWidth(400)
self.setWindowTitle("PyQt Dialog Test")
self.setWindowFlags(flags | Qt.WindowStaysOnTopHint )

def choose_file(self):
filename = QFileDialog.getOpenFileName(self, 'Open File', '.')
self.filename = filename
# emit signal, notifying the MainWindow that the filename has changed
self.emit(SIGNAL('filenamechanged'))

def show_file(self):
self.text.setText(self.filename)

class QtDialogTestCL(lwsdk.ILayoutTool):
def __init__(self, context):
super(QtDialogTestCL, self).__init__()
self.app = QApplication([])
self.do_it()

def do_it(self):
self.app.main = MainWindow()
# connect signals to Ok Cancel buttonbox
self.app.main.btnbox.accepted.connect(self.do_acce pt)
self.app.main.btnbox.rejected.connect(self.do_canc el)

# Start the main loop.
while self.app.main.APP_RUNNING:
self.app.processEvents()
lwsdk.command("RefreshNow")
time.sleep(0.01)

#clean up the app after the window closes
del self.app
return lwsdk.AFUNC_OK

def do_accept(self):
#check if there's a file name in the line edit
if self.app.main.filename :
print("do something with %s" % self.app.main.filename)
else:
print("Guess no file was selected or we canceled")
self.app.main.APP_RUNNING = False
self.app.main.close()

def do_cancel(self):
print("Canceled")
self.app.main.APP_RUNNING = False
self.app.main.close()

ServerTagInfo = [
( "Python QtDialog Test Custom Loop", lwsdk.SRVTAG_USERNAME | lwsdk.LANGID_USENGLISH ),
( "QtDialog Test CL", lwsdk.SRVTAG_BUTTONNAME | lwsdk.LANGID_USENGLISH ),
( "Utilities/Python", lwsdk.SRVTAG_MENU | lwsdk.LANGID_USENGLISH )
]

ServerRecord = { lwsdk.GenericFactory("LW_PyQtDialogTestCL", QtDialogTestCL) : ServerTagInfo }

xchrisx
07-18-2012, 09:05 AM
Thanks for the examples, very much appreciated.

xchrisx
07-18-2012, 10:34 AM
Unfortunately I couldn't get it to work. I installed PyQT in my Python 2.7 folder (both are 32 bit) and I get the error:

ImportError: DLL load failed: %1 is not a valid Win32 application.

I also tried to use the sys.append and point to the PyQT folder. As a last resort I even added them to my PYTHONPATH system variable and still a no go. Anyone have any suggestions of what I am doing wrong or ideas of maybe how to fix this?

Red_Oddity
07-19-2012, 10:36 AM
That error usually has to do with mismatched modules for different architectures.

I think your LW (i take it it is the 32bit version), is importing from a wrong path.

xchrisx
07-19-2012, 10:37 AM
Yes I am running LW 64 bit. Does that mean I have to have the Python 2.7 64bit and PyQT 64 bit installs to work properly with them?

BigHache
07-19-2012, 07:05 PM
Just an FYI, it was stated officially that XPanels was not working yet in Python. Two updates have come out since but I have not seen a statement that says it is now fully functioning.

Red_Oddity
07-20-2012, 03:08 AM
Yes I am running LW 64 bit. Does that mean I have to have the Python 2.7 64bit and PyQT 64 bit installs to work properly with them?

Yes.

vfxdomain
08-19-2012, 05:59 PM
Guys. if i am writing my tool using PyLW instead of LScript, is there any option to create GUI visualy, like i did a while ago with LScript Interface Builder or whatever it called, for my LScript? Handcoding GUI isn't fun at all :( RubyMotion, are you listening?! :P

BigHache
08-19-2012, 06:42 PM
I'm afraid not, no. Believe me that was one of the first questions I had.

walfridson
08-20-2012, 08:43 AM
Why not use pyqt designer and just load the .ui directly in code?

vfxdomain
08-20-2012, 03:49 PM
Damn it, that's a real shame ;( I saw that LS GUI Designer can export panels *.c files and so-called interface description files, *.idf, both no use i guess? So yeah, PyQt is the only way to go then...Python and LScript can't communicate, right?

vfxdomain
08-20-2012, 04:46 PM
BTW, sorry for slight off-topic, but how people usually building GUI's for their C/C++ plugins? Using LS GUI designer and exporting C code, or any other options (besides handcoding, obviously, lol)

Lightwolf
08-20-2012, 07:07 PM
BTW, sorry for slight off-topic, but how people usually building GUI's for their C/C++ plugins? Using LS GUI designer and exporting C code, or any other options (besides handcoding, obviously, lol)
Handcoding. :D
Granted, with helper classes to manage the layout a little when it comes to LWPanels.
I do prefer XPanels if I have the chance to use them though.

Cheers,
Mike

Cageman
08-20-2012, 08:05 PM
PyQT... are you sure that is necessary? Both XPanels and LWPanels are available in the Python SDK (and I assume they work). Unless you are doing something totally new that goes beyond current LW gui paradigm you shouldn't need anything more.

Cross-platform UI for calling scripts..

We have a system that allows us to use the same GUI in Maya, Max and Nuke and you simply click on the software within the UI that you are using for the moment (it does autosense it if I'm not misstaken). This UI, appart from displaying scripts for the selected software to execute, also has Favourites. Very cool stuff, but, unfortunately, we (or our scripter/inventor for this project) has yet to be able to do something to support LW.

walfridson
08-22-2012, 03:44 AM
Damn it, that's a real shame ;( I saw that LS GUI Designer can export panels *.c files and so-called interface description files, *.idf, both no use i guess? So yeah, PyQt is the only way to go then...Python and LScript can't communicate, right?

Have a look at qt designer, you're gonna love it. Far better than ls gui designer. Easier workflow as well, creating editing and connecting.


Cross-platform UI for calling scripts..

We have a system that allows us to use the same GUI in Maya, Max and Nuke and you simply click on the software within the UI that you are using for the moment (it does autosense it if I'm not misstaken). This UI, appart from displaying scripts for the selected software to execute, also has Favourites. Very cool stuff, but, unfortunately, we (or our scripter/inventor for this project) has yet to be able to do something to support LW.
Most likely all qt, so with the new lw any python junkie should be able to import it. Damn wish I lived in Malmo..... ;)

Tollman
09-04-2012, 08:05 AM
I'm gonna jump right in here since this discussion seemed similar to what i need.
I'm trying to run this cross application tool, (the one that Cageman talked about), in Lightwave, but i seem to have some difficulties with submodules to PyQt4, the PyQt4 module itself loads in all fine and dandy, but the QtCore and QtGui cant be found.
When typing: "from PyQt4 import QtGui" for example, the console gives me an error saying: "ImportError: DLL load failed: The specified procedure could not be found."
Is there something i need to install to access those?

EDIT: oh, and i'm using Lightwave 11.0.3 x64

xchrisx
09-04-2012, 06:07 PM
You are not the only one, I am still struggling to get it working. and I am using 11.02 x64

Red_Oddity
09-05-2012, 08:11 AM
Are you using a 64 bit Python and 64 bit PyQt?
You NEED 64 bit versions of everything installed when working with LW x64.

A DLL error with Python usually means a mismatch in architectures.

Also, check you environment variables, those tend to be the cause of many problems with mixed Python / app architectures as well.

jeric_synergy
09-05-2012, 09:29 AM
Just checked my Python version, which SHOULD be 2.7.3, but it's still stuck at 2.2.

Also: how do you check 64bit python, when TMK all DOS windows (cmd.exe) are 32bit??

walfridson
09-05-2012, 04:33 PM
If you start python in a windows console you will see a 64bit python.exe in your process monitor.

jeric_synergy
09-05-2012, 04:49 PM
What makes me wonder is the "system32" in the title line of the DOS console.

Of course, it's not even displaying the version number (2.7.3) I installed for both 32 and 64 bit , so I have no idea what's going on.

walfridson
09-05-2012, 05:17 PM
are you sure "python" is pointing to the correct install
check your environment settings in control panel or in python
import os
for path in os.environ["PATH"].split(";"):
print path

walfridson
09-05-2012, 05:20 PM
Here's a little test how to load .ui files. I've attached the ui - just open it in designer.exe

The script has some paths you need to change before testing - my sys append and BASE_FOLDER...

However LW is crashing upon second launch, not sure why yet...
But have a look.


import lwsdk

import sys, os
from os.path import *

sys.path.append("C:/Python27/Lib/site-packages")

from PyQt4 import QtGui, QtCore, uic

__author__ = "x"
__date__ = "July 17 2012"
__copyright__ = "x"
__version__ = "1.0"
__maintainer__ = "x"
__email__ = "x"
__status__ = "x"
__lwver__ = "11"

app = QtGui.QApplication(sys.argv)
BASE_FOLDER = "D:/workspace/eclipse_projects/lw_test/"

class SetupWindow(lwsdk.IGeneric):
def __init__(self, parent):
super(SetupWindow, self).__init__()
uipath = BASE_FOLDER + "lw_test.ui"
self.win = uic.loadUi(uipath)
self.win.show()

QtGui.QWidget.connect(self.win.actionDo_Something, QtCore.SIGNAL("triggered()"), self.menu_action_do_something)

QtGui.QWidget.connect(self.win.pushButton_button, QtCore.SIGNAL("clicked()"), self.button_clicked_method)

app.exec_()

def menu_action_do_something(self):
print "hello there"

def button_clicked_method(self):
print "you clicked the button!"


ServerTagInfo = [
("Python QtDialog Test", lwsdk.SRVTAG_USERNAME | lwsdk.LANGID_USENGLISH),
("QtDialog Test", lwsdk.SRVTAG_BUTTONNAME | lwsdk.LANGID_USENGLISH),
("Utilities/Python", lwsdk.SRVTAG_MENU | lwsdk.LANGID_USENGLISH)
]

ServerRecord = { lwsdk.GenericFactory("LW_PyQtDialogTest", SetupWindow) : ServerTagInfo }

Tollman
09-10-2012, 09:02 AM
Are you using a 64 bit Python and 64 bit PyQt?
You NEED 64 bit versions of everything installed when working with LW x64.

A DLL error with Python usually means a mismatch in architectures.

Also, check you environment variables, those tend to be the cause of many problems with mixed Python / app architectures as well.

I just did a fresh install of both Python itself and the PyQt package (both 64 bit), but i still get the error :confused:
How about the standard package of PyQt that comes with the installation of Lightwave, they dont really work either, are they for another version of the app? (LightWave11.0\bin\lib\PyQt4)
And the environment variables, i'm not very familiar with those, but they list the path to the Python27 folder (along with a list of other folders), is that correct?

I find this very strange, and i have a hard time believing that it's the wrong bit version since the core "PyQt4" package works, and it's just the submodules that fail. :stumped:

jeric_synergy
09-10-2012, 11:55 AM
I find this very strange, and i have a hard time believing that it's the wrong bit version since the core "PyQt4" package works, and it's just the submodules that fail. :stumped:
At a much noobier level I just had a similar issue, so what the heck: check your PYTHONHOME system variable, and also PYTHONPATH to make sure the submodule libraries in question are findable by Python.

walfridson
09-11-2012, 01:59 AM
Print your sys.path and os.environ["PATH"] and post here

Tollman
09-11-2012, 02:41 AM
the sys.path is:
['C:\\Program Files\\NewTek\\LightWave11.0\\support\\plugins\\sc ripts\\Python\\Layout\\ChannelFilter',
'C:\\Program Files\\NewTek\\LightWave11.0\\support\\plugins\\sc ripts\\Python\\Layout\\CustomObject',
'C:\\Program Files\\NewTek\\LightWave11.0\\support\\plugins\\sc ripts\\Python\\Layout\\DisplacementMap',
'C:\\Program Files\\NewTek\\LightWave11.0\\support\\plugins\\sc ripts\\Python\\Layout\\Generic',
'C:\\Program Files\\NewTek\\LightWave11.0\\support\\plugins\\sc ripts\\Python\\Layout\\ImageFilter',
'C:\\Program Files\\NewTek\\LightWave11.0\\support\\plugins\\sc ripts\\Python\\Layout\\ItemMotion',
'C:\\Program Files\\NewTek\\LightWave11.0\\support\\plugins\\sc ripts\\Python\\Layout\\Master',
'C:\\Program Files\\NewTek\\LightWave11.0\\support\\plugins\\sc ripts\\Python\\Layout\\ObjectReplacement',
'C:\\Program Files\\NewTek\\LightWave11.0\\support\\plugins\\sc ripts\\Python\\Layout\\Shader',
'C:\\Program Files\\NewTek\\LightWave11.0\\support\\plugins\\sc ripts\\Python\\Layout\\Utility\\Tool',
'C:\\Program Files\\NewTek\\LightWave11.0\\support\\plugins\\sc ripts\\Python\\Modeler\\CommandSequence',
'',
'C:\\Program Files\\NewTek\\LightWave11.0\\bin\\python27.zip',
'C:\\Python27\\Lib',
'C:\\Python27\\DLLs',
'C:\\Python27\\Lib\\lib-tk',
'C:\\Program Files\\NewTek\\LightWave11.0\\bin',
'C:\\Python27',
'C:\\Python27\\lib\\site-packages',
'C:/Program Files/NewTek/LightWave11.0/bin',
'C:\\Users\\jonas.borgman\\.NewTek\\LightWave\\11. 0\\plugins']


and the environ reads:

'C:\\Program Files\\NewTek\\LightWave11.0\\bin;
C:\\Python27\\Lib\\site-packages;
C:\\Python27\\Lib\\site-packages\\PyQt4;
C:\\Program Files (x86)\\NVIDIA Corporation\\PhysX\\Common;
C:\\Program Files\\Autodesk\\mrstand3.7.1-maya2009\\bin;
T:\\Rendersoftware\\Autodesk\\Maya2009\\bin;
T:\\Rendersoftware\\Autodesk\\Maya2008\\bin;
T:\\RenderSoftware\\Autodesk\\Maya8.5_x64\\bin;
C:\\Windows\\system32;
C:\\Windows;
C:\\Windows\\System32\\Wbem;
C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0;
C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0;
C:\\Program Files (x86)\\Windows Imaging\\;
C:\\Program Files\\Common Files\\Autodesk Shared\\;
C:\\Program Files (x86)\\QuickTime\\QTSystem\\;
C:\\Program Files\\Perforce;
C:\\Program Files (x86)\\Autodesk\\Backburner\\'