Question about wxpython with reactivex

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Question about wxpython with reactivex

Gene Horodecki
Hi, I'm making an application with wxpython, and one part of it contains a list of servers and I am signing into those servers with pexpect/ssh.  The logins happen on a background thread managed by rxpy, which does have a WxScheduler available.  As servers are logged in, I would like to refresh the listctrl as each server is updated in the list.  I have not had good results.  First I tried sending a call for the list to populate directly from the thread, this caused crashes and unpredictable behavior which I wasn't too surprised about.  Then I tried wx.CallAfter but this doesn't end up triggering until after the reactive chain is complete.  I also tried wx.PostEvent which I expected to work, but it has the same behavior as CallAfter.  I'm wondering what I am missing here.

I tried a demo with wxpython and threads I found on the internet, and it starts a native Python Thread and seems to be able to update the contents of a label with a PostEvent so I'm not sure why I'm not getting the same behavior with rxpy.  Any suggestions?

--
You received this message because you are subscribed to the Google Groups "wxPython-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Question about wxpython with reactivex

Robin Dunn
On Thursday, April 12, 2018 at 7:42:58 AM UTC-7, Gene Horodecki wrote:
Hi, I'm making an application with wxpython, and one part of it contains a list of servers and I am signing into those servers with pexpect/ssh.  The logins happen on a background thread managed by rxpy, which does have a WxScheduler available.  As servers are logged in, I would like to refresh the listctrl as each server is updated in the list.  I have not had good results.  First I tried sending a call for the list to populate directly from the thread, this caused crashes and unpredictable behavior which I wasn't too surprised about.  Then I tried wx.CallAfter but this doesn't end up triggering until after the reactive chain is complete.  I also tried wx.PostEvent which I expected to work, but it has the same behavior as CallAfter.  I'm wondering what I am missing here.

I tried a demo with wxpython and threads I found on the internet, and it starts a native Python Thread and seems to be able to update the contents of a label with a PostEvent so I'm not sure why I'm not getting the same behavior with rxpy.  Any suggestions?


Are you sure rxpy is using background threads and not blocking the main thread? If anything is blocking the return to the MainLoop then no events (wx.CallAfter uses events in its implementation) can be delivered, and things will behave in the way you describe.

--
Robin

--
You received this message because you are subscribed to the Google Groups "wxPython-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Question about wxpython with reactivex

Gene Horodecki
Thanks for the response.  RxPy is definitely using background threads since I can still select different items in the foreground while it is going. 

Since I posted this, I have set up a demo that is behaving as you describe.  My app has a listcontrol in a panel in a notebook so I guess I have to figure out why it is not behaving the same way.

Here is my working demo with a reactive call:


from __future__ import print_function
import time
import wx
from rx import Observable

from threading import Thread



# Define notification event for thread completion
from rx.concurrency import ThreadPoolScheduler

EVT_RESULT_ID = wx.NewId()


def EVT_RESULT(win, func):
"""Define Result Event."""
win.Connect(-1, -1, EVT_RESULT_ID, func)


class ResultEvent(wx.PyEvent):
"""Simple event to carry arbitrary result data."""

def __init__(self, data):
"""Init Result Event."""
wx.PyEvent.__init__(self)
self.SetEventType(EVT_RESULT_ID)
self.data = data

class TestListControl(wx.ListCtrl):
def __init__(self,parent):
super(TestListControl, self).__init__(parent,
#size=(-1,100),
style=wx.LC_REPORT|wx.BORDER_SUNKEN)

self.list_items = [
"0 test runs",
]
self.InsertColumn(0,'Test Runs')

def populate(self):
self.DeleteAllItems()
row = 0
for i in self.list_items:
self.InsertItem(row,i)
row += 1

def set_data(self,new_data):
self.list_items.append(new_data)

class MyForm(wx.Frame):
# ----------------------------------------------------------------------
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Tutorial")
list_contents = [
]

# Add a panel so it looks the correct on all platforms
panel = wx.Panel(self, wx.ID_ANY)
self.displayLbl = wx.StaticText(panel, label="Amount of time since thread started goes here")
self.btn = btn = wx.Button(panel, label="Start Thread")
self.test_list = test_list = TestListControl(panel)

btn.Bind(wx.EVT_BUTTON, self.onButton)

sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.displayLbl, 0, wx.ALL | wx.CENTER, 5)
sizer.Add(btn, 0, wx.ALL | wx.CENTER, 5)
sizer.Add(test_list, 0, wx.ALL | wx.CENTER, 5)
panel.SetSizer(sizer)

# Set up event handler for any worker thread results
EVT_RESULT(self, self.updateDisplay)
self.pool_scheduler = ThreadPoolScheduler(5)
self.test_list.populate()

def map_func(self,data):
wx.PostEvent(self, ResultEvent(data))
return data

# ----------------------------------------------------------------------
def onButton(self, event):
"""
Runs the thread
"""
#TestThread(self)
Observable.range(1,100).subscribe_on(self.pool_scheduler).interval(1000).map(self.map_func).subscribe(
on_next=lambda x: print("on_next",x),
on_completed=lambda : print("on_completed"),
on_error=lambda x: print("on_error",x)

)
self.displayLbl.SetLabel("Thread started!")
btn = event.GetEventObject()
btn.Disable()

# ----------------------------------------------------------------------
def updateDisplay(self, msg):
"""
Receives data from thread and updates the display
"""
t = msg.data
if isinstance(t, int):
self.displayLbl.SetLabel("Time since thread started: %s seconds" % t)
self.test_list.set_data("%s runs" % t)
self.test_list.populate()
else:
self.displayLbl.SetLabel("%s" % t)
self.btn.Enable()


# ----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
app = wx.App()
frame = MyForm().Show()
app.MainLoop()




On Thursday, 12 April 2018 13:26:32 UTC-3, Robin Dunn wrote:
On Thursday, April 12, 2018 at 7:42:58 AM UTC-7, Gene Horodecki wrote:
Hi, I'm making an application with wxpython, and one part of it contains a list of servers and I am signing into those servers with pexpect/ssh.  The logins happen on a background thread managed by rxpy, which does have a WxScheduler available.  As servers are logged in, I would like to refresh the listctrl as each server is updated in the list.  I have not had good results.  First I tried sending a call for the list to populate directly from the thread, this caused crashes and unpredictable behavior which I wasn't too surprised about.  Then I tried wx.CallAfter but this doesn't end up triggering until after the reactive chain is complete.  I also tried wx.PostEvent which I expected to work, but it has the same behavior as CallAfter.  I'm wondering what I am missing here.

I tried a demo with wxpython and threads I found on the internet, and it starts a native Python Thread and seems to be able to update the contents of a label with a PostEvent so I'm not sure why I'm not getting the same behavior with rxpy.  Any suggestions?


Are you sure rxpy is using background threads and not blocking the main thread? If anything is blocking the return to the MainLoop then no events (wx.CallAfter uses events in its implementation) can be delivered, and things will behave in the way you describe.

--
Robin

--
You received this message because you are subscribed to the Google Groups "wxPython-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Question about wxpython with reactivex

Gene Horodecki
Thank you for your willingness to help.  I have been on some very UNhelpful mailing lists recently, so I really appreciate the reach out.

It turns out my problem is not the event at all, but the fact that sqlite scoped sessions are not commiting the database during the thread and I am reading expired data on the refresh.

I'm changing my listctrl to use memory data first and database data second, should resolve the issue.



On Thursday, 12 April 2018 13:33:06 UTC-3, Gene Horodecki wrote:
Thanks for the response.  RxPy is definitely using background threads since I can still select different items in the foreground while it is going. 

Since I posted this, I have set up a demo that is behaving as you describe.  My app has a listcontrol in a panel in a notebook so I guess I have to figure out why it is not behaving the same way.

Here is my working demo with a reactive call:


from __future__ import print_function
import time
import wx
from rx import Observable

from threading import Thread



# Define notification event for thread completion
from rx.concurrency import ThreadPoolScheduler

EVT_RESULT_ID = wx.NewId()


def EVT_RESULT(win, func):
"""Define Result Event."""
win.Connect(-1, -1, EVT_RESULT_ID, func)


class ResultEvent(wx.PyEvent):
"""Simple event to carry arbitrary result data."""

def __init__(self, data):
"""Init Result Event."""
wx.PyEvent.__init__(self)
self.SetEventType(EVT_RESULT_ID)
self.data = data

class TestListControl(wx.ListCtrl):
def __init__(self,parent):
super(TestListControl, self).__init__(parent,
#size=(-1,100),
style=wx.LC_REPORT|wx.BORDER_SUNKEN)

self.list_items = [
"0 test runs",
]
self.InsertColumn(0,'Test Runs')

def populate(self):
self.DeleteAllItems()
row = 0
for i in self.list_items:
self.InsertItem(row,i)
row += 1

def set_data(self,new_data):
self.list_items.append(new_data)

class MyForm(wx.Frame):
# ----------------------------------------------------------------------
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Tutorial")
list_contents = [
]

# Add a panel so it looks the correct on all platforms
panel = wx.Panel(self, wx.ID_ANY)
self.displayLbl = wx.StaticText(panel, label="Amount of time since thread started goes here")
self.btn = btn = wx.Button(panel, label="Start Thread")
self.test_list = test_list = TestListControl(panel)

btn.Bind(wx.EVT_BUTTON, self.onButton)

sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.displayLbl, 0, wx.ALL | wx.CENTER, 5)
sizer.Add(btn, 0, wx.ALL | wx.CENTER, 5)
sizer.Add(test_list, 0, wx.ALL | wx.CENTER, 5)
panel.SetSizer(sizer)

# Set up event handler for any worker thread results
EVT_RESULT(self, self.updateDisplay)
self.pool_scheduler = ThreadPoolScheduler(5)
self.test_list.populate()

def map_func(self,data):
wx.PostEvent(self, ResultEvent(data))
return data

# ----------------------------------------------------------------------
def onButton(self, event):
"""
Runs the thread
"""
#TestThread(self)
Observable.range(1,100).subscribe_on(self.pool_scheduler).interval(1000).map(self.map_func).subscribe(
on_next=lambda x: print("on_next",x),
on_completed=lambda : print("on_completed"),
on_error=lambda x: print("on_error",x)

)
self.displayLbl.SetLabel("Thread started!")
btn = event.GetEventObject()
btn.Disable()

# ----------------------------------------------------------------------
def updateDisplay(self, msg):
"""
Receives data from thread and updates the display
"""
t = msg.data
if isinstance(t, int):
self.displayLbl.SetLabel("Time since thread started: %s seconds" % t)
self.test_list.set_data("%s runs" % t)
self.test_list.populate()
else:
self.displayLbl.SetLabel("%s" % t)
self.btn.Enable()


# ----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
app = wx.App()
frame = MyForm().Show()
app.MainLoop()




On Thursday, 12 April 2018 13:26:32 UTC-3, Robin Dunn wrote:
On Thursday, April 12, 2018 at 7:42:58 AM UTC-7, Gene Horodecki wrote:
Hi, I'm making an application with wxpython, and one part of it contains a list of servers and I am signing into those servers with pexpect/ssh.  The logins happen on a background thread managed by rxpy, which does have a WxScheduler available.  As servers are logged in, I would like to refresh the listctrl as each server is updated in the list.  I have not had good results.  First I tried sending a call for the list to populate directly from the thread, this caused crashes and unpredictable behavior which I wasn't too surprised about.  Then I tried wx.CallAfter but this doesn't end up triggering until after the reactive chain is complete.  I also tried wx.PostEvent which I expected to work, but it has the same behavior as CallAfter.  I'm wondering what I am missing here.

I tried a demo with wxpython and threads I found on the internet, and it starts a native Python Thread and seems to be able to update the contents of a label with a PostEvent so I'm not sure why I'm not getting the same behavior with rxpy.  Any suggestions?


Are you sure rxpy is using background threads and not blocking the main thread? If anything is blocking the return to the MainLoop then no events (wx.CallAfter uses events in its implementation) can be delivered, and things will behave in the way you describe.

--
Robin

--
You received this message because you are subscribed to the Google Groups "wxPython-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Question about wxpython with reactivex

sebastian lópez
In reply to this post by Gene Horodecki
Good morning

I'm playing with your script and so I decided to put some sleep time and chek if your gui is frozen and It did --> I append it

from time import sleep
from random import randint

...
sleep(randint(1,5))
...

Then I think the time to be connected from one computer to another one varies. Then I give a try to my library and the gui it's not frozen anymore

To make that I introduced a threat decorator under the updating function where it suppose you have to connect with another computers.
I hope this will help you.



El jueves, 12 de abril de 2018, 11:33:06 (UTC-5), Gene Horodecki escribió:
Thanks for the response.  RxPy is definitely using background threads since I can still select different items in the foreground while it is going. 

Since I posted this, I have set up a demo that is behaving as you describe.  My app has a listcontrol in a panel in a notebook so I guess I have to figure out why it is not behaving the same way.

Here is my working demo with a reactive call:


from __future__ import print_function
import time
import wx
from rx import Observable

from threading import Thread



# Define notification event for thread completion
from rx.concurrency import ThreadPoolScheduler

EVT_RESULT_ID = wx.NewId()


def EVT_RESULT(win, func):
"""Define Result Event."""
win.Connect(-1, -1, EVT_RESULT_ID, func)


class ResultEvent(wx.PyEvent):
"""Simple event to carry arbitrary result data."""

def __init__(self, data):
"""Init Result Event."""
wx.PyEvent.__init__(self)
self.SetEventType(EVT_RESULT_ID)
self.data = data

class TestListControl(wx.ListCtrl):
def __init__(self,parent):
super(TestListControl, self).__init__(parent,
#size=(-1,100),
style=wx.LC_REPORT|wx.BORDER_SUNKEN)

self.list_items = [
"0 test runs",
]
self.InsertColumn(0,'Test Runs')

def populate(self):
self.DeleteAllItems()
row = 0
for i in self.list_items:
self.InsertItem(row,i)
row += 1

def set_data(self,new_data):
self.list_items.append(new_data)

class MyForm(wx.Frame):
# ----------------------------------------------------------------------
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Tutorial")
list_contents = [
]

# Add a panel so it looks the correct on all platforms
panel = wx.Panel(self, wx.ID_ANY)
self.displayLbl = wx.StaticText(panel, label="Amount of time since thread started goes here")
self.btn = btn = wx.Button(panel, label="Start Thread")
self.test_list = test_list = TestListControl(panel)

btn.Bind(wx.EVT_BUTTON, self.onButton)

sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.displayLbl, 0, wx.ALL | wx.CENTER, 5)
sizer.Add(btn, 0, wx.ALL | wx.CENTER, 5)
sizer.Add(test_list, 0, wx.ALL | wx.CENTER, 5)
panel.SetSizer(sizer)

# Set up event handler for any worker thread results
EVT_RESULT(self, self.updateDisplay)
self.pool_scheduler = ThreadPoolScheduler(5)
self.test_list.populate()

def map_func(self,data):
wx.PostEvent(self, ResultEvent(data))
return data

# ----------------------------------------------------------------------
def onButton(self, event):
"""
Runs the thread
"""
#TestThread(self)
Observable.range(1,100).subscribe_on(self.pool_scheduler).interval(1000).map(self.map_func).subscribe(
on_next=lambda x: print("on_next",x),
on_completed=lambda : print("on_completed"),
on_error=lambda x: print("on_error",x)

)
self.displayLbl.SetLabel("Thread started!")
btn = event.GetEventObject()
btn.Disable()

# ----------------------------------------------------------------------
def updateDisplay(self, msg):
"""
Receives data from thread and updates the display
"""
t = msg.data
if isinstance(t, int):
self.displayLbl.SetLabel("Time since thread started: %s seconds" % t)
self.test_list.set_data("%s runs" % t)
self.test_list.populate()
else:
self.displayLbl.SetLabel("%s" % t)
self.btn.Enable()


# ----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
app = wx.App()
frame = MyForm().Show()
app.MainLoop()




On Thursday, 12 April 2018 13:26:32 UTC-3, Robin Dunn wrote:
On Thursday, April 12, 2018 at 7:42:58 AM UTC-7, Gene Horodecki wrote:
Hi, I'm making an application with wxpython, and one part of it contains a list of servers and I am signing into those servers with pexpect/ssh.  The logins happen on a background thread managed by rxpy, which does have a WxScheduler available.  As servers are logged in, I would like to refresh the listctrl as each server is updated in the list.  I have not had good results.  First I tried sending a call for the list to populate directly from the thread, this caused crashes and unpredictable behavior which I wasn't too surprised about.  Then I tried wx.CallAfter but this doesn't end up triggering until after the reactive chain is complete.  I also tried wx.PostEvent which I expected to work, but it has the same behavior as CallAfter.  I'm wondering what I am missing here.

I tried a demo with wxpython and threads I found on the internet, and it starts a native Python Thread and seems to be able to update the contents of a label with a PostEvent so I'm not sure why I'm not getting the same behavior with rxpy.  Any suggestions?


Are you sure rxpy is using background threads and not blocking the main thread? If anything is blocking the return to the MainLoop then no events (wx.CallAfter uses events in its implementation) can be delivered, and things will behave in the way you describe.

--
Robin

--
You received this message because you are subscribed to the Google Groups "wxPython-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.

rxpy_2_frozen.py (3K) Download Attachment
rxpy_not_frozen.py (3K) Download Attachment