Method from another class does not work entirely when called - python

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?

Related

Self variable not updated after running function in different class

I have a class (AngleInfo) in a file (Display.py) with a self variable (WheelAngle) which is not updated after running a function (GetAngle). This function is being called in a class in a second file (ManageInfo.py) with a trigger based on events. When I try to use the WheelAngle in a second class (AngleProcess) in Display.py, the value doesn't update from the initialization. When the function is triggered in the MessageHandler class, it has access to raw data being represented by m in the GetAngle declaration.
There is another class (SpeedInfo) in a different file (Radar.py) where the self variable (VehicleSpeed) is being updated after running its corresponding information retrieval function (GetSpeed) in the ManageInfo class.
The working case has a threading system, but after replicating it in the non-working case I found no improvement. I don't understand why the WheelAngle is not being updated inside the class and comparing with the working case hasn't brought me closer to the answer.
So basically after I run GetAngle I see WheelAngle has the correct value inside that function but when I call the self variable in the UpdatePlot function of the AngleProcess class in the Display.py file I get the initial value. I even tried to create a different function in the AngleInfo class to access WheelAngle and then call this function in the UpdatePlot function in the AngleProcess class, but the result is the same.
Keep in mind a working example is not possible since it requires live data being sent. Also, even though WheelAngle and VehSpeed don't seem to be used, the code that follows has been ommited for simplicity!
Any ideas? There is a sample of the code below. Thank you!
Display.py
class AngleInfo():
def __init__(self):
self.WheelAngle = 0
def GetAngle(self,m):
self.WheelAngle = float(m) # Angle is correct
class AngleProcess():
def __init__(self):
self.AngleInfoObj = AngleInfo()
def UpdatePlot(self,tupledata):
WheelAngle = self.AngleInfoObj.WheelAngle # Angle is set to initial
Radar.py
class SpeedInfo(threading.Thread):
def __init__(self,page):
threading.Thread.__init__(self)
self.daemon = True
self.start()
self.VehSpeed = 0
def run(self):
VehSpeed = self.VehSpeed # Speed is correct
def GetSpeed(self,m):
self.VehSpeed = float(m) # Speed is correct
ManageInfo.py
from AurixCAN import Receiver
from Radar import SpeedInfo
from Display import AngleInfo
class MessageHandler:
def __init__(self,page):
self.SpeedInfo = SpeedInfo(page)
self.AngleInfo = AngleInfo()
DataSet = Receiver(canIDsandCalls={0xE:[self.SpeedInfo.GetSpeed,self.AngleInfo.GetAngle]})

How do I best store API data into a Python class instance variable?

I would like some advice on how to best design a class and it's instance variables. I initialize the class with self.name. However, the main purpose of this class it to retrieve data from an API passing self.name as a parameter, and then parsing the data accordingly. I have a class method called fetch_data(self.name) that makes the API request and returns ALL data. I want to store this data into a class instance variable, and then call other methods on that variable. For example, get_emotions(json), get_personality_traits(json), and get_handle(json), all take the same dictionary as a parameter, assign it to their own local variables, and then manipulate it accordingly.
I know I can make fetch_data(self.name) return data, and then call fetch_data(self.name) within the other methods, assign the return value to a local variable, and manipulate that. The problem is then I will need to call the API 5 times rather than 1, which I can't do for time and money reasons.
So, how do I make the result of fetch_data(self.name) global so that all methods within the class have access to the main data object? I know this is traditionally done in an instance variable, but in this scenario I can't initiliaze the data since I don't have it until after I call fetch_data().
Thank you in advance!
It seems like you just need to do something like this:
class Foo(object):
def __init__(self, name):
self.name = name
self.data = None
def fetch_data(self):
if self.data is None:
# Only call the API once
self.data = self.really_fetch_data()
return self.data
def get_emotions(self):
emotions = self.fetch_data().get("emotions")
...
Why don't you just try to solve this as you described?
For example, you can take this as a starting point:
import json
class APIBundle(object):
def __init__(self, name):
self.name = name
self.data = None
self.update()
def update():
response = json.loads(API_request(self.name))
# Do some parsing on response
self.data = response
def get_emotions():
# Work through current data state
# and filter as desired
result = []
for message in self.data['messages']:
if message.find(':)') != -1:
result.append((message, 'positive'))
if message.find(':(') != -1:
result.append((message, 'negative'))
return result
if __name__ == '__main__':
ab = APIBundle('my-secret-name')
print(self.get_emotions())
Try to do it with self.data=None , or make an instance variable and call whenever you need. writing algorithm will make this thing more complex try to solve issue with inbuilt functions or with algorithm program vulnerability will affect alot.

Using callbacks to run function using the current values in a class

I struggled to think of a good title so I'll just explain it here. I'm using Python in Maya, which has some event callback options, so you can do something like on save: run function. I have a user interface class, which I'd like it to update when certain events are triggered, which I can do, but I'm looking for a cleaner way of doing it.
Here is a basic example similar to what I have:
class test(object):
def __init__(self, x=0):
self.x = x
def run_this(self):
print self.x
def display(self):
print 'load user interface'
#Here's the main stuff that used to be just 'test().display()'
try:
callbacks = [callback1, callback2, ...]
except NameError:
pass
else:
for i in callbacks:
try:
OpenMaya.MEventMessage.removeCallback(i)
except RuntimeError:
pass
ui = test(5)
callback1 = OpenMaya.MEventMessage.addEventCallback('SomeEvent', ui.run_this)
callback2 = OpenMaya.MEventMessage.addEventCallback('SomeOtherEvent', ui.run_this)
callback3 = ......
ui.display()
The callback persists until Maya is restarted, but you can remove it using removeCallback if you pass it the value that is returned from addEventCallback. The way I have currently is just check if the variable is set before you set it, which is a lot more messy than the previous one line of test().display()
Would there be a way that I can neatly do it in the function? Something where it'd delete the old one if I ran the test class again or something similar?
There are two ways you might want to try this.
You can an have a persistent object which represents your callback manager, and allow it to hook and unhook itself.
import maya.api.OpenMaya as om
import maya.cmds as cmds
om.MEventMessage.getEventNames()
class CallbackHandler(object):
def __init__(self, cb, fn):
self.callback = cb
self.function = fn
self.id = None
def install(self):
if self.id:
print "callback is currently installed"
return False
self.id = om.MEventMessage.addEventCallback(self.callback, self.function)
return True
def uninstall(self):
if self.id:
om.MEventMessage.removeCallback(self.id)
self.id = None
return True
else:
print "callback not currently installed"
return False
def __del__(self):
self.uninstall()
def test_fn(arg):
print "callback fired 2", arg
cb = CallbackHandler('NameChanged', test_fn)
cb.install()
# callback is active
cb.uninstall()
# callback not active
cb.install()
# callback on again
del(cb) # or cb = None
# callback gone again
In this version you'd store the CallbackHandlers you create for as long as you want the callback to persist and then manually uninstall them or let them fall out of scope when you don't need them any more.
Another option would be to create your own object to represent the callbacks and then add or remove any functions you want it to trigger in your own code. This keeps the management entirely on your side instead of relying on the api, which could be good or bad depending on your needs. You'd have an Event() class which was callable (using __call__() and it would have a list of functions to fire then its' __call__() was invoked by Maya. There's an example of the kind of event handler object you'd want here

How can I retrieve a signal parameter with New-Style Syntax?

Inside a custom button class I have a signal which emits when is dropped something into it. Here the relevant method:
class CustomButton
linked = QtCore.pyqtSignal()
...
def dropEvent(self, e):
print e.source().objectName()
print self.objectName()
# set the drop action as LinkAction
e.setDropAction(QtCore.Qt.LinkAction)
# tell the QDrag we accepted it
e.accept()
#Emit linked signal with the drag object's name as parameter
self.linked.emit( e.source().objectName() )
return QtGui.QPushButton.dropEvent(self, QtGui.QDropEvent(QtCore.QPoint(e.pos().x(), e.pos().y()), e.possibleActions(), e.mimeData(), e.buttons(), e.modifiers()))
In otherhand, outside the class, in the main application I'm creating a slot, and a way to connect it to the signal.
#The slot (actually is just a python callable)
def on_link(self):
input = self.sender().objectName()[4:]
print input
#I need to print the name of the other object emitted as str parameter in the signal....
#Instance of custom button
custom_button.linked.connect( lambda: on_link( custom_button ) )
At this point I already know that I can get the sender() of the signal, however, I don't know how to get the parameter of self.linked.emit( e.source().objectName() ). I just know that first I have to change first this: linked = QtCore.pyqtSignal(str), but don't know how to write the connection or the slot and retrieve the e.source().objectName() in the emit signal.
The current design of the slot looks very confusing. At first glance, it looks like an instance method, but it is actually just a module-level function with a fake self parameter.
I would suggest something simpler, and more explicit, like this:
class CustomButton(QtGui.QPushButton):
linked = QtCore.pyqtSignal(str, str)
def dropEvent(self, event):
...
self.linked.emit(self.objectName(), event.source().objectName())
return QtGui.QPushButton.dropEvent(self, event)
def on_link(btn_name, src_name):
print btn_name, src_name
custom_button.linked.connect(on_link)
An alternative design would be to send the objects, instead of their names:
linked = QtCore.pyqtSignal(object, object)
...
self.linked.emit(self, event.source())
def on_link(button, source):
print button.objectName(), source.objectName()

Python Attribute Error with multiple classes and Tkinter

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.

Categories

Resources