Hi,

I am having a problem with function and class syntax.

I have one class (MakePanel1) that creates a button and label. The button-click event of the button is linked to a function (daclick1) that changes the text of the label. This works well.

I have another class (MakePanel2) that creates a second button. I want this second button to call the button-click function of the first button.

My incorrect call MakePanel1.daclick1() whilst in the MakePanel2 class produces the error ' TypeError: unbound method daclick1() must be called with MakePanel1 instance as first argument (got nothing instead) '.

#!/usr/bin/python


import wx
import time


class MakePanel1(wx.Panel):
    def __init__(self, Parent, *args, **kwargs):
        wx.Panel.__init__(self, Parent, *args, **kwargs)
        
        self.dalabel = wx.StaticText(self, -1, "   panel 1 label   ")
        self.dabutton1 = wx.Button(self, label="button 1") 
        self.dabutton1.Bind(wx.EVT_BUTTON, self.daclick1 )
        self.bs1 = wx.BoxSizer(wx.HORIZONTAL)
        self.bs1.Add(self.dabutton1,0)
        self.bs1.Add(self.dalabel,0)
        self.SetSizer(self.bs1)
        
    def daclick1(self, event):
        self.dalabel.SetLabel(str(time.time()))


class MakePanel2(wx.Panel):
    def __init__(self, Parent, *args, **kwargs):
        wx.Panel.__init__(self, Parent, *args, **kwargs)

        self.dabutton2 = wx.Button(self, label="button 2") 
        self.dabutton2.Bind(wx.EVT_BUTTON, self.daclick2 )
        self.bs2 = wx.BoxSizer(wx.HORIZONTAL)
        self.bs2.Add(self.dabutton2,0,wx.ALL,20)
        self.SetSizer(self.bs2)

    def daclick2(self, event):
        MakePanel1.daclick1()

class DisFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)

        self.Panel1 = MakePanel1(self)
        self.Panel2 = MakePanel2(self)

        bs = wx.BoxSizer(wx.VERTICAL)
        bs.Add(self.Panel1,1,wx.EXPAND);
        bs.Add(self.Panel2,1,wx.EXPAND);

        self.SetSizer(bs)
        self.Fit()


if __name__ == '__main__':
    app = wx.App()
    daframe = DisFrame(None)
    daframe.Show()
    app.MainLoop()

How do I call the button-click function of button 1 whilst in the button-click function of button 2 ?

Any assistance appreciated. Thanks.

One solution to fix problem is to have MakePanel2 inherit MakePanel1, then you only need to create instance of MakePanel1 in MakePanel2. For some odd reason wx.BoxSizer() gets confused with the label location. Look the code over.

#!/usr/bin/python


import wx
import time


class MakePanel1(wx.Panel):
    def __init__(self, Parent, *args, **kwargs):
        wx.Panel.__init__(self, Parent, *args, **kwargs)
        
        self.dalabel = wx.StaticText(self, -1, "   panel 1 label   ")
        self.dabutton1 = wx.Button(self, label="button 1") 
        self.dabutton1.Bind(wx.EVT_BUTTON, self.daclick1 )
        self.bs1 = wx.BoxSizer(wx.HORIZONTAL)
        self.bs1.Add(self.dabutton1,0)
        self.bs1.Add(self.dalabel,0)
        self.SetSizer(self.bs1)
        
    def daclick1(self, event):
        self.dalabel.SetLabel(str(time.time()))


class MakePanel2(MakePanel1):
    """Note that MakePanel2 inherits MakePanel1"""
    def __init__(self, Parent, *args, **kwargs):
        wx.Panel.__init__(self, Parent, *args, **kwargs)

        self.dabutton2 = wx.Button(self, label="button 2") 
        self.dabutton2.Bind(wx.EVT_BUTTON, self.daclick2 )
        self.bs2 = wx.BoxSizer(wx.HORIZONTAL)
        self.bs2.Add(self.dabutton2,0,wx.ALL,20)
        self.SetSizer(self.bs2)

    def daclick2(self, event):
        #MakePanel1.daclick1()  # gives error, need to make instance first
        self.Panel1 = MakePanel1(self)
        self.Panel1.daclick1(self)

class DisFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)

        self.Panel1 = MakePanel1(self)
        self.Panel2 = MakePanel2(self)

        bs = wx.BoxSizer(wx.VERTICAL)
        bs.Add(self.Panel1,1,wx.EXPAND);
        bs.Add(self.Panel2,1,wx.EXPAND);

        self.SetSizer(bs)
        self.Fit()


if __name__ == '__main__':
    app = wx.App()
    daframe = DisFrame(None)
    daframe.Show()
    app.MainLoop()

Thanks for the reply Bumsfeld.

Unfortunately your suggestion seems to create another panel on Panel2 (with a button and label), and this modification does not change the text of the label I wish to alter (on the first panel).

Even though I am a novice python user, I feel as though I must be able to call the button-click function of the first button through the button-click event of the second button using a proper syntax call.

I simply don't have a good understanding of how to access python objects and functions between classes.

Aha, now I see it! All you need to do is reference daclick1() properly! Forget the inherited solution.

#!/usr/bin/python


import wx
import time


class MakePanel1(wx.Panel):
    def __init__(self, Parent, *args, **kwargs):
        wx.Panel.__init__(self, Parent, *args, **kwargs)
        
        self.dalabel = wx.StaticText(self, -1, "   panel 1 label   ")
        self.dabutton1 = wx.Button(self, label="button 1") 
        self.dabutton1.Bind(wx.EVT_BUTTON, self.daclick1 )
        self.bs1 = wx.BoxSizer(wx.HORIZONTAL)
        self.bs1.Add(self.dabutton1,0)
        self.bs1.Add(self.dalabel,0)
        self.SetSizer(self.bs1)
        
    def daclick1(self, event):
        self.dalabel.SetLabel(str(time.time()))


class MakePanel2(wx.Panel):
    """Note that MakePanel2 inherits MakePanel1"""
    def __init__(self, Parent, *args, **kwargs):
        wx.Panel.__init__(self, Parent, *args, **kwargs)

        self.dabutton2 = wx.Button(self, label="button 2") 
        self.dabutton2.Bind(wx.EVT_BUTTON, self.daclick2 )
        self.bs2 = wx.BoxSizer(wx.HORIZONTAL)
        self.bs2.Add(self.dabutton2,0,wx.ALL,20)
        self.SetSizer(self.bs2)

    def daclick2(self, event):
        # reference daclick1() properly!!!!!!! 
        daframe.Panel1.daclick1(self)

class DisFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)

        self.Panel1 = MakePanel1(self)
        self.Panel2 = MakePanel2(self)

        bs = wx.BoxSizer(wx.VERTICAL)
        bs.Add(self.Panel1,1,wx.EXPAND);
        bs.Add(self.Panel2,1,wx.EXPAND);

        self.SetSizer(bs)
        self.Fit()


if __name__ == '__main__':
    app = wx.App()
    daframe = DisFrame(None)
    daframe.Show()
    app.MainLoop()

Sorry, was sitting in my favorite internet bistro and got distracted by friends!

Many thanks Bumsfeld !

Your suggestion is perfect.

I was about to post the following solution before seeing your response:

MakePanel1.daclick1(daframe.Panel1,None)

My proposal is definitely less elegant than your solution.

Thanks again.

Right. Python does not allow the use of unbound methods like

MyClass.method()

Instead, it always requires

my_inst = MyClass()
my_inst.method()

The reason I got confused by these things is that we can do things like

import random
random.randint(5)

which appears to be an unbound method. However, it's actually just a function residing in the random module.

Jeff

Thanks Jeff.

Your explanation has helped to remove some confusion I've had about valid calling conventions.

I understand how your approach is required when accessing widgets created in a class - it seems logical that you need to create the widget first [ my_inst = MyClass() ] before attempting to access the widget in any way [ my_inst.method() ].

But what happens when a class consists only of functions without any creation of widgets ?
Does the call my_inst = MyClass() in such a case mean anything ?

Much appreciate your comments.

In the case of:

import random
random.randint(5)

random is a module, and you are using the namespace random. Note that random is not a class!

To find out if an object is a class use something like this:

import inspect
if inspect.isclass(object_name):
    print "object_name is a class"

Generally, Python recommends to start class names with a Capital letter. This disinguishes them from module namespaces.

Here is a case where making an instance of an empty class means something:

class Employee:
    pass


john = Employee() # Create an empty employee record/struct
ted = Employee()

# fill the fields of the record/struct
john.name = 'John Johnson'
john.dept = 'computer lab'
john.salary = 3000

ted.name = 'Ted Tetris'
ted.dept = 'human resources'
ted.salary = 5000

print "%s works in the %s and earns $%s/month" % (john.name, john.dept, john.salary)

In other words, you can create new class variables outside the class!

Thanks Ene Uran. Your post is very informative.


I understand how your approach is required when accessing widgets created in a class - it seems logical that you need to create the widget first [ my_inst = MyClass() ] before attempting to access the widget in any way [ my_inst.method() ].

But what happens when a class consists only of functions without any creation of widgets ?
Does the call my_inst = MyClass() in such a case mean anything ?

Indeed it does. Here's an example:

class Test(object):
    def this(self, arg):
        print "my arg: ", arg

a = Test()

We can now "look" at a as follows:

>>> a
<__main__.Test object at 0x00B83F50>
>>> a.this
<bound method Test.this of <__main__.Test object at 0x00B83F50>>
>>>

So a lives in memory, and a.this does, also. In other words, the object consists of nothing more or less than the 'this' method.

BTW, you can come close to an "unbound" method call like this:

>>> Test().this(4)   # not quite Test.this(4), but close!
my arg: 4

which creates an unassigned instance of Test(), calls its this method with arg = 4, and following the result, garbage-collects the Test() object out of existence. It probably runs slowly for large objects and/or many invocations, but it does work.

Jeff

The fog is slowly lifting.
Thanks to Jeff and all of the previous posters for their insightful comments.

I spoke incorrectly here:


BTW, you can come close to an "unbound" method call like this:

>>> Test().this(4)   # not quite Test.this(4), but close!
my arg: 4

which creates an unassigned instance of Test(), calls its this method with arg = 4, and following the result, garbage-collects the Test() object out of existence. It probably runs slowly for large objects and/or many invocations, but it does work.

While the above *does* work as advertised, I've since discovered that you *can* call unbound methods if they are declared as staticmethods. Here is an example:

class MyClass(object):
    def test(this):
        print this
    test = staticmethod(test)

>>> MyClass.test(4)
4

Sorry to mislead!

Jeff

I am throwing this in for the inquisitive amongst you ...

class Static(object):
    k = 0
    # classmethod decorator allows you to simply call Static.count()
    @classmethod
    def count(self):
        self.k += 1
        return self.k

print Static.count()  # 1
print Static.count()  # 2
print Static.count()  # 3

Thanks to Jeff for the amendment, and to vegaseat.

So there is no need to create an 'instance' of a class before calling any of its functions, as long as a 'classmethod' or 'staticmethod' directive is employed in an appropriate manner.

Very helpful.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.