Removing Item from Scene unsuccessful and causes error - python

Removing a QGraphicsPathItem from a QGraphicsScene raises "QGraphicsScene::removeItem: item 0x204f6a20880's scene (0x0) is different from this scene (0x204f7e519c0)".
I also printed the .scene() method of the item and printed the scene, both of which are the same - "<examples.example_calculator.nodes.operations.GraphScene object at 0x00000204F79E5C10>".
So I do not understand how the scene I am deleting the item from, is not the same as the one that the item is in, which is proved true by my two print statements, however the error still comes up.
Here is the code:
class GraphCurve(QGraphicsView):
def __init__(self, scene):
super().__init__()
self.scene = scene
self.setScene(scene)
self.setSceneRect(0,0,self.scene.sx, self.scene.sy)
self.pntrad = 10
self.selected=None
self.cpoints=[]
self.ccpoints=[]
self.points = []
self.paths = []
######WHERE THE ERROR COMES UP######
def updateCurve(self, mvpnt):
try:
x = self.cpoints.index(mvpnt)
mvpnt = self.points[x]
except:
try:
x = self.ccpoints.index(mvpnt)
mvpnt = self.points[x]
except:
pass
try:
ind = self.points.index(mvpnt)
######WHERE THE ERROR COMES UP##########################################################
print(str(self.scene)) #####MARK THIS AS A#####
print(str(self.paths[ind-1].scene())) #####SAME AS A#####
self.scene.removeItem(self.paths[ind-1])
except:
pass
try:
######WHERE THE ERROR COMES UP##########################################################
self.scene.removeItem(self.paths[ind])
except:
pass
points = [self.points[ind], self.points[ind-1]]
for x in points:
try:
drawpath = QPainterPath()
drawpath.moveTo(QPointF(x.pos().x()+self.pntrad/2, x.pos().y()+self.pntrad/2))
drawpath.cubicTo(QPointF(self.ccpoints[self.points.index(x)].pos().x()+(self.pntrad/2), self.ccpoints[self.points.index(x)].pos().y()+(self.pntrad/2)),
QPointF(self.cpoints[self.points.index(x)+1].pos().x()+(self.pntrad/2), self.cpoints[self.points.index(x)+1].pos().y()+(self.pntrad/2)),
QPointF(self.points[self.points.index(x)+1].pos().x()+(self.pntrad/2), self.points[self.points.index(x)+1].pos().y()+(self.pntrad/2)))
######WHERE THE ITEM IS CREATED#####################################################
path = self.scene.addPath(drawpath)
print(str(path.scene())) #####THIS IS SAME AS A#####
self.paths.append(path)
except:
pass

Related

Python class scope lost in nested recursion function

When I attempted to write a recursive nested function within a python class, every time the recursive function completed and returned back to the first function my class property reverted back to the original state.
def main():
input = [
[1,3,1],
[1,5,1],
[4,2,1]
]
sln = Solution()
sln.findPath(input)
print("Output: " + str(sln.minPathSum))
print(*sln.minPath, sep = "->")
class Solution():
minPath = []
minPathSum = None
grid = []
def findPath(self, grid):
self.minPath = []
self.minPathSum = None
self.grid = grid
self.recurse(0,0,[])
def recurse(self, xCoord, yCoord, currentPath):
if(len(self.grid) <= yCoord):
return
if(len(self.grid[yCoord]) <= xCoord):
return
currentValue = self.grid[yCoord][xCoord]
currentPath.append(currentValue)
if(len(self.grid) - 1 == yCoord and len(self.grid[yCoord]) - 1 == xCoord):
currentPathSum = sum(currentPath)
if(self.minPathSum == None or currentPathSum < self.minPathSum):
self.minPathSum = currentPathSum
self.minPath = currentPath
else:
#right
self.recurse(xCoord + 1, yCoord, currentPath)
#down
self.recurse(xCoord, yCoord + 1, currentPath)
currentPath.pop()
return
if __name__ == "__main__":
main()
Running this results in:
Output: 7
Debugging the code within VSCode does indicate that self.minPath is getting set within the recurse function; however, it appears to be losing scope to the original class instance.
In addition I attempted to recreate the same nested situation with separate code and ended up with the following.
def main():
tst = ScopeTest()
tst.run()
print(*tst.tstArray)
print(tst.tstNumber)
class ScopeTest():
tstArray = []
tstNumber = 0
def run(self):
self.otherFunc()
def otherFunc(self):
self.tstArray = [1,2,3,4,5]
self.tstNumber = 7
if __name__ == "__main__":
main()
The above did return the expected outcome which leads me to think this has something to do with the recursion.
Just a heads up, I'm fairly new to python so I may be making a rookie mistake, but I can't seem to figure out why this is happening.
Your recurse() method is generating a currentPath parameter and when this is deemed to be correct you execute: self.minPath = currentPath. Unfortunately, you just reference the same object as currentPath which is later mutated.
Your line should be: self.minPath = currentPath[:]
You then see some contents in self.minPath
Mandatory link to Ned Batchelder
Also you can remove these lines:
minPath = []
minPathSum = None
grid = []
from just below class Solution():

Program glitches out after a few seconds (tkinter)

I am writing a program that should display some data that's going over a CanBus (data is being refreshed every 200ms), however when I make an objects from my class like this:
batteryDegree = PlaceInfo(root,img='battery.png',fotox=0.23,fotoy=0.35,foto_y_resize=180,foto_x_resize=180,anchor='center', text="100*C",textx=0.23,texty=0.38,canid=0x1000400,byte=2,size=30)
motorDegree = PlaceInfo(root,img='motor.png',fotox=0.75,fotoy=0.35,foto_y_resize=200,foto_x_resize=200,anchor='center', text="100*C",textx=0.76,texty=0.37,canid=0x1000400,byte=0,size=30)
Here is my class:
bus = can.interface.Bus(channel='can0')
root = Tk()
root.geometry("800x480")
root.config(bg='red')
canvas = Canvas(root,width=800,height=480,highlightthickness=0)
canvas.pack()
class PlaceInfo():
def __init__(self,root, **kwargs):
self.root = root
if 'img' in kwargs:
if 'foto_x_resize' in kwargs and 'foto_y_resize' in kwargs:
self.png = ImageTk.PhotoImage(Image.open('imgs/' + kwargs['img']).resize((kwargs['foto_x_resize'],kwargs['foto_y_resize']),Image.ANTIALIAS))
else:
self.png = ImageTk.PhotoImage(Image.open('imgs/' + kwargs['img']))
self.image = canvas.create_image(800 * kwargs['fotox'],480 * kwargs['fotoy'] ,image=self.png,anchor='center')
if 'text' in kwargs:
self.text = canvas.create_text(kwargs['textx'] * 800,
kwargs['texty'] * 480,
text=kwargs['text'],
fil=kwargs['kleur'] if 'kleur' in kwargs else 'white',
font=('arial',kwargs['size'] if 'size' in kwargs else 10))
if 'canid' in kwargs and 'index' not in kwargs:
self.changeData(kwargs['canid'], kwargs['byte'],kwargs['textx'], kwargs['texty'],kwargs['size'])
elif 'canid' in kwargs and 'index' in kwargs:
self.changeDataWithIndex(kwargs['canid'], kwargs['byte'],kwargs['textx'], kwargs['texty'],kwargs['size'],kwargs['index'])
def changeData(self,canid,byte,textx,texty,size):
try:
msg = bus.recv(1)
canvas.delete(self.text)
if msg.arbitration_id == canid:
self.data = msg.data[byte]
self.text = canvas.create_text(textx * 800,
texty * 480,
text=str(self.data),
fill='white',
font=('arial',size))
except Exception as e:
print(e)
self.root.after(250,lambda: self.changeData(canid,byte,textx,texty,size))
The program works for a few seconds but breaks after, what I mean is that it just stops displaying data and when I move my window around the colors are all over the place.
(When I print something in my functions it still prints)
Since you have more than one instance of PlaceInfo, therefore there is more than one after task reading the CanBus which may cause race condition. Also after task A may read the message for task B and vice versa.
You need to centralize the reading of the CanBus and perform the reading in a thread because recv() is a blocking task.
So you can create a CanReader class which extends from threading.Thread and call recv() in its run() function:
from threading import Thread
from queue import SimpleQueue
class CanReader(Thread):
def __init__(self, channel):
super().__init__(daemon=True)
self._bus = can.interface.Bus(channel=channel)
self._queues = {}
def register(self, canid):
if canid not in self._queues:
self._queues[canid] = []
queue = SimpleQueue()
self._queues[canid].append(queue)
return queue
def run(self):
while True:
msg = self._bus.recv()
for q in self._queues[msg.arbitration_id]:
q.put(msg)
Then you can create an instance of CanReader as below:
bus = CanReader('can0')
bus.start()
CanReader provides a function register() for PlaceInfo to register and get a SimpleQueue object holding the messages received by CanReader.
Below is modified PlaceInfo:
class PlaceInfo():
def __init__(self, root, **kwargs):
self.root = root
img = kwargs.get('img', None)
if img:
image = Image.open('images/'+img)
foto_x_resize, foto_y_resize = kwargs.get('foto_x_resize'), kwargs.get('foto_y_resize')
if foto_x_resize and foto_y_resize:
image = image.resize((foto_x_resize, foto_y_resize), Image.ANTIALIAS)
self.png = ImageTk.PhotoImage(image)
self.image = canvas.create_image(800*kwargs['fotox'], 480*kwargs['fotoy'], image=self.png, anchor='center')
textx = kwargs.get('textx', None)
texty = kwargs.get('texty', None)
fill = kwargs.get('kleur', 'white')
size = kwargs.get('size', 10)
text = kwargs.get('text', None)
if text:
self.text = canvas.create_text(textx*800, texty*480, text=text, fill=fill, font=('arial',size))
canid = kwargs.get('canid', None)
if canid:
self.queue = bus.register(canid) # register to CanReader using canid
if 'index' not in kwargs:
self.changeData(canid, kwargs['byte'])
else:
self.changeDataWithIndex(canid, kwargs['byte'], kwargs['index'])
def changeData(self, canid, byte):
try:
msg = self.queue.get_nowait() # read message
self.data = msg.data[byte]
canvas.itemconfigure(self.text, text=str(self.data))
except Exception as e:
print(e)
self.root.after(250, self.changeData, canid, byte)
Hope this help you solve the issue.

Blender 2.80: handle exceptions in the dialog

I created a dialog in blender 2.80 to be able to insert some values and send them through the "Ok" button.
Inside the "execute" function some operations are performed and, at the end, the window is closed. However, in case of exceptions, the problem must be handled by notifying the user and leaving the dialog box open. To do this I use a try-catch block but it does not work: in case of exception the dialog box closes whatever the returned value.
Is there any way to fix this?
#subscribe
class Test(Operator):
bl_idname = "bl_idname"
bl_label = "bl_label"
bl_description = "bl_description"
input_a = bpy.props.StringProperty(
name = "name_a",
default = "default"
)
input_b = bpy.props.StringProperty(
name = "name_b",
default = "default"
)
input_c = bpy.props.StringProperty(
name = "name_c",
default = "default"
)
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self, width = 450)
def draw(self, context):
col = self.layout.column(align = True)
col.prop(self, "input_a")
col = self.layout.column(align = True)
col.prop(self, "input_b")
col = self.layout.column(align = True)
col.prop(self, "input_c")
def execute(self, context):
try:
#something...
return {"FINISHED"}
except Exception as e:
print(str(e))
return {"CANCELLED"} #I also tried RUNNING_MODAL or PASS_THROUGH

Python pass value in class and def using .self

I want to pass some values to a def but am having issues with properly adding this.
In
class MyAddin():
def __init__(self, imapinfopro, thisApplication): #also tried including _self.Table_Path in this.
try:
self._pro = imapinfopro
self._thisApplication = thisApplication
self._tab = None
#self._table_path =(r'D:\junk\28355.tab')
# Some standard variables to allow functions to be created for each action for easier use. r_ is for Ribbon actions
r_item_name="Infrastructure_Data" #Name of button on main ribbon (no spaces allowed)
r_button_name="Water" #same as operations in the ribbon_customization.py
r_subroup_name="Not Abandoned" #same as "Table" and "CallBack" in the ribbon_customization.py
r_subgroup_action="Open\nClose" #same as 'MB Handler\nNo Parameter'in the ribbon_customization.py
tab = self._pro.Ribbon.Tabs.Add(r_item_name)
self._tab = tab
if tab:
group = tab.Groups.Add(r_button_name, r_button_name)
if group:
button = group.Controls.Add("ButtonOpenTable", r_subroup_name, ControlType.Button)
button.IsLarge = True
button.LargeIcon = CommonUtil.path_to_uri("pack://application:,,,/MapInfo.StyleResources;component/Images/Mapping/infoTool_32x32.png")
button.SmallIcon = CommonUtil.path_to_uri("pack://application:,,,/MapInfo.StyleResources;component/Images/Mapping/infoTool_16x16.png")
button.Command = AddinUtil.create_command(self.open_table_click)
except Exception as e:
print("Failed to load: {}".format(e))
def open_table_click(self, sender):
#table_path=(r'D:\junk\28355.tab') #if left in this is used to open the table but I want to get this value from button.Command above.
table = self._pro.Catalog.OpenTable(table_path)
CommonUtil.do("map from {}".format(table.Alias))
CommonUtil.do("browse * from {}".format(table.Alias))
pass
I want to send the value for table_path in to def open_table_click from button.Command = AddinUtil.create_command(self.open_table_click). If it wasn't a self call it would be ..._command(self.open_table_click, tablepath) and def open_table_click(self, sender, table_path):

QCheckBox state change PyQt4

I'm trying to implement a system in PyQt4 where unchecking a checkbox would call function disable_mod and checking it would call enable_mod. But even though state is changing the checkboxes call the initial function they started with. For this case if an already checked box was clicked it'd always keep calling the disable_mod function! I don't understand why is this happening? Can you guys help me out here a little bit? Here's my code:
from PyQt4 import QtCore, QtGui
from os import walk
from os.path import join
import sys
def list_files_regex(dir):
l = []
for (root, dirnames, filenames) in walk(dir):
for d in dirnames:
list_files_regex(join(root, d))
l.extend(filenames)
return l
directory = "J:/test_pack"
directory = join(directory, "/mods")
count = 0
for y in list_files_regex(directory):
print y
count += 1
print count
class ModEdit(QtGui.QMainWindow):
def __init__(self, title, icon, x, y, w, h):
super(ModEdit, self).__init__()
self.setWindowTitle(title)
self.setWindowIcon(QtGui.QIcon(icon))
self.setGeometry(x, y, w, h)
self.choices = []
self.init()
def init(self):
scroll_widget = QtGui.QScrollArea()
sub_widget = QtGui.QWidget()
v_layout = QtGui.QVBoxLayout()
for y in list_files_regex(directory):
tmp = QtGui.QCheckBox(y, self)
tmp.resize(tmp.sizeHint())
if tmp.text()[len(tmp.text()) - 8: len(tmp.text())] != 'disabled':
tmp.setChecked(True)
# if tmp.isChecked() == 0:
# tmp.stateChanged.connect(self.enable_mod)
# if tmp.isChecked():
# tmp.stateChanged.connect(self.disable_mod)
# v_layout.addWidget(tmp)
self.choices.append(tmp)
print self.choices
for choice in self.choices:
v_layout.addWidget(choice)
if choice.isChecked():
choice.stateChanged.connect(self.disable_mod)
else:
choice.stateChanged.connect(self.enable_mod)
sub_widget.setLayout(v_layout)
scroll_widget.setWidget(sub_widget)
self.setCentralWidget(scroll_widget)
self.show()
def enable_mod(self):
print "ENABLE_TEST"
print self.choices[1].isChecked()
def disable_mod(self):
print "DISABLE_TEST"
print self.choices[1].isChecked()
def test(self):
print 'test'
for ch in self.choices:
if ch.isChecked():
ch.stateChanged.connect(self.disable_mod)
else:
ch.stateChanged.connect(self.enable_mod)
class Rename(QtCore.QObject):
enable = QtCore.pyqtSignal
disable = QtCore.pyqtSignal
app = QtGui.QApplication(sys.argv)
ex = ModEdit("Minecraft ModEdit", "ModEdit.png", 64, 64, 640, 480)
sys.exit(app.exec_())
The problem is that you're only connecting the checkbox stateChanged signal once during initialization. After the state of the checkbox changes, you're not disconnecting the signal and reconnecting it to the correct slot.
You'll need to connect the stateChanged signal to an intermediary slot that will decide which function to call based on the checkbox state. Since you're using the same slot for multiple checkboxes, it's probably best to also pass the checkbox to the slot as well.
from functools import partial
def init(self):
...
for tmp in list_of_checkboxes:
enable_slot = partial(self.enable_mod, tmp)
disable_slot = partial(self.disable_mod, tmp)
tmp.stateChanged.connect(lambda x: enable_slot() if x else disable_slot())
def enable_mod(self, checkbox):
print "ENABLE_TEST"
print checkbox.isChecked()
def disable_mod(self, checkbox):
print "DISABLE_TEST"
print checkbox.isChecked()
Alternatively, since we are now passing the checkbox to the slots, you could just use a single slot and check the checkbox state inside the slot
def init(self):
...
for tmp in list_of_checkboxes:
slot = partial(self.enable_disable_mod, tmp)
tmp.stateChanged.connect(lambda x: slot())
def enable_disable_mod(self, checkbox):
if checkbox.isChecked():
...
else:
...

Categories