Need help with Classes in Python - python

I'm making a program that imports a custom widget that is a class (it inherits from the Tkinter Frame widget). It all works great until I get to binding. To reduce confusion we'll call the main application app, the module it's importing the widget from lib, and the widget that's being imported into app will be called cwid.
Basically I need to somehow reference a function in app, so that it may be bound to my widget in lib.
The function I'm trying to bind a widget within cwid to is element_click (The function element_click is in app.):
lambda event: element_click(event, elementinfo[3])
So the binding would look something like this in lib (element is a canvas widget within cwid)
element.bind('<ButtonRelease-1>', lambda event: element_click(event, elementinfo[3]))
The above line wont work however seeing as element_click is in app. So I tried a work around which doesn't seem to be working.
import app
loc = app.EOG
element.bind('<ButtonRelease-1>', lambda event: loc.element_click( event, elementinfo[3]))
When I try the above I get the following error:
TypeError: unbound method element_click() must be called with EOG instance as first argument (got Event instance instead)
EOG is a class in app which contains element_click.
Also, all of the above bits of code are snippets.
EDIT:
Tried loc = app.EOG(), and go the following error:
AttributeError: EOG instance has no attribute '__nonzero__'

I think you just want:
loc = app.EOG()
Then, loc is an instance of EOG, and loc.element_click is a bound method, so it works as intended.

Related

Calling function in different class in Tkinter GUI (Python 3.4)

I have a tkinter GUI that I structured based on Bryan Oakley's post here. I have a container class and some other classes. I have a button in my navigation class that is trying to call a function in the container class. For some reason I cant get self.parent... to work and have to type in Container... to get the function to work. The first line of code below works but the second line gets the error you see below.
self.Clear.bind("<Button-1>", lambda event: self.parent.combine_funcs(self.parent.ChangeRange(F_Clear), Container.UpdateToolBar(self, F_Clear)))
self.Clear.bind("<Button-3>", lambda event: self.parent.combine_funcs(self.parent.ChangeRange(F_Clear), self.parent.UpdateToolBar(self, F_Clear)))
TypeError: UpdateToolBar() takes 2 positional arguments but 3 were given
It only has a problem with the UpdateToolBar def...
def UpdateToolBar(self, file):
self.parent.ToolBar1.itemconfigure('toolbar', text=('CalcTime: '+str(self.parent.total)[:5]))
If I change the code to:
self.parent.UpdateToolBar(F_Clear)))
I get:
AttributeError: 'Container' object has no attribute 'parent'
I'm hoping the answer to this will give me more insight into interacting with code in different classes.

Binding MouseClick to widget of an instance

I have an instance of a ttkcalendar object, "cal". When I bind a button click to cal, the function called only executes if I click in the corners of the ttcalendar frame; when I click on the actual calendar area, while the ttkcalendar functions execute, my bind does not.
This code runs when I click Frame corners
# Calendar Frame
self.cal=Calendar(LeftFrame)
self.cal.pack(side=TOP)
self.cal.bind("<Button-1>",self.clicked)
I thought that if I try to bind to the canvas object of the calendar it would work. However this code returns AttributeError: Calendar instance has no attribute canvas.
# Calendar Frame
self.cal=Calendar(LeftFrame)
self.cal.pack(side=TOP)
self.cal.canvas.bind("<Button-1>",self.clicked)
As I said, internal ttkcalendar binds work fine for switching date shown. Any insights? Thanks
The error message should be pretty clear. If python is telling you "AttributeError: Calendar instance has no attribute canvas", you must assume that to be true.
Looking at the source code of the Calendar class, I don't see any canvas attribute. Just like the error is telling you, you're trying to access an attribute that doesn't exist.
The Calendar class does have an attribute named _canvas, maybe you can try using that instead. Though, that leading underscore denotes that it is intended as a private attribute and may go away in future revisions of the code.

PyQt Runtime Error of QTreeWidgetItem

I'm trying to avoid the well-known PyQt Runtime Error when the underlying C/C++ object is deleted:
http://www.riverbankcomputing.com/pipermail/pyqt/2009-April/022809.html
PyQt4 - "RuntimeError: underlying C/C object has been deleted"
PyQt4 nested classes - "RuntimeError: underlying C/C++ object has been deleted"
PyQt: RuntimeError: wrapped C/C++ object has been deleted
Every one of my subclasses calls the super() method and therefore the base classes are properly constructed.
Still, I get this error and am wondering if it is due to the fact that I'm adding a QComboBox widget to a QTreeWidgetItem (using the setItemWidget() method of a QTreeWidget) but I cannot set the parent as the QTreeWidgetItem that contains it. When I try, I get the following error:
TypeError: QComboBox(QWidget parent=None): argument 1 has unexpected type 'QTreeWidgetItem'
Of course, I can either omit the parent in the constructor or pass the QTreeWidget as the parent, but I think I need to reference the correct parent.
I have subclassed the QComboBox and in my subclass it runs some basic operations on the QTreeWidget, but as soon as I enter the methods of my subclassed QComboBox, the underlying C object for the parent QTreeWidgetItem containing the QComboBox is deleted (which is why I'm thinking its something to do with setting the parent of the QComboBox).
I understand 9 times out of 10 the runtime error is due to not constructing the base class. But with that ruled out, how else can the error occur? Could it be due to not referencing the correct parent?
EDIT
I'm using the QComboBox to signal when a new combobox selection was made. Upon a new selection, it adds that selected value to a PyXB XML node. Interestingly, this issue only occurs if I append the value to the PyXB class binding storing the information permanently in an XML file. In otherwords, if that part of the code doesn't run I dont get the error - its only when the code runs the PyXB operation for appending a value to an XML node binding...
I usually avoid that kind of errors keeping a reference on my class to all the objects susceptible of being deleted like your QComboBox so try something like self.comboBoxHolder = QComboBox(...) when you create it.

How to refer to a base instance python gtk widget from a function in a module

I am writing a Python GTK application for studying some sort of math data. The main script has a single class with only three methods: __INIT__, main(self) for starting the loop and delete_event for killing it.
__INIT__ creates the GUI, which includes a TextBuffer and TextView widgets so that the analysis functions (defined on a separate functions.py module) can output their results to a common log/message area. A relevant extract follows:
include module functions(.py)
(...)
class TURING:
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
(...)
self.logscroll = gtk.ScrolledWindow()
self.logscroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
self.logbuffer = gtk.TextBuffer()
self.logpage = gtk.TextView(self.logbuffer)
self.logpage.set_editable(gtk.FALSE)
self.logpage.set_cursor_visible(gtk.FALSE)
self.logpage.set_wrap_mode(gtk.WRAP_CHAR)
self.logscroll.add(self.logpage)
self.logscroll.show()
self.logpage.show()
(...)
enditer = self.logbuffer.get_end_iter()
self.logbuffer.insert(enditer, 'Welcome!')
(...)
def main(self):
gtk.main()
if __name__ == "__main__":
turing = TURING()
turing.main()
The intermediate two lines successfully print a welcome message onto the message area defined by self.logpage.
Now, one of the functions in method functions checks whether the database is up to date and if not asks the user to load a new batch of raw data.
One way of doing this is to include a menu item that triggers that function, like this:
item_dataCheck.connect("activate", functions.data_check, '')
functions.data_check runs fine however when it tries to write its output to self.logbuffer an error is thrown complaining that menu item item_dataCheck has no property logbuffer. The offending code is
enditer = self.logbuffer.get_end_iter()
self.logbuffer.insert(enditer, 'Please update the database.')
Obviously the name self is representing the widget that invoked the function, viz., item_dataCheck. My question is how can I from functions.data_check refer directly to logbuffer as a member of the turing instance of the TURING class. I tried to write
enditer = turing.logbuffer.get_end_iter()
turing.logbuffer.insert(enditer, 'Please update the database.')
but that's is not working. I have tried hard to find a solution but with no success.
I believe the matter is quite trivial and I know I still have some serious conceptual problems with Python and OOP, but any help will be heartly appreciated. (I started out card punching Fortran programs on a Burroughs mainframe, so I could count on some community mercy...)
You can provide additional arguments when connecting signals to functions or methods. So your handler functions.data_check can accept extra arguments apart from self:
def data_check(self, logbuffer):
# ...
Then, you can connect with arguments:
item_dataCheck.connect("activate", functions.data_check, logbuffer)
Also, the self parameter is normally used as the first parameter in method definitions. This is a very strong convention so you should use obj or something similar instead. Specially since your signal handlers may be methods too; in which case you could mess it up with its arguments.

How to get all child components of QWidget in pyside/pyqt/qt?

I am developing a desktop application using pyside(qt), I want to access(iterate) all line edit components of QWidget. In qt I found two methods findChild and findChildren but there is no proper example found and My code shows error, 'form' object has no attribute 'findChild'.
Here 'form' is Qwidget form consist components lineEdit, comboboxes, Qpushbuttons etc.
Code:
lineEdits = form.findChild<QLineEdit>() //This is not working
lineEdits = form.findChild('QLineEdit) //This also not working
The signatures of findChild and findChildren are different in PySide/PyQt4 because there is no real equivalent to the C++ cast syntax in Python.
Instead, you have to pass a type (or tuple of types) as the first argument, and an optional string as the second argument (for matching the objectName).
So your example should look something like this:
lineEdits = form.findChildren(QtGui.QLineEdit)
Note that findChild and findChildren are methods of QObject - so if your form does not have them, it cannot be a QWidget (because all widgets inherit QObject).
Use this method QObject::findChildren(onst QString & name = QString()) with no parameters.
Omitting the name argument causes all object names to be matched.
Here is C++ example code:
QList<QLineEdit*> line_edits = form.findChildren<QLineEdit*>();

Categories