#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2004
    Posts
    17
    Rep Power
    0

    Image Map with wxPython


    I've unsucessfully trying to make an image map using wxpython using wxRegion for a while now. And I had 2 question: is it possible? and if yes, how.
  2. #2
  3. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2004
    Posts
    27
    Rep Power
    0
    Originally Posted by WheresTheAnyKey
    I've unsucessfully trying to make an image map using wxpython using wxRegion for a while now. And I had 2 question: is it possible? and if yes, how.
    I've yet to find anything that I (joe programmer) have not been able to do with Python and wxPython!!!

    Python enthusiasm aside...

    I'm assuming when you say image map, you are talking about testing for mouse clicks on the image. With that assumption...

    I've not used the wxRegion extensively, but it doesn't seem to support the poly-mode in Python. This means that if you are wanting to use the wxRegion's built-in Contains method, then you will be limited to rectangular mapping regions. If this is okay, then great! If not, then you will have to implement an algorithm to check for poly-type regions yourself. In this case you probably wouldn't need to bother with wxRegions at all.

    At this point, the question is: what is the context of the image you want to work with? How are you displaying the image? This determines how you get the mouse input and how to get your coordinates straight (origins, client coords., screen coords., etc.).

    Here's a possible example:
    Code:
    class ImagePanel( wx.Panel ):
        def __init__( self, parent ):
            wx.Panel.__init__( self, parent )
    
            self.Bind( wx.EVT_PAINT, self.__OnPaint )
            self.Bind( wx.EVT_LEFT_DOWN, self.__OnLeftDown )
            self.bmp = wx.Bitmap( "c:/images/test.bmp" )
    
            # Hardcoding the list here, but you could add methods
            # to set this.
            self.map = [ (0,0), (10,0), ... , (0,10) ]
    
        def __OnPaint( self, event ):
            dc = wx.PaintDC( self )
            dc.DrawBitmap( self.bmp, (0, 0) )
            event.Skip()
    
        def __OnLeftDown( self, event ):
            pos = event.GetPosition()
            region = MyHitTest( pos )
            
            # <Do something in response to the region here>
    
            event.Skip()
            
        def MyHitTest( self, pos ):
            # Compare pos to the self.map list of points using
            # custom algorithm.
            # - OR -
            # Check the Contains methods of the wxRegions.
            ...
            return hit_region
    
    ...    
    
    f=wx.Frame( None, -1, "Test Window" )
    p=ImagePanel( f )
    f.Show()
    To recap...

    If you can live with rectangular map regions, then wxRegions should work for you. Otherwise, no, wxRegions probably won't be needed, but you should still be able to accomplish your image map.

    Hope this helps,

    Derrick
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2004
    Posts
    17
    Rep Power
    0
    Originally Posted by p4j
    I'm assuming when you say image map, you are talking about testing for mouse clicks on the image. With that assumption...
    What i meant by image map is that when a user clicks on different regions on a picture different functions are intiated. I'm not sure what you meant about "testing for mouse clicks".

    I am confused why i can't just make a wxRegion class that covers a part of a picture and then use:
    Code:
    EVT_LEFT_DCLICK(self.Region1,self.OnRegion1Function)
    But when i do that i get this error
    Code:
    Traceback (most recent call last):
      File "C:\Python23\Programs\Prog2.py", line 131, in -toplevel-
        app = MyApp(0)
      File "C:\Python23\lib\site-packages\wx\core.py", line 5042, in __init__
        self._BootstrapApp()
      File "C:\Python23\lib\site-packages\wx\core.py", line 4791, in _BootstrapApp
        return _core.PyApp__BootstrapApp(*args, **kwargs)
      File "C:\Python23\Programs\Prog2.py", line 126, in OnInit
        frame = MainWindow(NULL, -1, "XP Custumizer")
      File "C:\Python23\Programs\Prog2.py", line 74, in __init__
        EVT_LEFT_DCLICK(self.Region,self.OnWindow)
      File "C:\Python23\lib\site-packages\wx\core.py", line 2671, in __call__
        self.Bind(target, id1, id2, func)
      File "C:\Python23\lib\site-packages\wx\core.py", line 2645, in Bind
        target.Connect(id1, id2, et, function)
    AttributeError: 'Region' object has no attribute 'Connect'
    I was hoping somebody could tell me what "Connect" is because it isn't apart of the code that i have written.

    I am also confused about the code that you have written for me(i'm not too good with wxPython yet). For instance i don't understand what "self.Bind" is supposed to do or the MyHitTest function.

    I hope it wouldn't be a problem if you could be a little more explanatory.
  6. #4
  7. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2004
    Posts
    27
    Rep Power
    0
    I was hoping somebody could tell me what "Connect" is because it isn't apart of the code that i have written.
    Connect is a method of the wxEvtHandler class. All windows are derived from the wxEvtHandler class. A wxRegion is NOT, however. The EVT_LEFT_CLICK event binder is expecting a wxEvtHandler class (or a derivation of it) for its first argument. So your giving it a wxRegion produces the error.

    A quick side note on your traceback:
    Code:
      File "C:\Python23\Programs\Prog2.py", line 74, in __init__
        EVT_LEFT_DCLICK(self.Region,self.OnWindow)  
      File "C:\Python23\lib\site-packages\wx\core.py", line 2671, in __call__
        self.Bind(target, id1, id2, func)
      File "C:\Python23\lib\site-packages\wx\core.py", line 2645, in Bind
        target.Connect(id1, id2, et, function)
    AttributeError: 'Region' object has no attribute 'Connect'
    This is the line of YOUR code that is causing the error (for the above reason). More on this next. And here is where your wxRegion is failing to act like a wxEvtHandler.


    For instance i don't understand what "self.Bind" is supposed to do
    self.Bind is another way to accomplish event binding. In my example, notice I supply it with 3 things: 1) self - the object that will be generating the event (in this case self is a wx.Panel), 2) wx.EVT_LEFT_DOWN - the event I am wanting to bind to, and 3) self.__OnLeftDown - the method I want to handle the event. This method of event binding is used widely in the wxPython demos. Look there for more examples of this style of event binding.

    An important thing to note here is that no matter HOW you bind an event, at the very least, the 3 things I mentioned above are always needed: the object generating the event, the type of event you are interested in, and the method handling the event. In your code:
    Code:
    EVT_LEFT_DCLICK(self.Region1,self.OnRegion1Function)
    you only supply 2 valid items (EVT_LEFT_DCLICK and self.OnRegion1Function). As mentioned above, self.Region1 is invalid because it is not derived from wxEvtHandler. In English, this means it doesn't generate events! It might help to think of a wxRegion as dealing only with 'virtual' space. You need something (like the wx.Panel in my example) that is in 'real' space to generate a click event for you (or any event for that matter). When the appropriate event handler is called (self.__OnLeftDown), you can then test the click position against your 'virtual' regions within the handler. And this will tell you which region has been clicked so that you can call the appropriate self.OnRegionXyzFunction.
    ... or the MyHitTest function.
    First of all, I should have put "self.MyHitTest", sorry if this confused you. Other than that, perhaps a working demo would help most here. Modify the example as follows and run:

    Code:
        def __init__( self, parent ):
            ...
            rg1 = wx.Region(0, 0, 30, 30)
            rg2 = wx.Region(0, 30, 30, 30)
            rg3 = wx.Region(30, 0, 30, 30)
            rg4 = wx.Region(30, 30, 30, 30)
    
            self.regions = [rg1, rg2, rg3, rg4]
    
        def __OnLeftDown( self, event ):
            pos = event.GetPosition()
            region = self.MyHitTest( pos )
    
            # Do something in response to the region.
            if region and self.regions.index(region) == 0:
                self.OnRegion1Function()
    
            event.Skip()
            
        def MyHitTest( self, pos ):
            # Check the Contains methods of the wxRegions.
            for region in self.regions:
                if region.Contains( *pos ) == wx.InRegion:
                    return region
    
            return None
    
        def OnRegion1Function( self ):
            print 'Region1 Clicked'
    (Text in red is modified or new code.)

    The above modifications should provide you with a fully functional, wxRegion-based example to work with. I should probably note that I have implemented one of several ways in which you could get wxPython to slap a BMP on the screen. I recommend you browse the wxPython demo for other ways to do this.

    I am using wxPython v. 2.5.1.5.

    Additionally, you should have (or can get) a wxWindows help file included with the 2.5.1.5 binaries that gives an alphabetical listing of all the wx classes and details of their use. Or if you prefer, you can view it online (http://www.wxpython.org/onlinedocs.php). The demo is also VERY useful in providing example code. Both resources *highly* recommended if you haven't found them already.

    Derrick
    Last edited by p4j; August 29th, 2004 at 10:17 PM. Reason: minor changes
  8. #5
  9. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2004
    Posts
    17
    Rep Power
    0
    I do have the wxWidgets Reference Manual and the Demo, but i can't find any demo related to an image map.
    I tried out your code:
    Code:
    import wx
    
    class ImagePanel( wx.Panel ):
         def __init__( self, parent ):
    
            self.Bind( wx.EVT_LEFT_DOWN, self.__OnLeftDown )
            self.bmp = wx.Bitmap( "C:/Python23/Programs/test.bmp" )
            
            rg1 = wx.Region(0, 0, 30, 30)
            rg2 = wx.Region(0, 30, 30, 30)
            rg3 = wx.Region(30, 0, 30, 30)
            rg4 = wx.Region(30, 30, 30, 30)
    
            self.regions = [rg1, rg2, rg3, rg4]
    
    
         def __OnLeftDown( self, event ):
            pos = event.GetPosition()
            region = self.MyHitTest( pos )
    
            # Do something in response to the region.
            if region and self.regions.index(region) == 0:
                self.OnRegion1Function()
    
            event.Skip()
            
         def MyHitTest( self, pos ):
            # Check the Contains methods of the wxRegions.
            for region in self.regions:
                if region.Contains( *pos ) == wx.InRegion:
                    return region
    
            return None
    
         def OnRegion1Function( self ):
            print 'Region1 Clicked'
            
    f=wx.Frame( None, -1, "Test Window" )
    p=ImagePanel( f )
    f.Show()
    but i got this error

    Code:
    Traceback (most recent call last):
      File "C:\Documents and Settings\inuk\Desktop\test.py", line 39, in ?
        p=ImagePanel( f )
      File "C:\Documents and Settings\inuk\Desktop\test.py", line 6, in __init__
        self.Bind( wx.EVT_LEFT_DOWN, self.__OnLeftDown )
      File "C:\Python23\lib\site-packages\wx\core.py", line 2613, in Bind
        event.Bind(self, id, id2, handler)
      File "C:\Python23\lib\site-packages\wx\core.py", line 2645, in Bind
        target.Connect(id1, id2, et, function)
      File "C:\Python23\lib\site-packages\wx\core.py", line 2580, in Connect
        return _core.EvtHandler_Connect(*args, **kwargs)
    TypeError: Expected a pointer
    It seemed to that the self.Bind was causing trouble, so i thought i would just take it out and see if the rest of the code worked. But i got this error.
    Code:
    13:50:05: Error: Can't create window of class wxWindowClass (error 1407: cannot
    find window class.)
    13:50:05: Warning: No handler found for image type.
    I've tweaked around with the code you have given me, it shows everything, but the functions don't work.
    This is the code that i made:
    Code:
    import wx
    
    class MainWindow(wx.Frame):
        def __init__(self,parent,id,title):
            wxFrame.__init__(self, parent,wx.ID_ANY,title, wx.DefaultPosition,
                             wxSize(800,600))
                                 
            #self.Bind( wx.EVT_LEFT_DOWN, self.__OnLeftDown )
            #self.bmp = wx.Bitmap( "C:\Python23\Programs\Test.bmp" )
            Pic = wx.Image('C:\Python23\Programs\Test.bmp', wx.BITMAP_TYPE_BMP).ConvertToBitmap()
            self.bmp = wx.StaticBitmap(self,-1,Pic,wx.DefaultPosition)
            
            rg1 = wx.Region(0, 0, 30, 30)
            rg2 = wx.Region(0, 30, 30, 30)
            rg3 = wx.Region(30, 0, 30, 30)
            rg4 = wx.Region(30, 30, 30, 30)
    
            self.regions = [rg1, rg2, rg3, rg4]
    
    
        def __OnLeftDown( self, event ):
            pos = event.GetPosition()
            region = self.MyHitTest( pos )
    
            # Do something in response to the region.
            if region and self.regions.index(region) == 0:
                self.OnRegion1Function()
    
            event.Skip()
            
        def MyHitTest( self, pos ):
            # Check the Contains methods of the wxRegions.
            for region in self.regions:
                if region.Contains( *pos ) == wx.InRegion:
                    return region
    
            return None
    
        def OnRegion1Function( self, event ):
            wxStaticText(self,-1,'Region1 Clicked',(100,100))
            
    class MyApp(wx.App):
        def OnInit(self):
            frame = MainWindow(NULL, -1, "XP Custumizer")
            frame.Show(true)
            self.SetTopWindow(frame)
            return true 
    
    app = MyApp(0)
    app.MainLoop()
    I've been messing around with it for a while and i can't figure out exactly whats wrong.
  10. #6
  11. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2004
    Posts
    27
    Rep Power
    0
    Originally Posted by WheresTheAnyKey
    I do have the wxWidgets Reference Manual and the Demo, but i can't find any demo related to an image map.
    I was referring you to the demo as a source of example code and inspiration, not specifically for an image map example.

    The code I gave you was 'fully-funcitonal' according to my unspoken definition of being run from PyShell. Sorry about not specifying this. But you did great in making up for my blunder and adding the appropriate App stuff.

    The code you posted has lots of problems with it. Many of them are basic Python syntax issues. I'll breeze over them real quick for the benefit of instruction, and then post the FULL, truly fully-functional, example for you to play with. BTW, I am new to posting on Dev Shed and have not become familiar with any sort of paste bin. If someone would kindly direct me in its existance and use, it would be much appreciated!

    To the code!

    For starters:
    Code:
            wxFrame.__init__(self, parent,wx.ID_ANY,title, wx.DefaultPosition, 
                              wxSize(800,600))
            # SHOULD BE:
            wx.Frame.__init__(self, parent, wx.ID_ANY, title, size = (800, 600))
    Comments:
    1) wxFrame should be wx.Frame. The same goes for wxSize.

    2) One thing I appreciate about wxPython is that they do a great job of providing default values and use of keywords. Utilize this. Specifically, you specified wx.DefaultPosition where they already do that for you. If you want to do this for clarity's sake, understandable. But you don't NEED to. And in my opinion, it is just as clear.

    3) You don't need to wrap your size (or position) information in a wx.Size class. Again, wxPython is very flexible about this, allowing you to treat a wx.Size and a tuple interchangeably.

    4) Because I left the position info out (allowing it to default), I then must use the "size=" keyword assignment syntax. Otherwise, the tuple would be considered a position.


    Code:
            #self.bmp = wx.Bitmap( "C:\Python23\Programs\Test.bmp" )
            Pic = wx.Image('C:\Python23\Programs\Test.bmp', wx.BITMAP_TYPE_BMP).ConvertToBitmap()
            self.bmp = wx.StaticBitmap(self,-1,Pic,wx.DefaultPosition)
            #VERSUS
            self.bmp = wx.Bitmap( "C:/Python23/Programs/test.bmp" )
    Comments:
    1) You must not have directly copied my example, because your repetition of my code uses '\' rather than '/' to separate directories. This is a CRUCIAL difference! In Python, the '\' has a special use (i.e. '\n', '\b', '\t', etc), so in order to get a true '\' character in the string, you must use '\\' instead. Play around in the python shell to understand this more. This will produce a filename that makes no sense and that does not exist. As a result, you won't properly load your BMP, and your program will not be happy!

    2) In my example, I specify the file: C:\Python23\Program\test.bmp. YOU MUST CHANGE THIS!!! I put this there as a place-holder only. If this file DNE on your machine, then you will not properly load the BMP and bad stuff will happen.

    3) Is there any reason in particular that you need to break the BMP loading into an Image and then convert to a bitmap rather than load straight into a Bitmap?

    4) Again, you specify wx.DefaultPosition which is not *required*. The only reason you should have this here is if you intentionally use it to add clarity to your code.


    Code:
            frame = MainWindow(NULL, -1, "XP Custumizer")
            frame.Show(true)
    Comments:
    1) This is not C, you should use None instead of NULL.

    2) Python is case-sensitive, so be sure to capitalize True and False

    And here is the modified example:
    Code:
    import wx
    
    
    class ImagePanel( wx.Panel ):
        def __init__( self, parent ):
            wx.Panel.__init__( self, parent )
    
            self.Bind( wx.EVT_PAINT, self.__OnPaint )
            self.Bind( wx.EVT_LEFT_DOWN, self.__OnLeftDown )
    
            #NOTE: You MUST replace this filename with your own
            self.bmp = wx.Bitmap( "C:/Python23/Programs/test.bmp" )
    
            rg1 = wx.Region(0, 0, 30, 30)
            rg2 = wx.Region(0, 30, 30, 30)
            rg3 = wx.Region(30, 0, 30, 30)
            rg4 = wx.Region(30, 30, 30, 30)
    
            self.regions = [rg1, rg2, rg3, rg4]
    
        def __OnPaint( self, event ):
            dc = wx.PaintDC( self )
            dc.DrawBitmap( self.bmp, (0, 0) )
            event.Skip()
    
        def __OnLeftDown( self, event ):
            pos = event.GetPosition()
            region = self.MyHitTest( pos )
    
            # Do something in response to the region.
            if region and self.regions.index(region) == 0:
                self.OnRegion1Function()
    
            event.Skip()
            
        def MyHitTest( self, pos ):
            # Check the Contains methods of the wxRegions.
            for region in self.regions:
                if region.Contains( *pos ) == wx.InRegion:
                    return region
    
            return None
    
        def OnRegion1Function(self):
            print 'Region1 Clicked'
    
    
    class MainWindow( wx.Frame ):
        def __init__( self, parent, id, title ):
            wx.Frame.__init__( self, parent, wx.ID_ANY, title, size=(800,600) )
            self.test_panel = ImagePanel( self )
    
           
    class MyApp( wx.App ):
        def OnInit( self ):
            #This must be called to...
            wx.InitAllImageHandlers()
            frame = MainWindow( None, -1, "XP Custumizer" )
    
            frame.Show(True)
            self.SetTopWindow( frame )
            return True 
    
    app = MyApp(0)
    app.MainLoop()
    I put this in a package directory of mine called 'my' and I named the file 'wany.py'. I then went to the command prompt and typed:
    Code:
    C:\Python23>python
    Python 2.3.3 (#51, Dec 18 2003, 20:22:39) [MSC v.1200 32 bit (Intel)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import my.wany
    Region1 Clicked
    Region1 Clicked
    >>>
    Down hill from here, right?

    Derrick
    Last edited by p4j; August 31st, 2004 at 05:12 PM. Reason: highlighting
  12. #7
  13. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2004
    Posts
    17
    Rep Power
    0
    Thanks for all your help. The funny thing is that i didn't copy your file path exactly. You didn't look closely your file said "c:\images\test.bmp" and mine said "C:\Python23\Programs\test.bmp". I guess we both have the habit of calling are test files test.*whatever*. I finally understand it. Thank for you help and i make sure to keep your tips in mind.
  14. #8
  15. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Aug 2004
    Posts
    27
    Rep Power
    0
    Great!

IMN logo majestic logo threadwatch logo seochat tools logo