Python Attribute Error with multiple classes and Tkinter - python

I'm trying create a gui using Tkinter that grabs a username and password and connects to a remote server and does a function. I slapped together some messy code and it more or less worked, but when I tried to recreate it in a tidy module, it broke. Its probably a newbie python error, but I can't spot it. EDIT: to clarify, when it worked, the only class was setupGui and any methods were under that class. Now that I've separated the gui from the methods, its not working.
class setupGui(object):
def __init__(self, parent):
##omited general frame stuff
self.userIn = ttk.Entry(self.topFrame, width = 20)
self.userIn.grid(row = 1, column = 1)
self.passIn = ttk.Entry(self.topFrame, width = 20, show ="*")
self.passIn.grid(row = 2, column = 1)
#Buttons
self.setupbtn = ttk.Button(self.topFrame, text = "Start Setup", command = setup().startSetup())
self.setupbtn.grid(row = 3, column = 0, pady = 10)
class setup(object):
def__init__(self):
self.userName = setupGui.userIn.get()
self.userPass = setupGui.passIn.get()
def startSetup(self):
self.another_related_fucntion # about 4 related functions actually
if __name__ == '__main__':
root = Tk()
gui = setupGui(root)
root.mainloop()
And if I don't have the command attached to the button, everything works fine (but obviously does diddly squat except look pretty). And when I attached the command, I get this error:
Traceback (most recent call last):
File "macSetup.py", line 211, in <module>
gui = setupGui(root)
File "macSetup.py", line 45, in __init__
self.setupbtn = ttk.Button(self.topFrame, text = "Start Setup", command = setup().startSetup())
File "macSetup.py", line 69, in __init__
self.userName = setupGui.userIn.get()
AttributeError: type object 'setupGui' has no attribute 'userIn'

In your code, userIn is set up as an instance variable of setupGui objects, not as an attribute of the setupGui class itself.
The simplest solution would be to merge the setupGui and setup classes to move startSetup in as a method of setupGui, then use command=self.startSetup when you initialize setupbtn—this calls startSetup as a bound method, and self should thus refer to the setupGui object, which you can then use e.g. self.userIn.get() and self.passIn.get() on.
If you'd rather keep the logic you have in the setup class out of the setupGui class, you can separate it out like this:
class setup(object):
def __init__(self, username, userpass):
self.userName = username
self.userPass = userpass
def startSetup(self):
# as before
then add this method to the setupGui class:
def dosetup(self):
setup(self.userIn.get(), self.passIn.get()).startSetup()
and instantiate the Button with command=self.dosetup. (I would personally make the setup class a standalone function, but I don't know how complicated your startSetup routine actually is, so I assume you have a good reason for making it a class.)

The command attribute takes a reference to a function, but you're calling the function and giving the result to the command attribute. The net result is that you're calling the setup function at the time that you create the button, not at the time that you click the button. Things aren't fully initialized yet, so you get the error.
You're doing this:
self.setupbtn = ttk.Button(self.topFrame, text = "Start Setup", command = setup().startSetup())
... when you should be doing something like this:
self.setupbtn = ttk.Button(self.topFrame, text = "Start Setup", command = setup().startSetup)
Note the lack of the trailing () on startSetup.
If you don't want to instantiate setup until the button is clicked, you have a couple of choices. The best, arguably, is to create a method:
def _launch_setup(self):
setup().setupGui()
...
self.setupbtn = ttk.Button(..., command=self._launch_setup)
You could also use a lambda, but in this case I recommend a named method.

The class setupGui itself doesn't have the attribute userIn.
In the __init__ method of setupGui you give the attribute to the instance, not the class.

Related

Calling a method with another method within another class (calling Method_B within Class_B, with Method_A within Class_A)

I am a Maya user and I am currently writting an Auto-Rig.
I created different Classes for each major tasks of the tool. (ex: Class_UI, Class_Arms_Rig, etc..)
The problem I have is that I can't call a method from "Class_Joints" (the class that will generates every needed Joints) with my "Class_UI"
Here are the codes :
First the Class_UI
import sys
sys.path.append('G:\\3D2\\Script\\Auto_Rig')
import Class_Joints
import Class_Arms
import maya.cmds as mc
class Window_UI(object):
# Initializing global variables
def __init__(self):
# Getting acces to the different modules
self.Arms = Class_Arms.Arms_Rig()
self.Joints = Class_Joints.Gen_Joints()
# Create Ui
self.create_UI()
# Creating the UI
def create_UI(self):
# Create window
self.UI = mc.window(title='Auto-Rig Tool', w=(300), h=(350))
# Main layout
self.mainLayout = mc.menuBarLayout()
### Joints Option ###
# Create Joints Button
self.createJointsButton = mc.button(label='Create Joints', command=self.Joints.gen_arms_joints)
Window_UI()
mc.showWindow()
Then the Class_Joints :
import maya.cmds as mc
class Gen_Joints:
# Creating arm Jnts and the list of it
def gen_arms_joints(self):
self.shoulderJnt = mc.joint(absolute=True, position=[5,8,0], n='L_Shoulder_Jnt')
self.elbowJnt = mc.joint(absolute=True, position=[10,8,-1.5], n='L_Elbow_Jnt')
self.wristJnt = mc.joint(absolute=True, position=[15,8,0], n='L_Wrist_Jnt')
self.handcupJnt = mc.joint(absolute=True, position=[18,8,0], n='L_HandCup_Jnt')
self.jntList = mc.ls(self.shoulderJnt, self.elbowJnt, self.wristJnt, self.handcupJnt)
When I run the Class_UI Code, the button within the UI is supposed to run the gen_arms_joints method within the Class_Joints
But I get this error message : # Error: gen_arms_joints() takes exactly 1 argument (2 given) #
I know that self is an implicit argument here but I do not know how to avoid this error.
Thank you all for your time.
:D
Cordially, Luca.
Two things i would recommend you do. I dont use Maya but i have built apps with multiple different GUIs.
Every GUI I've used when it comes to buttons is the first argument is a reference to self, and then there is usually 1 or 2 more arguments passed in. Some pass the reference to the button itself while others pass a argument that holds event details. My guess is this is what is happening. When you click the button it is passing in an "event" object that hold details about what was clicked and other details.
To truly find out what is passed change your function signature to this and see what is logged.
def gen_arms_joints(self, mystery_second_arg):
print(type(mystery_second_arg), mystery_second_arg)
self.shoulderJnt = mc.joint(absolute=True, position=[5,8,0], n='L_Shoulder_Jnt')
self.elbowJnt = mc.joint(absolute=True, position=[10,8,-1.5], n='L_Elbow_Jnt')
self.wristJnt = mc.joint(absolute=True, position=[15,8,0], n='L_Wrist_Jnt')
self.handcupJnt = mc.joint(absolute=True, position=[18,8,0], n='L_HandCup_Jnt')
self.jntList = mc.ls(self.shoulderJnt, self.elbowJnt, self.wristJnt, self.handcupJnt)
The issue is that in the __init__() call of the Class_UI, you have defined the wrong class call for the actual function gen_arm_joints(self), it should be: self.Joints = Gen_Joints(), seems like you have different import classNames but in the code you have called the class as Gen_Joints. You can't pass in two self classes references i.e the error traceback.
You will have to fix the import class to import Gen_Joints.
#FishingCode
Look out what I tried based on what you told me :
import sys
sys.path.append('G:\\3D2\\Script\\Auto_Rig')
import Class_Joints
import Class_Arms
import maya.cmds as mc
class Window_UI(object):
# Initializing global variables
def __init__(self):
# Getting acces to the different modules
self.Arms = Class_Arms.Arms_Rig()
self.Joints = Gen_Joints()
# Create Ui
self.create_UI()
# Creating the UI
def create_UI(self):
# Create window
self.UI = mc.window(title='Auto-Rig Tool', w=(300), h=(350))
# Main layout
self.mainLayout = mc.menuBarLayout()
# Create Joints Button
self.createJointsButton = mc.button(label='Create Joints', command=self.Joints.gen_arms_joints)
#show window
Window_UI()
mc.showWindow()
It's exactly what I have on Visual Studio Code
import maya.cmds as mc
class Gen_Joints:
# Creating arm Jnts and the list of it
def gen_arms_joints(self):
self.shoulderJnt = mc.joint(absolute=True, position=[5,8,0], n='L_Shoulder_Jnt')
self.elbowJnt = mc.joint(absolute=True, position=[10,8,-1.5], n='L_Elbow_Jnt')
self.wristJnt = mc.joint(absolute=True, position=[15,8,0], n='L_Wrist_Jnt')
self.handcupJnt = mc.joint(absolute=True, position=[18,8,0], n='L_HandCup_Jnt')
self.jntList = mc.ls(self.shoulderJnt, self.elbowJnt, self.wristJnt, self.handcupJnt)
VS Code tells me Undefined variable 'Gen_Joints'
And within maya I get # Error: NameError: file <maya console> line 16: global name 'Gen_Joints' is not defined #
mc.button passes on an extra boolean value to the method, so since that's not defined in gen_arms_joints it throws that error and says it's the wrong number of arguments.
So simply add an extra parameter and it'll fix it:
def gen_arms_joints(self, extra_param):
Or better yet use *args instead to be a bit more generic:
def gen_arms_joints(self, *args):
It's honestly kind of sneaky because it's not really stated in the documentation, but you can see it being used in the example at the bottom of the page.

Getters in python, tkinter

I need access to information from my class "makeEntry" precisely textvariables. I tried make get function but i read that in python it's not necessary.
def temp():
print(e_full_name.get_text())
class makeEnetry:
def __init__(self, i_parent, i_width, i_row, i_column, i_text):
test.set(i_text)
test = StringVar()
entry = Entry(master = i_parent, width = i_width, textvariable = test)
entry.grid(row = i_row, column = i_column, padx = 5, pady =5 )
def get_text(self):
return self.test.get()
I tried to move my test = StringVar() above function, just in class to refer to it in another function but it doesn't work. I'm newbie in python and I have no idea what I have done wrong.
def temp():
print(e_full_name.get_text())
class makeEnetry:
test = StringVar()
def __init__(self, i_parent, i_width, i_row, i_column, i_text):
test.set(i_text)
.
.
.
I have written a working solution. As I mentioned in my comment, the test variable should be an instance variable (self). I have added several comments to my example code for the better understanding.
Code:
import tkinter as tk
def temp():
print(entry_inst.get_text()) # Printing the result of "get_text" method of "entry_inst" object.
root = tk.Tk()
class makeEnetry:
def __init__(self, i_parent, i_width, i_row, i_column, i_text):
self.test = tk.StringVar()
self.test.set(i_text)
entry = tk.Entry(master=i_parent, width=i_width, textvariable=self.test)
entry.grid(row=i_row, column=i_column, padx=5, pady=5)
def get_text(self):
return self.test.get()
entry_inst = makeEnetry(root, 20, 1, 1, "TEST") # Creating an object from "makeEnetry" class
button = tk.Button(root, text="PUSH ME", command=temp) # Creating a button which will call the "temp" function
button.grid(row=2, column=1)
tk.mainloop()
GUI:
Console output:
FYI: I have changed the text in the entry box and clicked to button.
>>> python3 test.py
TEST
TEST1
TEST12
TEST123
Any variable that you want external functions to have access to need to be a instance variable.
For example, if you do self.test = StringVar(), and use self.test everywhere inside the class instead of just test, then any code that has a reference to the instance of the class can access the variable.
the_entry = makeEnetry(root, 20, 1, 1, "Hello, world!")
...
print(the_entry.test.get())
However, there's nothing wrong with creating a getter or setter. For example, StringVar has a getter (.get()) and setter (.set(...)). The advantage of creating getters and setters is that it separates the internal implementation from the external interface. With a getter or setter, you can hide the fact that you're using a StringVar, the caller doesn't need to know.
The thing with functions, instance variables, and with getters and setters, is that you are creating a user interface or API. You are creating an object and then telling the outside world "this is how you interact with this object". Sometimes getters and setters make sense, sometimes they don't. It depends on what you want the object UI/API to be, and whether or not you want to expose internal implementation details to the outside world.
In this specific case it seems like the_entry.get_text() is a cleaner interface than the_entry.test.get(...) since it gives you more freedom in changing the internal implementation without affecting code that uses this object. It all boils down to how you want external code to interact with the object you've created. Should the caller know that this object uses a StringVar, or should it just know that it can call get_text to interact with it?

Method from another class does not work entirely when called

I have this code:
class Matplotlib_figure(QMainWindow):
minimumCoords = None
maximumCoords = None
initial_marker = None
final_marker = None
limite = None
def __init__(self):
#A lot of stuff to draw a matplotlib figure
def minimumLimit(self):
self.cMinL = self.figure_canvas.mpl_connect("button_press_event", self.select_minimumLimit)
self.limite = "minimum"
def select_minimumLimit(self, event):
if event.button == 1:
self.clearMarker() #This is another method that i call
Matplotlib_figure.minimumCoords = None
Matplotlib_figure.minimumCoords = event.xdata
if Matplotlib_figure.minimumCoords <= Matplotlib_figure.maximumCoords or Matplotlib_figure.maximumCoords == None:
marker = self.axes.axvline(event.xdata,0,1, linestyle='dashed',
linewidth = 2, color = "green" )
self.figure_canvas.draw_idle()
Matplotlib_figure.initial_marker = marker
class Data(QDialog):
minimum = None
maximum = None
def __init__(self, parent):
QDialog.__init__(self, None, QWindowsStayOnTopHint)
uic.loadUi("", self)
def show_lines(self):
SelectData.minimo = self.lineEdit.text()
SelectData.maximo = self.lineEdit_2.text()
Matplotlib_figure.minimumCoords = float(SelectData.minimo)
Matplotlib_figure.maximumCoords = float(SelectData.maximo)
#Here is where i want to call a method in the Matplotlib_figure class
view = Matplotlib_figure()
view.minimumLimit()
view.maximumLimit()
The problem comes in the Data class. When i want to call the minimumLimitmethod in the Matplotlib_figureclass (from show_lines in Data class), it does not trigger the figure_canvas.mpl_connectmethod, and select_minimumLimitmethod does not work.
What am i doing wrong? Hope you can help me.
I think the key issue comes from this note in the matplotlib event handling docs:
The canvas retains only weak references to the callbacks. Therefore if a callback is a method of a class instance, you need to retain a reference to that instance. Otherwise the instance will be garbage-collected and the callback will vanish.
So you have created a new view in the show_lines method, but this is a local variable. When the function returns, the variable goes out of scope and python will probably try to delete it. Normally if you save a reference to a method off, then the method retains the object it is a method for, and this would not happen, but because mpl_connect only takes a weak reference to the function it does not retain view, and therefore when the show_lines returns, the method is lost too, and so the callback will revert to doing nothing.
You can probably fix this by rewriting show_lines to save the view off, something like:
def show_lines(self):
SelectData.minimo = self.lineEdit.text()
SelectData.maximo = self.lineEdit_2.text()
Matplotlib_figure.minimumCoords = float(SelectData.minimo)
Matplotlib_figure.maximumCoords = float(SelectData.maximo)
#Here is where i want to call a method in the Matplotlib_figure class
self.view = Matplotlib_figure()
self.view.minimumLimit()
self.view.maximumLimit()
Now the Matplotlib_figure instance will be retained as long as the Data instance is.
[Previous answer based on error in question kept below]
I don't know the QT framework or the matplotlib APIs very well, but it looks to me that you've created an instance of ViewWidget, which is an entirely separate class (subclass of the QT QMainWindow class, if I recognise that, which is an entirely different python module) from Matplotlib_figure. Therefore I would expect when you call minimumLimit() that you'd get an AttributeError exception, and I wouldn't expect it to call your method. If you want that you'll have to create an instance of it and call that:
view = Matplotlib_figure()
view.minimumLimit()
view.maximumLimit()
Without more context of where your ViewWidget comes from it is hard to understand how you think this should work. It's also a bit odd that you are creating a matplotlib figure that is a subclass of the unrelated QMainWindow class. What are you trying to achieve with this? Could you provide some more context for the code?

Gio SimpleAction to call a function

I have made menu using Gio action in a Gtk3 app.
The menu item is created as:
#in main file
MenuElem = menu.MenuManager
# Open Menu
action = Gio.SimpleAction(name="open")
action.connect("activate", MenuElem.file_open_clicked)
self.add_action(action)
The file_open_clicked is in menu.py, class MenuManager, defined as:
import gi
import pybib
import view
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
class MenuManager:
def __init__(self):
self.parsing = pybib.parser()
self.TreeView = view.treeview()
#file_open_clicked
#in menu.py
def file_open_clicked(self, widget):
dialog = Gtk.FileChooserDialog("Open an existing fine", None,
Gtk.FileChooserAction.OPEN,
(Gtk.STOCK_CANCEL,
Gtk.ResponseType.CANCEL,
Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
response = dialog.run()
if response == Gtk.ResponseType.OK:
filename = dialog.get_filename()
dialog.destroy()
self.TreeView.bookstore.clear()
self.TreeView.viewer(self.parsing.booklist)
# self.TreeView.view.set_model()
elif response == Gtk.ResponseType.CANCEL:
print("Cancel clicked")
dialog.destroy()
I am getting error:
Traceback (most recent call last):
File "/home/rudra/Devel/mkbib/Python/src/menu.py", line 81, in file_open_clicked
self.TreeView.bookstore.clear()
AttributeError: 'SimpleAction' object has no attribute 'TreeView'
I know SimpleAction takes one more option, and TreeView should be called.
But I dont know how.
Kindly help
Let me break down your code for you.
#in main file
MenuElem = menu.MenuManager
Here you set MenuElem to point to menu.MenuManager class. Probably you meant to initialize the object here such that MenuElem become an instance of the menu.MenuManagerclass. Such that the __init__ function of the MenuManager class was called. Thus the code should be:
#in main file
MenuElem = menu.MenuManager()
Then the next part where something goes wrong is in here:
def file_open_clicked(self, widget):
If we check the docs for the activate signal we see that it has 2 parameters. So currently without initializing the object self is set to the first parameter namely the SimpleAction and the widget is set to the activation parameter.
But as we now have initialized the MenuManager object, the file_open_clicked function will get 3 input parameters namely self, SimpleAction and parameter. Thus we need to accept them all like this:
def file_open_clicked(self, simpleAction, parameter):
Now the code will work as self is actually an object with the attribute TreeView. (Just for your information in Python variables and attributes are normally written in lowercase)
Your problem is that the TreeView attribute only exists on the MenuManager class, whereas, when you call the file_open_clicked method, the first argument (self) is the SimpleAction object created. Using the file_open_clicked method of a MenuManager instance would fix this.
menu_manager = MenuManager()
action = Gio.SimpleAction(name="open")
action.connect("activate", menu_manager.file_open_clicked)

api: how to get selected text from object sublime.Selection

How to get selected text in sublime text 3 plugin:
import sublime, sublime_plugin
class plugin_window__go_to_relative_plugin__Command(sublime_plugin.WindowCommand):
def run(self):
window = self.window
view = window.active_view()
sel = view.sel()
sublime.status_message("selection: "+sel)
My code throws error:
sublime.status_message("selection: "+sel)
TypeError: Can't convert 'Selection' object to str implicitly
view.sel() returns sublime.Selection object. But I don't know how to get selected text from there.
This plugin must work as following:
When I call it on view...
... it should set text "dow = self.w" to variable sel
When I do str(sel) it returns <sublime.Selection object at 0x1047fd8d0>
Docs are not very clear for me.
My understanding of what the documentation means is this:
It sounds like the sel() method of a sublime.View object returns a sublime.Selection object, which is a container of regions—so you should be able to iterate over its contents (the regions it contains) or index into them using the [] operation.
You can get the text associated with each sublime.Region in a Selectionby calling the substr(region) method of a sublime.View object. This makes sense as this editor allows there to be multiple simultaneous selections—one of its cooler features, IMHO.
Hope this helps.
In case of single selection:
import sublime, sublime_plugin
class selection_plugin__Command(sublime_plugin.WindowCommand):
def run(self):
print('selection_plugin__ called')
window = self.window
view = window.active_view()
sel = view.sel()
region1 = sel[0]
selectionText = view.substr(region1)
print(selectionText)
Use it in console and then append in python
view.substr((view.sel())[0])

Categories

Resources