PDA

View Full Version : Master Script ctl list box stuck in loop



ernpchan
12-18-2014, 04:55 PM
I have a list box that's inside of a Master Script. For some reason my list box looks to be continuously running through the functions that populate it. I have print statements in my functions and my console editor continues to print those lines even though the number of items in my list box is correct. What should I be looking for as to the cause of this stuck loop? My script actually works but I don't want garbage commands running in the background.

Sensei
12-18-2014, 08:30 PM
Show source code. And/or video record how it's working..

But event() is the main part of Layout master plugin class. You can't open GUI in event().

Where are you opening GUI of master?

ernpchan
12-18-2014, 08:31 PM
I'll try to post what I can tomorrow.

ernpchan
12-19-2014, 12:16 PM
Where are you opening GUI of master?

In my python script my gui is in:


def inter_ui(self):

I have a ctl_button that populates my listbox as the contents of my listbox are dependent on strings in two other fields. Once I hit the button that populates my listbox it looks like the listbox is continuously calling the functions that get the array of items.

Trying to see what code I can extract that will demonstrate the problem.

ernpchan
12-19-2014, 12:38 PM
Show source code.

If you type in 'Blah' in the field and hit Refresh, you'll get a lot of redundant prints statements back in the Python console. To me it seems odd that it's looping more than once to generate the list. With this stripped down version I'm not seeing the continual looping of print statements in the console over time. When I click on an item it prints back more than expected too. It almost looks like the list box is constantly trying to get it's contents populated which seems odd to me. Admittedly the list box is one of the controls that I struggle with the most. :confused:



__author__ = 'Ernest Chan'
__date__ = 'December 2014'
__copyright__ = 'Copyright (C) 2014'
__version__ = '1.0'
__maintainer__ = 'Ernest Chan'
__email__ = '[email protected]'
__status__ = '1.0'
__lwver__ = '11'

import lwsdk
reload(lwsdk)

console = lwsdk.LWPCoreConsole()
console.show()
console.clear()

script_uiName = 'ListTest'

lwMsg = lwsdk.LWMessageFuncs()
lwScene = lwsdk.LWSceneInfo()
lwItem = lwsdk.LWItemInfo()
lwBo = lwsdk.LWBoneInfo()
lwObjt = lwsdk.LWObjectInfo()
lwLi = lwsdk.LWLightInfo()
lwCam = lwsdk.LWCameraInfo()
lwII = lwsdk.LWInterfaceInfo()
lwImg = lwsdk.LWImageList()

lwChan = lwsdk.LWChannelInfo()
lwEnv = lwsdk.LWEnvelopeFuncs()

# master Layout class
class MyScript(lwsdk.IMaster):
def __init__(self,context):
super(MyScript,self).__init__()
self._ui = None
self._panel = None
self.witemID = None
self.char_text = ''
self.filter1_text = ''
self.filter2_text = ''
self.filter3_text = ''
self.switch = True
self.array = ['Blah_1','Blah_2','Blah_3','Boo']

def event(self,ma):
# need event code
if ma.eventCode == lwsdk.LWEVNT_NOTIFY_SCENE_SAVE_COMPLETE:
pass
return 0.0

def flags(self):
# return LightWave events and command to keep plugin in Master Plugin list Scene
return lwsdk.LWMASTF_RECEIVE_NOTIFICATIONS | lwsdk.LWMAST_SCENE

def inter_ui(self):
self._ui = lwsdk.LWPanels()
# create _ui
self._panel = self._ui.create('List')
self._ui.collect(self, self._panel)

self.witem = self._panel.str_ctl('Parent Item Name',20)
self.witem.set_str(self.char_text)
self.witem.move(5,5)

#####
self.filter1 = self._panel.str_ctl('',14)
self.filter1.set_str(self.filter1_text)
self.filter1.move(0,30)

self.refreshButton1 = self._panel.button_ctl('Refresh')
self.refreshButton1.set_event(self.refresh1)
self.refreshButton1.set_w(47)
self.refreshButton1.move(5,55)

self.list1 = self._panel.listbox_ctl('',100,10,self.list_name1, self.list_count1)
self.list1.set_w(150)
self.list1.set_h(300)
self.list1.move(5,80)
self.list1.set_select(self.list1_func)

self._panel.open(lwsdk.PANF_NOBUTT)

return lwsdk.AFUNC_OK

# function to save values to scene file
def inst_save(self,state):
state.lwsave_str(self.witem.get_str())
state.lwsave_str(self.filter1.get_str())

# function to load values from scene file
def inst_load(self,state):
result, value = state.lwload_str()
if result == 0:
pass
self.char_text = value

result, value = state.lwload_str()
if result == 0:
pass
self.filter1_text = value

# callback function to refresh list1
def refresh1(self,control,data):
self.list1.redraw()
print 'done'

#callback function for list1
def list1_func(self, control, user_data, row, selecting):
if row < 0:
return # list selections are being cleared

for i in range(0,len(self.meshArray1)):
if i == row:
print i
else:
print 'not sel'

def list_name1(self,control,userdata,row):
print 'dot'
self.meshArray1 = self.listArrBuild1('none')

if row == -1:
return 'column name'
return self.meshArray1[row]

# function to call functions to build item arrays for list1
def listArrBuild1(self,arg):
self.myArray1 = []
self.idArray1 = []
if not self.filter1.get_str() == '':
self.crawl1()
print 'ping'
return self.myArray1

# function to get number of items in list1
def list_count1(self, control, userdata):
self.counter1 = len(self.listArrBuild1('none'))
return self.counter1

# function to populate name and id arrays for list1
def crawl1(self):
for i in self.array:
if self.filter1.get_str() in i:
self.myArray1.append(i)
print self.myArray1

ServerTagInfo = [
( script_uiName, lwsdk.SRVTAG_USERNAME | lwsdk.LANGID_USENGLISH ),
( script_uiName, lwsdk.SRVTAG_BUTTONNAME | lwsdk.LANGID_USENGLISH ),
( 'Utilities/Python', lwsdk.SRVTAG_MENU | lwsdk.LANGID_USENGLISH )
]

# Master Layout script
ServerRecord = { lwsdk.MasterFactory(script_uiName, MyScript) : ServerTagInfo }

Sensei
12-19-2014, 01:00 PM
See LWSDK/html/handler.html

Master is handler therefor it has Interface() call-back:


XCALL_( int )
Interface( int version, GlobalFunc *global, LWInterface *local,
void *serverData )
{
if ( version != LWINTERFACE_VERSION )
return AFUNC_BADVERSION;

local->panel = NULL;
local->options = Options;
local->command = NULL;

return AFUNC_OK;
}

But its job is not creation and opening GUI, but filling LWXPanelID (local->panel), or filling local->options by yet another call-back, that's really creating LWPanelID panels. Options() HAS to open modal (waiting for user closing) window. Opening non-modal window by handler is different story.. You have to keep track of LWPanelID and must override window closing event to nullify that pointer when user closed it.. Must not create another instance of panel. What happens if you double click on your master in Layout Masters listview? Don't you have multiple windows?

As you can easily see, there are two GUI systems: LWPanel and LWXPanel
creation of each of them inside of handlers is different.
And even different is handling them, when you want to stay on screen for longer time.. It's tricky.

Python is on lower level that above, but it must use lower level architecture. I am wondering how it translates..

ernpchan
12-19-2014, 01:03 PM
Ah, that makes sense.

You're right, I can open another instance of my window. Never noticed that cuz I always close it.

So is this a limitation of python or me? 8)

Sensei
12-19-2014, 01:06 PM
Added you script to LW: as I thought, multiple same windows (move their position)...

- - - Updated - - -



So is this a limitation of python or me? 8)

It's because you didn't handled non-modal LWPanel in handlers correctly... It's not so easy.
Same is in C/C++ if you won't do it properly.

You know it might end up with serious crash? Panel pointer is pointing to the latest opened window. If user will start playing with older instances, might be crash, depending on what is done inside of call-backs..

ernpchan
12-19-2014, 01:14 PM
It's because you didn't handled non-modal LWPanel in handlers correctly... It's not so easy.
Same is in C/C++ if you won't do it properly.



Was afraid of that. I'll have to see if I can find an example of a proper non-modal panel for python. I just re-purpose other people's examples as I'm not good enough to start with a blank file. :hey:

Sensei
12-19-2014, 01:40 PM
If you type in 'Blah' in the field and hit Refresh, you'll get a lot of redundant prints statements back in the Python console.

When I am doing it, nothing happens...

ernpchan
12-19-2014, 02:13 PM
Try the text field above the list box. Not the one at the very top. Sorry.

Sensei
12-19-2014, 03:36 PM
Ahhh, literally "Blah". I thought so it means whatever I want..

See how it works here in attached video.
http://www.trueart.pl/Files/ListTest.avi

ernpchan
12-19-2014, 03:45 PM
You should keep the console open, the script works but in the console you'll see a bunch of looping print statements. A lot more than I would expect so my concern is that the script is continuing to do something the background using up unnecessary resources.

Sensei
12-19-2014, 04:00 PM
I had to analyze entire script to understand how it works..

It's wrong.

list_name1() call-back is called as many times as you have returned count.
count() call-back is called once, per refresh of listview.
So if something causes refresh (f.e. open window, move it, resize it, bring window to front etc. etc), list_name1() will be called 3 times for "Blah" filter. And you're regenerating array many many times, because name() is using self.listArrBuild1().

You should have ALREADY created array before name()/count() is executed.

So, generate array should be completely outside of name()/count().
Good place for it, might be Refresh button event call-back, or Filter change string event call-back.
Inside of them you need to cause refresh of listview.
In LWSDK we do it by

#define REDRAW_CON(ctl) ((*ctl->draw)(ctl,DR_REFRESH))
#define RENDER_CON(ctl) ((*ctl->draw)(ctl,DR_RENDER))

or refresh/redraw entire panel.

ernpchan
12-19-2014, 04:20 PM
Ah ok, thanks Sensei!