COM Server, wxApp, forms, delphi and other things

Hi,

I have create a COM server of a wxApp object but really I want to create a COM server of a wxFrame. It is possible create a wxFrame object directly, without create first a wxApp.

I want to invoke this COM server from Delphi, i.e, I want to insert de COM server in some place of my delphi form, so the COM server must be a wxFrame and not a wxApp independent application.

The problem is that the wxApp is opened in other window and the execution remain in the mainloop method. And I need insert the COM server in my form window and I want to invoke methods of the COM server from Delphi.

It's possible???

Thanks

Jose wrote:

Hi,

I have create a COM server of a wxApp object but really I want to create a COM server of a wxFrame. It is possible create a wxFrame object directly, without create first a wxApp.

AFAIK, it's not possible to use wxWindows/wxPython components without having a running wxApp. The wxApp does a lot of initialization and management.

However, it is possible (though difficult) to create a wxApp as a responsive COM server. The key is that all wx code needs to be run in a secondary thread, and that thread *must* be the first one that imports wxPython. The COM code will run in your COM client's thread, and should be as independent as possible from the wxApp -- its only purpose should be to forward commands and information.

When I did this, I created a COM server that immediately starts a second thread. That second thread then creates my wxApp. I have a pair of queue.Queue channels to send input and output between the threads, so that a call to a COM server method results in a command token being put in the "input" queue, and the COM server then waits to see a response in the "output" queue. The wxApp uses idle processing to keep checking the input queue (without blocking!), and when it sees a token in the queue it performs the appropriate command and puts a response in the output queue.

There's some strange issues with closing the app that I never did completely resolve, and my idle processing could've been done better. I also have no way for the wxApp to initiate events to the COM side, but for my purposes I don't have a need for that. At some point in the (hopefully not too distant) future, I'm going to be completely redesigning this app, so hopefully I'll be able to work out better solutions to these issues this time.

Jeff Shannon
Technician/Programmer
Credit International

Jeff Shannon wrote:

When I did this, I created a COM server that immediately starts a second thread. That second thread then creates my wxApp. I have a pair of queue.Queue channels to send input and output between the threads, so that a call to a COM server method results in a command token being put in the "input" queue, and the COM server then waits to see a response in the "output" queue. The wxApp uses idle processing to keep checking the input queue (without blocking!), and when it sees a token in the queue it performs the appropriate command and puts a response in the output queue.

There's some strange issues with closing the app that I never did completely resolve, and my idle processing could've been done better. I also have no way for the wxApp to initiate events to the COM side, but for my purposes I don't have a need for that. At some point in the (hopefully not too distant) future, I'm going to be completely redesigning this app, so hopefully I'll be able to work out better solutions to these issues this time.

Can i see some of COM server code? My attempt did not work.

regards,
Niki Spahiev

I would definitely be interested in this sort of thing as well. I have
been attempting similar things and am about to try implementing the win32ui
stuff (raw Win32 api...AHHHH!!!) - which I have no desire to do right
now...

Mark.

···

"Jeff Shannon" <jeff@ccvcorp.com> wrote on 26/02/2003 05:10:52 PM:

At some point in the
(hopefully not too distant) future, I'm going to be completely
redesigning this app, so hopefully I'll be able to work out better
solutions to these issues this time.

Niki Spahiev wrote:

Jeff Shannon wrote:

When I did this, I created a COM server that immediately starts a second thread. That second thread then creates my wxApp.

Can i see some of COM server code? My attempt did not work.

I don't have time to try to resolve the problems I mentioned just now, but I can show you the core part of the COM server code. Be warned that I haven't really looked at this code in over a year (and I was pretty new to wxPython then -- this was one of my learning projects). Some of the code is (failed) attempts to work around the exit-issues. (After the COM server is sent an exit command, it requests that the wxApp shut down, but that shutdown doesn't happen until the wxApp receives another event. I've tried to trigger this with wxWakeUpIdle(), with no luck. I don't think I tried playing with wxCallAfter(), though... ) I should also mention that, while the COM server is theoretically destroyed, the host (COM client) application is still running and it seems to cache the COM server (changes to the COM server code don't show up until I close the application and restart it), so there may be a hidden cache reference that's keeping things alive... I'm not enough of an expert on COM to really understand the interplay here -- I just barely know enough about COM to accomplish this much.

This application is designed to show and manipulate (scale, rotate, etc) specific images under control from another application (which is actually a COM-enabled terminal emulator connected to our database host).

import win32com.server
import pythoncom
import threading, Queue

class ImgCOMServer:
    _public_methods_ = [ ... ]
    _reg_progid_ = "..."
    _reg_clsid_ = "..." # GUID
    _reg_clstx_ = pythoncom.CLSCTX_LOCAL_SERVER

    def __init__(self):
        self.inqueue = Queue.Queue()
        self.outqueue = Queue.Queue()
        self.thread = MyThread(self.inqueue, self.outqueue)
        self.thread.setDaemon(1)
        self.thread.start()

    def SendCmd(self, token):
        print "Sending command %s" % token.command
        self.inqueue.put(token)
        reply = self.outqueue.get()
        if reply.command == token.command and reply.ok:
            print "Received reply: %s" % reply.result
            return reply.result
        raise win32com.server.exception.COMException(
            'Unable to complete command %s' % token.command)

The 'token' being used in the SendCmd method is simply an instance object (of class Token, defined elsewhere), which has a single mandatory attribute 'command' that contains a string indicating the command name, and various optional attributes carrying any additional information a particular command might need. Once the wxApp has handled the command, it puts the same token back into the outqueue, with an 'ok' flag to indicate success or failure and a 'result' which may or may not be None. This currently executes a single command and then waits for a response -- a more complex program might use command ids and nonblocking queue reads to send multiple simultaneous commands, but I didn't need that and didn't want to mess with the complexity. (IIRC, the Thread.setDaemon() call in __init__() was one of those failed work-arounds for the exiting issue that I mentioned...)

class MyThread(threading.Thread):
    def __init__(self, inqueue, outqueue):
        threading.Thread.__init__(self, name='ImageViewerThread')
        self.inqueue = inqueue
        self.outqueue = outqueue

    def run(self):
        print "Starting thread..."
        import ImageApp
        self._app = ImageApp.MyApp(0)
        self._app.inqueue = self.inqueue
        self._app.outqueue = self.outqueue
        print "Entering message loop..."
        self._app.MainLoop()
        print "Message loop exited."
        self.WakeUp()
        self._app.frame = None
        self._app = None

    def WakeUp(self):
        from time import sleep
        sleep(0.25)
        self._app.WakeUp()

The WakeUp() method simply asks the wxApp to call wxWakeUpIdle(), in the hopes of triggering an event that would convince my wxFrame to close. That didn't work. Neither did setting the references to my wx objects to None. The various print statements let me use PythonWin's trace collector to keep track of what's happening.

Note that the ImageApp module is the first point where wxPython.wx is imported, and I don't import ImageApp until I'm inside my running (secondary) thread. ImageApp is a pretty simple module, which simply creates a wxApp and sets idle handling for it, and creates a Controller object that manages windows, process commands, and all that junk. (I'm not sure now why I separated this from the wxApp, and would probably consolidate the two now.)

from wxPython.wx import *
import Controller

class MyApp(wxApp):
    def OnInit(self):
        wxInitAllImageHandlers()
        self._controller = Controller.Controller(self)
        self.SetTopWindow(self._controller.GetTopWindow())
        self.inqueue = None
        self.outqueue = None
        self.IsClosing = 0
        EVT_IDLE(self, self.OnIdle)
        return true

    def OnIdle(self, event):
        if self.IsClosing:
            return
        event.RequestMore()
        if self.inqueue != None:
            try:
                token = self.inqueue.get_nowait()
                self._controller.DispatchCommand(token)
            except Queue.Empty:
                pass

    def WakeUp(self):
        print "Waking up idle handling..."
        wxWakeUpIdle()

Note that my idle handling is horribly inefficient -- it will spin in circles doing nothing until a command is sent. A better way to do this would be to have the COM server call WakeUp when it sends a command, but the WakeUp was added later (to try to resolve other problems) and I did an incomplete job of retrofitting.

The only relevant part of the Controller object is its Exit() method:

    def Exit(self, token):
        print "Exiting ImageViewer"
        self._app.IsClosing = 1
        self.UpdateConfig()
        for win in self.Windows:
            win.Close(1)
        sleep(1)
        self._app.ExitMainLoop()

First, it sets a flag on the wxApp so that OnIdle will simply return, instead of asking for more idle events. I update my wxConfig object (to save window positions, etc), then close all windows. The sleep() call was an attempt to give the windows time to close before I shut down the wxApp, but (again) this didn't work. One of the first things I'd try, if I were to start working on this again, would be to use wxCallAfter() to delay the ExitMainLoop() call...

I hope that these code excerpts help. I'd considered making a Wiki page with this information, but didn't want to do that until I'd fixed the problems I was having, and never got a chance to do that. All I ask is that, if anyone *does* figure out how to get this to exit cleanly, that they share the solution with me. :slight_smile:

Mark Melvin wrote:

···

"Jeff Shannon" <jeff@ccvcorp.com> wrote on 26/02/2003 05:10:52 PM:

At some point in the
(hopefully not too distant) future, I'm going to be completely
redesigning this app, so hopefully I'll be able to work out better
solutions to these issues this time.

I would definitely be interested in this sort of thing as well. I have
been attempting similar things and am about to try implementing the win32ui
stuff (raw Win32 api...AHHHH!!!) - which I have no desire to do right
now...

Well i am actually using MFC(win32ui) because of that. :frowning:

Niki Spahiev

Hi,

I am working on python and VB. I have a com server on python which is a
drawing application. I create an object in VB to start the application when
I click on the menu in the form. If at a time only one instance of the
application is crated then it works properly. If I open multiple instances
of the application without closing the previous windows then also everything
works fine until I close the MDI Form. When i close the MDI Form, the MDI
Form closes but VB hangs.

VB Code :

Private Sub mnuHi_Click()

    Dim a As Object

    Set a = CreateObject("Python.Dhruv")

    a.make

    Set a = Nothing

End Sub

Python Code :

class Dhruv:
    _reg_clsid_ = "{37F1DE73-2DE8-49A9-B5FB-794439E2C618}"
    _reg_desc_ = "Dhruv"
    _reg_progid_ = "Python.Dhruv"
    _reg_clsctx_ = CLSCTX_INPROC
    _public_methods_ = ['make']
    _public_attrs_ = []
    _readonly_attrs_ = []
    _reg_class_spec_ = "d.Dhruv"
    def make(self):
        self.app = wxPySimpleApp()
        self.frame1 = DoodleFrame1(None)
        self.frame1.Show(true)
        self.app.MainLoop()

def RegisterMe():
    import win32com.server.register
    win32com.server.register.UseCommandLine(Dhruv)

RegisterMe()

How can I prevent this??

Dhruv

Hi all,

I have a tabbing issue in a dialog in one of my larger programs which I
have reduced to a somewhat ugly and non-conventional app - but I have not
had my morning coffee yet, so that is my excuse....

When this is executed, and you press the Tab key, the 'End Address' field
is skipped. However, when you Shift-Tab in reverse, all of the controls
are tabbed over properly. I am assuming this is due somehow to the sizer
arrangement I have used, or a flag I have not included, or may simply be a
bug. Can anyone explain this one for me?

Thanks,
Mark.

(See attached file: tabprob.py)

tabprob.py (3.1 KB)

Dhruv Dhody wrote:

Hi,

I am working on python and VB. I have a com server on python which is a
drawing application. I create an object in VB to start the application when
I click on the menu in the form. If at a time only one instance of the
application is crated then it works properly. If I open multiple instances
of the application without closing the previous windows then also everything
works fine until I close the MDI Form. When i close the MDI Form, the MDI
Form closes but VB hangs.

Sorry for not answering this but I know nothing about VB and very little about COM. So either someone else will need to offer help or you'll need to figure it out yourself (and hopefully share the answer with the rest of us.) Repeatedly sending the same message won't get any more answers than sending it once...

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Mark Melvin wrote:

Hi all,

I have a tabbing issue in a dialog in one of my larger programs which I
have reduced to a somewhat ugly and non-conventional app - but I have not
had my morning coffee yet, so that is my excuse....

When this is executed, and you press the Tab key, the 'End Address' field
is skipped. However, when you Shift-Tab in reverse, all of the controls
are tabbed over properly. I am assuming this is due somehow to the sizer
arrangement I have used, or a flag I have not included, or may simply be a
bug. Can anyone explain this one for me?

No, but taking off the wxTE_RICH2 style seems to fix it.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

> When this is executed, and you press the Tab key, the 'End Address'

field

> is skipped. However, when you Shift-Tab in reverse, all of the

controls

> are tabbed over properly. I am assuming this is due somehow to the

sizer

> arrangement I have used, or a flag I have not included, or may simply

be a

> bug. Can anyone explain this one for me?

No, but taking off the wxTE_RICH2 style seems to fix it.

Thanks, Robin. Works for me. I only had the wxTE_RICH2 due to my cut and
paste rituals...

Mark.

···

Robin Dunn <robin@alldunn.com> wrote on 07/03/2003 06:29:37 PM:

More info on this:

It seems that whenever the list data itself is visible I get this error
(when it is not part of the sizer the default size is so small the list
data itself is not visible). I am refreshing the list of data periodically
with the .Refresh() method, and every time this method is called this
assertion occurs. Everything appears to be working fine - but the
assertion is very annoying. Any help on this?

Thanks,
Mark.

   Hi everyone,

   I have wxPython 2.4.0.1 on Win2K with Python 2.2.1.

   I have a wxGenBitmapToggleButton inside a sizer which works fine. I
   have since added a wxListCtrl working in *virtual* mode to the same
   sizer, and I now get an error when I mouse over my
   wxGenBitmapToggleButton as follows:

   Traceback (most recent call last):
     File "C:\Python22\lib\site-packages\wxPython\lib\buttons.py", line
   513, in OnMotion
       if not self.IsEnabled():
     File "C:\Python22\lib\site-packages\wxPython\windows.py", line 309, in
   IsEnabled
       val = apply(windowsc.wxWindow_IsEnabled,(self,) + _args, _kwargs)
   wxPython.wxc.wxPyAssertionError: C++ assertion "wxAssertFailure" failed
   in e:\projects\wx\src\msw\listctrl.cpp(2328): not supposed to be called

   (I don't have an 'E:' drive by the way...)

   If I totally remove the list control, or simply remove the list control
   from the sizer, and just let it sit on top of the other controls I do
   not get the error.

   I could probably reduce my code to an example - but before I go through
   all the labour involved - is this a known issue? If so - how do I work
   around it?

   Thanks,
   Mark.

Actually, the traceback for the Refresh() assertion error is as follows:

Traceback (most recent call last):
  File "ext3demo.py", line 113, in OnDataRefresh
    self.data_list.Refresh()
  File "C:\Python22\lib\site-packages\wxPython\windows.py", line 363, in
Refresh

    val = apply(windowsc.wxWindow_Refresh,(self,) + _args, _kwargs)
wxPython.wxc.wxPyAssertionError: C++ assertion "wxAssertFailure" failed in
e:\projects\wx\src\msw\listctrl.cpp(2328): not supposed to be called

Mark.

It seems that whenever the list data itself is visible I get this error
(when it is not part of the sizer the default size is so small the list
data itself is not visible). I am refreshing the list of data

periodically

with the .Refresh() method, and every time this method is called this
assertion occurs. Everything appears to be working fine - but the
assertion is very annoying. Any help on this?

Thanks,
Mark.

   Hi everyone,

   I have wxPython 2.4.0.1 on Win2K with Python 2.2.1.

   I have a wxGenBitmapToggleButton inside a sizer which works fine. I
   have since added a wxListCtrl working in *virtual* mode to the same
   sizer, and I now get an error when I mouse over my
   wxGenBitmapToggleButton as follows:

   Traceback (most recent call last):
     File "C:\Python22\lib\site-packages\wxPython\lib\buttons.py", line
   513, in OnMotion
       if not self.IsEnabled():
     File "C:\Python22\lib\site-packages\wxPython\windows.py", line 309,

in

   IsEnabled
       val = apply(windowsc.wxWindow_IsEnabled,(self,) + _args, _kwargs)
   wxPython.wxc.wxPyAssertionError: C++ assertion "wxAssertFailure"

failed

   in e:\projects\wx\src\msw\listctrl.cpp(2328): not supposed to be

called

   (I don't have an 'E:' drive by the way...)

   If I totally remove the list control, or simply remove the list

control

   from the sizer, and just let it sit on top of the other controls I do
   not get the error.

   I could probably reduce my code to an example - but before I go

through

   all the labour involved - is this a known issue? If so - how do I

work

···

"Mark Melvin" <Mark.Melvin@dspfactory.com> wrote on 31/03/2003 12:02:10 PM: > > More info on this:

   around it?

   Thanks,
   Mark.

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-users-help@lists.wxwindows.org

Mark Melvin wrote:

   Traceback (most recent call last):
     File "C:\Python22\lib\site-packages\wxPython\lib\buttons.py", line
   513, in OnMotion
       if not self.IsEnabled():
     File "C:\Python22\lib\site-packages\wxPython\windows.py", line 309, in
   IsEnabled
       val = apply(windowsc.wxWindow_IsEnabled,(self,) + _args, _kwargs)
   wxPython.wxc.wxPyAssertionError: C++ assertion "wxAssertFailure" failed
   in e:\projects\wx\src\msw\listctrl.cpp(2328): not supposed to be called

   (I don't have an 'E:' drive by the way...)

I do. :slight_smile:

You need to add this to your virtual list control derived class:

  def OnGetItemImage(self, item):
    return -1.

(There is a bug in the docs.)

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Sweet! It worked.

One more question - I am referencing the data in my ListCtrl to an external
Python list, which I update with new values as they come in - then call
.Refresh() on the ListCtrl. At high speeds, this causes a lot of flicker
(obviously). Is there a better way to do this to avoid the flicker? My
ListCtrl is in inside a sizer, which is right on my main wxFrame. It seems
a bit better if I use wxCLIP_CHILDREN on the main wxFrame.

Thanks again,
Mark.

···

Robin Dunn <robin@alldunn.com> wrote on 31/03/2003 05:33:56 PM: > You need to add this to your virtual list control derived class:

   def OnGetItemImage(self, item):
      return -1.

(There is a bug in the docs.)

Mark Melvin wrote:

···

Robin Dunn <robin@alldunn.com> wrote on 31/03/2003 05:33:56 PM: > > >>You need to add this to your virtual list control derived class:

  def OnGetItemImage(self, item):
     return -1.

(There is a bug in the docs.)

Sweet! It worked.

One more question - I am referencing the data in my ListCtrl to an external
Python list, which I update with new values as they come in - then call
.Refresh() on the ListCtrl. At high speeds, this causes a lot of flicker
(obviously). Is there a better way to do this to avoid the flicker?

You can try Refresh(False) so it doesn't erase the window first. Also, if you can determine if the new/changed items would not be visible, then ther eis no need to call Refresh at all. GetTopItem and GetCountPerPage should help with that.

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

> One more question - I am referencing the data in my ListCtrl to an

external

> Python list, which I update with new values as they come in - then call
> .Refresh() on the ListCtrl. At high speeds, this causes a lot of

flicker

> (obviously). Is there a better way to do this to avoid the flicker?

You can try Refresh(False) so it doesn't erase the window first. Also,
if you can determine if the new/changed items would not be visible, then
ther eis no need to call Refresh at all. GetTopItem and GetCountPerPage
should help with that.

Removing the parent wxCLIP_CHILDREN, and adding the False parameter to
Refresh() worked like a charm. I will look into the other methods for the
cases where I know what has and hasn't changed.

Thanks, Robin.

Mark.

···

Robin Dunn <robin@alldunn.com> wrote on 31/03/2003 06:32:37 PM: