3d animation from cinema4d to blender - animation

I need to import a mesh animation from Cinema4D into Blender.
I tried to do that using Collada.The Collada 1.3 importer doesn't seem to
do anything, the Collada 1.4 importer seems to work, but the animation didn't get
imported into Blender.
After reading this post:
Problem solved!
In case anyone comes in here looking
for the answer, I spoke to Otomo via
email and he kindly explained that the
problem lies in the .dae file being
exported incorrectly from C4D.
I hope Otomo doesn't mind me quoting
his email, I just don't want other
people to waste the time I did on such
a stupid problem.
Open up the .dae in a text editor and
change:
data
data
to this:
data
data
The fps must also be the same in both
c4d and blender.
I tried that, but I get an error:
FEEDBACK: Illusoft Collada 1.4 Plugin v0.3.162 started
The minor version of the file you are using is newer then the plug-in, so errors may occur.
image not found: None
Traceback (most recent call last):
File "/Applications/blender/blender.app/Contents/MacOS/.blender/scripts/bpymodules/colladaImEx/cstartup.py", line 681, in ButtonEvent
onlyMainScene, applyModifiers)
File "/Applications/blender/blender.app/Contents/MacOS/.blender/scripts/bpymodules/colladaImEx/translator.py", line 120, in __init__
self.__Import(fileName)
File "/Applications/blender/blender.app/Contents/MacOS/.blender/scripts/bpymodules/colladaImEx/translator.py", line 127, in __Import
documentTranslator.Import(fileName)
File "/Applications/blender/blender.app/Contents/MacOS/.blender/scripts/bpymodules/colladaImEx/translator.py", line 333, in Import
self.sceneGraph.LoadFromCollada(self.colladaDocument.visualScenesLibrary.items, self.colladaDocument.scene)
File "/Applications/blender/blender.app/Contents/MacOS/.blender/scripts/bpymodules/colladaImEx/translator.py", line 550, in LoadFromCollada
ob = sceneNode.ObjectFromDae(daeNode)
File "/Applications/blender/blender.app/Contents/MacOS/.blender/scripts/bpymodules/colladaImEx/translator.py", line 2079, in ObjectFromDae
a.LoadFromDae(daeAnimation, daeNode, newObject)
File "/Applications/blender/blender.app/Contents/MacOS/.blender/scripts/bpymodules/colladaImEx/translator.py", line 1254, in LoadFromDae
interpolationsSource = daeAnimation.GetSource(interpolations.source)
AttributeError: 'NoneType' object has no attribute 'source'
Has anyone come across this issue ? Where can I find a newer Collada importer ?
Any hints on modifying the importer ?
Note:
Blender 2.5a2 imports collada animations, but the coordinate system is different and not all animation makes it through. For example, when I animate a box from 0,0,0 to 100,100,100,
rotate it on x,y,z and scale it on x,y,z, in Blender I get: translation on 1 axis(x which originally is y in cinema 4d), rotation is fine, scale is ignored.

I ended up writing a script.
I barely found usable C.O.F.F.E.E. documentation so went ahead and installed py4D.
The documentation is pretty good and the support on the forum is amazing.
Here's the current script:
import c4d
from c4d import documents,UVWTag
from c4d.utils import Deg
from c4d import symbols as sy, plugins, utils, bitmaps, gui
import math
def BlenderExport():
if not op: return
if op.GetType() != 5100:
print 'Selected Object is not an editable mesh'
return
unit = 0.001#for scale
foffset = 1#for frames
bd = doc.GetRenderBaseDraw()
scr = bd.GetFrameScreen()
rd = doc.GetActiveRenderData()
sizeX = int(rd[sy.RDATA_XRES_VIRTUAL])
sizeY = int(rd[sy.RDATA_YRES_VIRTUAL])
name = op.GetName()
fps = doc.GetFps()
sFrame= doc.GetMinTime().GetFrame(fps)
eFrame= doc.GetMaxTime().GetFrame(fps)
code = 'import Blender\nfrom Blender import *\nimport bpy\nfrom Blender.Mathutils import *\n\nscn = bpy.data.scenes.active\ncontext=scn.getRenderingContext()\ncontext.fps = '+str(fps)+'\ncontext.sFrame = '+str(sFrame)+'\ncontext.eFrame = '+str(eFrame)+'\ncontext.sizeX = '+str(sizeX)+'\ncontext.sizeY = ' + str(sizeY) + '\n'
def GetMesh(code):
# goto 0
doc.SetTime(c4d.BaseTime(0, fps))
c4d.DrawViews( c4d.DA_ONLY_ACTIVE_VIEW|c4d.DA_NO_THREAD|c4d.DA_NO_REDUCTION|c4d.DA_STATICBREAK )
c4d.GeSyncMessage(c4d.EVMSG_TIMECHANGED)
doc.SetTime(doc.GetTime())
c4d.EventAdd(c4d.EVENT_ANIMATE)
code += 'editmode = Window.EditMode()\nif editmode:\tWindow.EditMode(0)\n'
coords4D = op.GetPointAll()
coords = 'coords = ['
uvw = 0
uvs = 'uvs = ['
for tag in op.GetTags():
if tag.GetName() == "UVW":
uvw = tag
for c in coords4D:
coords += '['+str(c.x*unit)+','+str(c.z*unit)+','+str(c.y*unit)+'],'
coords = coords.rpartition(',')[0] + ']\n'
faces4D = op.GetAllPolygons()
fcount = 0
faces = 'faces = ['
for f in faces4D:
faces += '['+str(f)+'],'
uv = uvw.Get(fcount);
uvs += '[Vector('+str(uv[0].x)+','+str(1.0-uv[0].y)+'),Vector('+str(uv[1].x)+','+str(1.0-uv[1].y)+'),Vector('+str(uv[2].x)+','+str(1.0-uv[2].y)+')],'
fcount += 1
faces = faces.rpartition(',')[0] + ']\n'
uvs = uvs.rpartition(',')[0] + ']\n'
code = code + coords + faces + uvs
code += "c4dmesh = bpy.data.meshes.new('"+name+"_mesh')\nc4dmesh.verts.extend(coords)\nc4dmesh.faces.extend(faces)\n\nob = scn.objects.new(c4dmesh,'"+name+"_obj')\nc4dmesh.flipNormals()\n\nif editmode:\tWindow.EditMode(1)\n\n"
code += "c4dmesh.quadToTriangle()\nc4dmesh.addUVLayer('c4duv')\n"
code += "for f in range(0,"+str(fcount)+"):\n\tc4dmesh.faces[f].uv = uvs[f]\n"
return code
def GetIPOKeys(code):
# store properties for tracks
tracks = op.GetCTracks()
# 0,1,2 = Position, 3,4,5 = Scale, 6,7,8 = Rotation, 9 = PLA
# props = [[lx,f],[ly,f],[lz,f],[sx,f],[sy,f],[sz,f],[rx,f],[ry,f],[rz,f]]
try:
props = []
trackIDs = [3,4,5,6,7,8,0,2,1]
propVals = ['LocX','LocZ','LocY','SizeX','SizeY','SizeZ','RotZ','RotX','RotY']
propIPOs = ['Ipo.OB_LOCX','Ipo.OB_LOCZ','Ipo.OB_LOCY','Ipo.OB_SCALEX','Ipo.OB_SCALEY','Ipo.OB_SCALEY','Ipo.OB_ROTZ','Ipo.OB_ROTX','Ipo.OB_ROTY']
for t in range(0,9):
props.append([[],[]])
curve = tracks[t].GetCurve()
keyCount = curve.GetKeyCount()
for k in range(0,keyCount):
key = curve.GetKey(k)
props[t][0].append(key.GetValue())
props[t][1].append(key.GetTime().GetFrame(fps))
# find the max key
maxProp = max(enumerate(props), key = lambda tup: len(tup[1]))[1][1]
maxKeys = len(maxProp)
# loop through tracks and keys
for key in range(0,maxKeys):
code += "Blender.Set('curframe',"+str(maxProp[key])+")\n"
for track in trackIDs:
if(key < len(props[track][0])):
code += "ob."+propVals[track] + " = " + str(props[track][0][key]) + '\n'
code += 'key = ob.insertIpoKey(' + propIPOs[track] + ')\n'
except:
pass
return code
# mesh/morph animation -> mesh always has the same number of verts
def GetShapeKeys(code):
track = 0;
tracks = op.GetCTracks()
for t in tracks:
if(t.GetName() == 'PLA'): track = t
# track = op.GetCTracks()[9]
if track != 0:
curve = track.GetCurve()
keyCount = curve.GetKeyCount()
verts = []
frames = []
vertsNum = op.GetPointCount()
ctime = doc.GetTime()
for k in range(1,keyCount):
key = curve.GetKey(k)
frames.append(key.GetTime().GetFrame(fps))
c4d.StatusSetBar(100*(k/keyCount))
doc.SetTime(key.GetTime())
c4d.DrawViews( c4d.DA_ONLY_ACTIVE_VIEW|c4d.DA_NO_THREAD|c4d.DA_NO_REDUCTION|c4d.DA_STATICBREAK )
c4dvecs = op.GetPointAll();
blendvecs = []
for v in c4dvecs:
blendvecs.append([v.x*unit,v.z*unit,v.y*unit])
verts.append(blendvecs)
c4d.GeSyncMessage(c4d.EVMSG_TIMECHANGED)
doc.SetTime(ctime)
c4d.EventAdd(c4d.EVENT_ANIMATE)
c4d.StatusClear()
code += '\n\n# shape keys\nverts = ' + str(verts) + '\n'
code += "if(ob.activeShape == 0):\n\tob.insertShapeKey()\n\n"
for f in range(0,len(frames)):
kNum = str(f+1)
code += "if editmode: Window.EditMode(0)\n"
code += "for v in range(0,"+str(vertsNum)+"):\n\tc4dmesh.verts[v].co.x = verts["+str(f)+"][v][0]\n\tc4dmesh.verts[v].co.y = verts["+str(f)+"][v][1]\n\tc4dmesh.verts[v].co.z = verts["+str(f)+"][v][2]\n"
code += "c4dmesh.calcNormals()\n"
code += "ob.insertShapeKey()\n"
code += "if editmode: Window.EditMode(1)\n"
code += "shapeKey = ob.getData().getKey()\n"
code += "newIpo = Ipo.New('Key','newIpo')\n"
code += "if(shapeKey.ipo == None): shapeKey.ipo = newIpo\n"
code += "if(shapeKey.ipo['Key "+kNum+"'] == None): shapeKey.ipo.addCurve('Key "+kNum+"')\n"
if(f == 0): code += "shapeKey.ipo['Key "+kNum+"'].append(BezTriple.New(1.0,0.0,0.0))\n"
if(f > 0): code += "shapeKey.ipo['Key "+kNum+"'].append(BezTriple.New("+str(float(frames[f-1]))+",0.0,0.0))\n"
code += "shapeKey.ipo['Key "+kNum+"'].append(BezTriple.New("+str(float(frames[f]))+",1.0,0.0))\n"
else:
#no PLA tracks, look for morph tag
vertsNum = op.GetPointCount()
for tag in op.GetTags():
if tag.GetType() == 1019633:
# print tag[sy.MORPHTAG_MORPHS]
'''
work around
1. store first key for each track curve
2. set the first key value to 1 for the 1st track and 0 for the others
3. store the mesh vertices -> track name verts = []
4. after all track verts are stored, restore the original values
5. write the the curve keys for blender shape keys
'''
code += "if(ob.activeShape == 0):\n\tob.insertShapeKey()\n\n"
tc = 0
tcs = str(tc+1)
for track in tag.GetCTracks():
curve = track.GetCurve()
value = curve.GetKey(0).GetValue()
curve.GetKey(0).SetValue(curve,1.0)
print track.GetName()
doc.SetTime(c4d.BaseTime(0, fps))
c4d.DrawViews( c4d.DA_ONLY_ACTIVE_VIEW|c4d.DA_NO_THREAD|c4d.DA_NO_REDUCTION|c4d.DA_STATICBREAK )
c4dvecs = op.GetPointAll();
blendverts = []
for v in c4dvecs:
blendverts.append([v.x*unit,v.z*unit,v.y*unit])
code += "Key"+tcs+"verts = " + str(blendverts)+"\n"
code += "if editmode: Window.EditMode(0)\n"
code += "for v in range(0,"+str(vertsNum)+"):\n\tc4dmesh.verts[v].co.x = Key"+tcs+"verts[v][0]\n\tc4dmesh.verts[v].co.y = Key"+tcs+"verts[v][1]\n\tc4dmesh.verts[v].co.z = Key"+tcs+"verts[v][2]\n"
code += "c4dmesh.calcNormals()\n"
code += "ob.insertShapeKey()\n"
code += "if editmode: Window.EditMode(1)\n"
code += "shapeKey = ob.getData().getKey()\n"
code += "newIpo = Ipo.New('Key','newIpo')\n"
code += "if(shapeKey.ipo == None): shapeKey.ipo = newIpo\n"
code += "if(shapeKey.ipo['Key "+tcs+"'] == None): shapeKey.ipo.addCurve('Key "+tcs+"')\n"
print op.GetPointAll()
c4d.GeSyncMessage(c4d.EVMSG_TIMECHANGED)
curve.GetKey(0).SetValue(curve,value)
keyCount = curve.GetKeyCount()
for k in range(0,keyCount):
key = curve.GetKey(k)
value = key.GetValue()
frame = key.GetTime().GetFrame(fps)
code += "shapeKey.ipo['Key "+tcs+"'].append(BezTriple.New("+str(float(frame))+","+str(value)+",0.0))\n"
tc += 1
tcs = str(tc+1)
c4d.EventAdd(c4d.EVENT_ANIMATE)
return code
def GetCamera(code):
bd = doc.GetRenderBaseDraw()
cp = bd.GetSceneCamera(doc)
if cp is None: cp = bd.GetEditorCamera()
fov = Deg(cp[sy.CAMERAOBJECT_FOV])
pos = cp.GetPos()
rot = cp.GetRot()
code += "\nc4dCam = Camera.New('persp','c4d_"+cp.GetName()+"')\nc4dCam.drawPassepartout = True\nc4dCam.alpha = 0.5\nc4dCam.drawLimits = 1\nc4dCam.dofDist = 100.0\n"
code += "c4dCam.angle = "+str(fov)+"\nc4dCamLens = c4dCam.lens\nc4dCam.lens = c4dCamLens\nWindow.RedrawAll()\nc4dCamObj = scn.objects.new(c4dCam)\n"
code += "c4dCamObj.setLocation("+str([pos.x,pos.z,pos.y])+")\n"
code += "c4dCamObj.setEuler("+str([rot.x+(math.pi*.5),rot.y,rot.z])+")\n"
code += "scn.setCurrentCamera(c4dCamObj)\n"
return code
code = GetMesh(code)
code = GetIPOKeys(code)
code = GetShapeKeys(code)
code = GetCamera(code)
file = open(doc.GetDocumentPath()+'/'+op.GetName()+'_export.py','w')
file.write(code)
file.close()
BlenderExport()
It supports ipo keys(position,rotation,scale).
Point Level Animation gets converted to multiple shape keys.
Morph Tag animation preserves nicely.

Related

TensorFlow training gets slower every batch

I am new to TensorFlow and I get my code running successfully by modifying tutorials from the official website.
I checked some other answers on StackOverflow, which says my problem is likely due to something is being added to the graph every time. However, I have no idea where to look for the code that might have caused this.
Also, I used tf.py_function to map the dataset because I really need to enable eagerly mode in the mapping.
def get_dataset(data_index):
# data_index is a Pandas Dataframe that contains image/label pair info, each row is one pair
data_index = prepare_data_index(data_index)
# shuffle dataframe here because dataset.shuffle is taking very long time.
data_index = data_index.sample(data_index.shape[0])
path = path_to_img_dir
# list of dataframe indices indicating rows that are going to be included in the dataset for training.
indices_ls = ['{}_L'.format(x) for x in list(data_index.index)] + ['{}_R'.format(x) for x in list(data_index.index)]
# around 310k images
image_count = len(indices_ls)
list_ds = tf.data.Dataset.from_tensor_slices(indices_ls)
# dataset.shuffle is commented out because it takes too much time
# list_ds = list_ds.shuffle(image_count, reshuffle_each_iteration=False)
val_size = int(image_count * 0.2)
train_ds = list_ds.skip(val_size)
val_ds = list_ds.take(val_size)
def get_label(index):
index = str(np.array(index).astype(str))
delim = index.split('_')
state = delim[1]
index = int(delim[0])
if state == 'R':
label = data_index.loc[index][right_labels].to_numpy().flatten()
elif state == 'L':
label = data_index.loc[index][left_labels].to_numpy().flatten()
return tf.convert_to_tensor(label , dtype=tf.float16)
def get_img(index):
index = str(np.array(index).astype(str))
delim = index.split('_')
state = delim[1]
index = int(delim[0])
file_path = '{}_{}.jpg'.format(data_index.loc[index, 'sub_folder'],
str(int(data_index.loc[index, 'img_index'])).zfill(4)
)
img = tf.io.read_file(os.path.join(path, file_path))
img = tf.image.decode_jpeg(img, channels=3)
full_width = 320
img = tf.image.resize(img, [height, full_width])
# Crop half of the image depending on the state
if state == 'R':
img = tf.image.crop_to_bounding_box(img, offset_height=0, offset_width=0, target_height=height,
target_width=int(full_width / 2))
img = tf.image.flip_left_right(img)
elif state == 'L':
img = tf.image.crop_to_bounding_box(img, offset_height=0, offset_width=int(full_width / 2), target_height=height,
target_width=int(full_width / 2))
img = tf.image.resize(img, [height, width])
img = tf.keras.preprocessing.image.array_to_img(
img.numpy(), data_format=None, scale=True, dtype=None
)
# Apply auto white balancing, output an np array
img = AWB(img)
img = tf.convert_to_tensor(img, dtype=tf.float16)
return img
def process_path(index):
label = get_label(index)
img = get_img(index)
return img, label
AUTOTUNE = tf.data.experimental.AUTOTUNE
train_ds = train_ds.map(lambda x: tf.py_function(
process_path,
[x], (tf.float16, tf.float16)), num_parallel_calls=AUTOTUNE)
val_ds = val_ds.map(lambda x: tf.py_function(
process_path,
[x], (tf.float16, tf.float16)), num_parallel_calls=AUTOTUNE)
def configure_for_performance(ds):
ds = ds.cache()
# ds = ds.shuffle(buffer_size=image_count)
ds = ds.batch(batch_size)
ds = ds.prefetch(buffer_size=AUTOTUNE)
return ds
train_ds = configure_for_performance(train_ds)
val_ds = configure_for_performance(val_ds)
return train_ds, val_ds
Can anyone please help me? Thanks!
Here is the rest of my code.
def initialize_model():
IMG_SIZE = (height, width)
preprocess_input = tf.keras.applications.vgg19.preprocess_input
IMG_SHAPE = IMG_SIZE + (3,)
base_model = tf.keras.applications.VGG19(input_shape=IMG_SHAPE,
include_top=False,
weights='imagenet')
global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
prediction_layer = tf.keras.layers.Dense(class_num, activation=tf.nn.sigmoid, use_bias=True)
inputs = tf.keras.Input(shape=(height, width, 3))
x = preprocess_input(inputs)
x = base_model(x, training=True)
x = global_average_layer(x)
outputs = prediction_layer(x)
model = tf.keras.Model(inputs, outputs)
def custom_loss(y_gt, y_pred):
L1_loss_out = tf.math.abs(tf.math.subtract(y_gt, y_pred))
scaler = tf.pow(50.0, y_gt)
scaled_loss = tf.math.multiply(L1_loss_out, scaler)
scaled_loss = tf.math.reduce_mean(
scaled_loss, axis=None, keepdims=False, name=None
)
return scaled_loss
base_learning_rate = 0.001
model.compile(optimizer=tf.keras.optimizers.SGD(learning_rate=base_learning_rate, momentum=0.9),
loss=custom_loss,
metrics=['mean_absolute_error']
)
return model
def train(data_index, epoch_num, save_path):
train_dataset, validation_dataset = get_dataset(data_index)
model = initialize_model()
model.summary()
history = model.fit(train_dataset,
epochs=epoch_num,
validation_data=validation_dataset)
model.save_weights(save_path)
return model, history

GUI plot refreshing incorrectly on wxpython panel

Ok, so I've been tasked with creating a VERY simple GUI at work (I'm an intern). The task is to eventually connect to a machine and process real data, but right now I'm working on randomly generated sine data with noise. I've chose to work in Python 3.0, and use wxpython to create my GUI components.
As I want everything to appear on the same window, I'm using panels (hence wx.lib.plot.PlotCanvas rather than something like matplotlib.pyplot)
The problem that I have is that over time, the plot seems to 'expand' off of the panel. This is temporarily solved when I manually resize the window, but resumes again immediately after (you need to run the code to see what I mean).
Expansion over time in panel
Another problem (that has bugged me since I have started writing the code) is that sometimes when I resize the window (manually) or minimize it and then maximize it again, the timer randomly starts and stops.
I have tried all sorts of things (changing padding in sizers, extra arguments, changing time between refreshes GetBestSize()) but I believe that I simply don't understand wxpython well enough to identify where the problem is
I would really appreciate any help you can shed on either of these problems (I don't know, they might even be linked to each other).
FYI: I am not an experienced coder, and my code is not finished (I have more functions to code, but I feel like I should resolve this first). I have constructed this code by looking at different techniques from various tutorials and websites like stackoverflow, so I know it's not formatted well and could definitely be made more efficient. Also, I have removed some parts just to be safe about confidentiality - nothing important, just strings in messages.
PS: If you do have an easier way to do the whole plot/update thing that doesn't have this problem (preferably still in wx) I would be thrilled to hear that as well
And here's my code:
EDIT: Solved the expanding problem by using self.p2.SetSize((W+0,L+0)) instead of (self.p2.GetBestSize())
EDIT: Made transitions much smoother by just regenerating data and redrawing it on existing canvas in the evt_timer function (instead of recreating the whole canvas, which gave a blink-y appearance if you know what I mean)
import wx
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import leastsq
import wx.lib.plot as plot
import time
import os
wildcard = "Text File (*.txt)|*.txt|"\
"Picture (*.png)|*.png|"\
"All files (*.*)|*.*"#This wildcard shows the options for file endings in the "SAVE" tab - see OnSave(self,event)
wildcard2 = "Picture (*.png)|*.png|"\
"Text File (*.txt)|*.txt|"\
"All files (*.*)|*.*"
class PlotCanvas(plot.PlotCanvas):
def __init__(self,parent,id,size,accepted):
"""
This randomly generates sine data (with noise) and plots it to a panel.
Incorporated as a separate class instead of instatiating it as a plot.PlotCanvas object
to overcome an issue of the size of the plot in the panel.
"""
plot.PlotCanvas.__init__(self,parent,id,style=wx.BORDER_SUNKEN,size = size)
N = 100 # number of data points
self.t = np.linspace(0, 4*np.pi, N)
f = 1.15247 # Optional!! Advised not to use
self.data = 3.0*np.sin(f*self.t+0.001) + 0.5 + np.random.randn(N) # create artificial data with noise
guess_mean = np.mean(self.data)
guess_phase = 0
guess_freq = 1
guess_amp = 1
optimize_func = lambda x: x[0]*np.sin(x[1]*self.t+x[2]) + x[3] - self.data
est_amp, est_freq, est_phase, est_mean = leastsq(optimize_func, [guess_amp, guess_freq, guess_phase, guess_mean])[0]
fine_t = np.arange(0,max(self.t),0.1)
data_fit=est_amp*np.sin(est_freq*fine_t+est_phase)+est_mean
multiplier = 1
dataset1 = [(x,[d for d in self.data][[td for td in self.t].index(x)])for x in [td for td in self.t]]
fitdata1 = [(x,[df for df in data_fit][[tf for tf in fine_t].index(x)]) for x in [tf for tf in fine_t]]
dataset =[(x,y*multiplier) for (x,y) in dataset1]
fitdata = [(x,y*multiplier) for (x,y) in fitdata1]
self.data = dataset
self.data2 = fitdata
line = plot.PolyLine(self.data,legend = 'random',colour = 'light blue', width =2)
line2 = plot.PolyLine(self.data2,legend = 'sineline',colour ='black',width =2)
a = []
if "D" in accepted:
a.append(line)
if "S" in accepted:
a.append(line2)
if "G" in accepted:
pass
if "L" in accepted:
pass
gc = plot.PlotGraphics(a,'Line Graph','X','Y')
xmin = self.t[0]-0.01*(self.t[-1]-self.t[0])
xmax = self.t[-1]+0.01*(self.t[-1]-self.t[0])
self.Draw(gc,xAxis=(xmin,xmax),yAxis=(min([x[1] for x in dataset])-0.01*(max([x[1] for x in dataset])-min([x[1] for x in dataset])),
max([x[1] for x in dataset])+0.01*(max([x[1] for x in dataset])-min([x[1] for x in dataset]))))
#self.showLegend = True
#self.enableZoom = True
def Dialog(self, parent, message, c):# Will be used to notify the user of errors/processes
if c == "W":
caption = "Warning!"
dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_WARNING)
elif c == "I":
caption = "Information"
dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()#Destroys dialog on close
class Frame(wx.Frame):
"""
This is the main class. In it, we declare the separate panels, canvas, menubar, buttons and sizers.
"""
def __init__(self,parent,id,title):
wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition)
self.CurrentDirectory = os.getcwd()
self.timer=wx.Timer(self)#Instantiating the timer
self.count=0
self.Bind(wx.EVT_TIMER,self.evt_timer)#Binding it to itself so that it is always triggered
self.Bind(wx.EVT_PAINT,self.paint)
menubar = wx.MenuBar()
fileMenu = wx.Menu() #Creating the Menubar at the top
#Creating 3 menus: fileMenu,fit,and help
save = wx.Menu()
z = wx.MenuItem(save,wx.ID_ANY,'Save Raw Data\tCtrl+D')
self.Bind(wx.EVT_MENU,self.OnSave,z)
save.Append(z)
z= wx.MenuItem(save,wx.ID_ANY,'Save Image\tCtrl+I')
self.Bind(wx.EVT_MENU,self.OnSaveImage,z)
save.Append(z)
fileMenu.AppendSubMenu(save,'&Save')
fileMenu.AppendSeparator()
z = wx.MenuItem(fileMenu, wx.ID_EXIT, '&Quit\tCtrl+W')
self.Bind(wx.EVT_MENU, self.OnQuit, z)
fileMenu.Append(z)
fit = wx.Menu()#Making a check menu
self.gaussian = fit.Append(wx.ID_ANY,'Gaussian',kind = wx.ITEM_CHECK)
#self.Bind(wx.EVT_MENU,self.ToggleGaussian,self.gaussian)
fit.Check(self.gaussian.GetId(),False)
self.sine = fit.Append(wx.ID_ANY,'Sine',kind = wx.ITEM_CHECK)
#self.Bind(wx.EVT_MENU,self.ToggleSine,self.sine)
fit.Check(self.sine.GetId(),False)
self.linear = fit.Append(wx.ID_ANY,'Linear',kind=wx.ITEM_CHECK)
#self.Bind(wx.EVT_MENU,self.ToggleLinear,self.linear)
fit.Check(self.linear.GetId(),False)
help = wx.Menu()
z = wx.MenuItem(help,wx.ID_ANY,'&About\tCtrl+H')
self.Bind(wx.EVT_MENU,self.OnHelp,z)
help.Append(z)
menubar.Append(fileMenu, '&File')
menubar.Append(fit, '&Fit')
menubar.Append(help, '&Help')#adding menus to menubar
self.SetMenuBar(menubar)#formatting the frame with menubar
self.sp = wx.SplitterWindow(self)#Splitting the window into 2 panels
self.p1 = wx.Panel(self.sp,style = wx.SUNKEN_BORDER)#For buttons and user events
self.p2 = wx.Panel(self.sp,style = wx.SUNKEN_BORDER)#For display of the plot
self.sp.SplitVertically(self.p1,self.p2,300)
sizer = wx.GridBagSizer(3, 3)#Versatile sizer for layout of first panel self.p1
bitmappath = self.CurrentDirectory + "\\BITMAPS"
bmp = wx.Bitmap(bitmappath+"\\SAVE.BMP",wx.BITMAP_TYPE_BMP)
self.saveBtn = wx.BitmapButton(self.p1,wx.ID_ANY,bitmap = bmp,size =(bmp.GetWidth()+10,bmp.GetHeight()+10))
self.Bind(wx.EVT_BUTTON,self.OnSave,self.saveBtn)
sizer.Add(self.saveBtn, (0, 0), wx.DefaultSpan, wx.ALL,5)
bmp = wx.Bitmap(bitmappath +"\\START.BMP",wx.BITMAP_TYPE_BMP)
self.startBtn = wx.BitmapButton(self.p1,-1,bitmap = bmp,size =(bmp.GetWidth()+10,bmp.GetHeight()+10))# A button that starts and stops the plotting
self.startBtn.startval = "START"
self.Bind(wx.EVT_BUTTON,self.paint,self.startBtn)
sizer.Add(self.startBtn, (0, 1), wx.DefaultSpan,wx.ALL,5)
sizer1 = wx.BoxSizer(wx.VERTICAL)
W,L = self.p2.GetSize()
self.p2.canvas = PlotCanvas(self.p2,wx.ID_ANY,(W,L),["D"])
sizer1.Add(self.p2.canvas,1,wx.ALL,0,0)
self.p2.SetSizerAndFit(sizer1)
self.p1.SetSizerAndFit(sizer)
self.p2.SetSizerAndFit(sizer1)
self.p2.SetSize(W,L)
self.Maximize(True)
self.Centre()
self.Show()
############### event methods ###########
def paint(self,event):
"""
Updates the canvas based on the value of the startbtn(not the image). Bound to self.timer.
"""
bitmappath = self.CurrentDirectory + "\\BITMAPS"
if self.startBtn.startval == "START":
self.timer.Start(1)# increase the value for more time
bmp = wx.Bitmap(bitmappath + "\\STOP.BMP",wx.BITMAP_TYPE_BMP)
self.startBtn.SetBitmap(bmp)
self.startBtn.startval = "STOP"
elif self.startBtn.startval == "STOP":
self.timer.Stop()
bmp = wx.Bitmap(bitmappath+ "\\START.BMP",wx.BITMAP_TYPE_BMP)
self.startBtn.SetBitmap(bmp)
self.startBtn.startval = "START"
def evt_timer(self,event):
self.count +=1
if self.count== 10:# By increasing count (or the number in self.timer.Start()) you can increase the interval between updates
#self.p2.canvas.Clear()
sizer1 = wx.BoxSizer(wx.VERTICAL)
W,L = self.p2.GetSize()
a = ["D"]
if self.sine.IsChecked():
a.append("S")
elif self.linear.IsChecked():
a.append("L")
elif self.gaussian.IsChecked():
a.append("G")
self.p2.canvas = PlotCanvas(self.p2,wx.ID_ANY,(W,L),a)
sizer1.Add(self.p2.canvas,1,wx.ALL,0,0)
self.p2.SetSizerAndFit(sizer1)
self.p2.SetSize(self.p2.GetBestSize())
self.count=0 # reset the count
def Dialog(self, parent, message, c):# Will be used to notify the user of errors/processes
if c == "W":
caption = "Warning!"
dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_WARNING)
elif c == "I":
caption = "Information"
dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()#Destroys dialog on close
def OnSave(self,event):#Triggered by menubar and button
try:
rawdata = self.p2.canvas.data
raw_X =[x[0] for x in rawdata]
raw_Y =[x[1] for x in rawdata]
dlg = wx.FileDialog(#Code for this from http://www.blog.pythonlibrary.org
self, message="Save file as ...",
defaultDir=self.CurrentDirectory,
defaultFile=str(time.ctime()), wildcard=wildcard, style=wx.FD_SAVE
)
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
dlg.Destroy()
f = open(path+".txt","w+")
for i in range(len(raw_X)):
f.write(str(raw_X[i])+"\t"+str(raw_Y[i])+"\n")
f.close()
self.Dialog(None,"File successfully saved","I")
except UnboundLocalError:#Catch error when user closes save window without selecting any directory or filename
pass
def OnSaveImage(self,event):
try:
rawdata = self.p2.canvas.data
raw_X = [x[0] for x in rawdata]
raw_Y = [x[1] for x in rawdata]
dlg = wx.FileDialog(
self, message="Save file as ...",
defaultDir=self.CurrentDirectory,
defaultFile=str(time.ctime()), wildcard=wildcard2, style=wx.FD_SAVE
)
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
dlg.Destroy()
fig1 = plt.figure()
plt.plot(raw_X,raw_Y)
plt.title("Raw Data")
fig1.savefig(path+".png")
self.Dialog(None,"File successfully saved","I")
except UnboundLocalError:
pass
def OnMultiply(self,e):
try:
factor = self.x.GetValue()
factor = float(factor)
self.IntegrationTime = factor
except ValueError as e:
self.Dialog(None,str(e),"W")
def OnQuit(self, e):
self.Close()
def OnHelp(self,e):
self.Dialog(None,"N/A","I")
def ToggleSine(self,e):
pass
def ToggleLinear(self,e):
self.Dialog(None,"Not added yet","W")
def ToggleGaussian(self,e):
self.Dialog(None,"Not added yet","W")
if __name__ =="__main__":
app=wx.App()
Frame(None,-1,"N/A")
app.MainLoop()

VideoCapture.read() returns past image

I'm running python3.6 with openCV on the Raspberry pi(OS is Raspbian)
The approximate structure of the code is as follows.
The image is captured at time interval(3~5 min).
Captured image is processed in functions and returns measure(kind of accuracy)
Iterate 1.~2. until end_check() returns True
Problem is that the most recent taken image is out of date. It looks it was taken almost 10 minutes ago. All images recently taken are late. But the images taken at the beginning seem to be timed. And the time recorded in all .jpg files entered correctly
+It looks this problem is occured after more than a hour. (20~22 iterates)
Images are captured with cam0.read() in the cv2 package. Below is main part of the code. It is quite long to upload full code. It someone request, I will update.
def run(interval,model_list):
cam0 = cv2.VideoCapture(0) #Only cam0 is used. cam2 is just to record.
camdir = "/home/pi/capstone/cam0/"
cam2 = cv2.VideoCapture(1)
cam2dir = "/home/pi/capstone/cam2/"
runNo = 0
acc_list = list()
error_list = list()
end = False
while(end == False):
print(runNo,"th run")
img_name = "%s.jpg" %runNo
frame, res = cam0.read() #`res` is the image which will be processed
cv2.imwrite(os.path.join(camdir,img_name),res)
_ , cam2pic = cam2.read()
cv2.imwrite(os.path.join(cam2dir,img_name),cam2pic)
try:
temp = Real(res)
mat = temp.match(model_list)
acc_list.append([mat,runNo])
print("Accuracy=", mat)
except ValueError:
acc_list.append(["ValueError",runNo])
error_list.append(["ValueError",runNo])
except AttributeError:
acc_list.append(["AttributeError", runNo])
error_list.append(["AttributeError",runNo])
except SmallObjectError:
acc_list.append(["SmallObjectError", runNo])
error_list.append(["SmallObjectError",runNo])
runNo = runNo+1
endNo = 40
if(runNo/2 > endNo):
end_check(res, end)
elif(runNo > endNo):
end = True
sleep(interval*60)
with open("acc_list.txt", "w") as output: #records for tracking errors
output.write(str(acc_list))
with open("err_list.txt", "w") as output:
output.write(str(error_list))
cam0.release()
cam2.release()
run(3.5,model_list)
(+) Some newly found things and guess
The images time gap is getting bigger with code running
Code finally showed OpenCV Error
It looks kind of Video signal is stored in RAM on R-pi and .read() returning out-dated image in RAM
Stored Video signal in RAM araise resource problem
Below is the OpenCV Error
OpenCV Error: Assertion failed (dsize.area() > 0 || (inv_scale_x > 0 && inv_scale_y > 0)) in resize, file /home/pi/opencv/opencv-3.4.0/modules/imgproc/src/resize.cpp, line 4045
Traceback (most recent call last):
File "runpi.py", line 264, in <module>
run(3.5,model_list)
File "runpi.py", line 234, in run
mat = temp.match(model_list)
File "runpi.py", line 184, in match
self.__resize(model.get_m_inform())
File "runpi.py", line 147, in __resize
self.mask = cv2.resize(self.mask, None, fx=reratio, fy=reratio, interpolation = inter_method)
cv2.error: /home/pi/opencv/opencv-3.4.0/modules/imgproc/src/resize.cpp:4045: error: (-215) dsize.area() > 0 || (inv_scale_x > 0 && inv_scale_y > 0) in function resize
(+) Some part of code araising Error
This is __.resize() method. When I process the image that occured OpenCV Error manually, it works well even if OpenCV Error pointing out kind of image size matter. So I thought it is not matter of image itself or size got from md_inf(). Anyway Here is code.
def __resize(self, md_inf):
#md_inf = [219, 122, 132, 171, 262] <-sample
reratio = md_inf[0]/self.y
if(reratio>1):
inter_method = cv2.INTER_LINEAR
else:
inter_method = cv2.INTER_AREA
###below is line 147###
self.mask = cv2.resize(self.mask, None, fx=reratio, fy=reratio, interpolation = inter_method)
temp = np.zeros((md_inf[3], md_inf[4]), np.uint8)
m_cx, m_cy = md_inf[1:3]
_, contour, _ = cv2.findContours(self.mask, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
total_contour = contour[0]
for ctr in contour[1:]:
total_contour = np.concatenate((total_contour, ctr), axis =0)
mmt = cv2.moments(total_contour)
if(mmt['m00'] < 7500):
raise SmallObjectError
self.cy = int(mmt['m10']/mmt['m00']) #y is horrizon axis
self.cx = int(mmt['m01']/mmt['m00']) #x is vertical axis
x, y = self.mask.shape
adjust = m_cx - self.cx + x - temp.shape[0]
if(adjust > 0):
m_cx = m_cx - adjust
temp[(m_cx-self.cx):(m_cx-self.cx) +x, (m_cy-self.cy):(m_cy-self.cy) +y] = self.mask
self.mask = temp
I agree with Mark Serchell's comment. The way I am using is to set variable to time + x seconds and check. OpenCV have useful feature for skiping frames like cam.grab(). It will just read that frame from buffer but will not do anything with it. In that way you can avoid "suffering from buffering". Simple code would be:
import cv2
import time
cam = cv2.VideoCapture(url)
ret,frame = cam.read()
timeCheck = time.time()
future = 10*60 # delay
while ret:
if time.time() >= timeCheck:
ret,frame = cam.read()
# Do your staff here
timeCheck = time.time()+future
else:
# Read from buffer, but skip it
ret = cam.grab() # note that grab() function returnt only status code,not the frame

how to draw a line in windows store apps

I am trying to create an application which should consist of a line drawn when keyboard key is pressed. When the left arrow in the keyboard is pressed then the line should move in left direction. When right arrow in the keyboard is pressed then the respective line should move in right direction.
I think it is possible with Path class but I don't know how to implement. Even I don't know how to start the code. Can you please guide me how to draw the line in windows store apps.
private PathGeometry DrawGeometry()
{
bool largeArc = WedgeAngle > 180.0;
Size outerArcSize = new Size(Radius, Radius);
Size innerArcSize = new Size(InnerRadius, InnerRadius);
Point innerArcStartPoint = Utilities.ComputeCartesianCoordinate(RotationAngle, InnerRadius);
Point ButtomLineEndPoint = Utilities.ComputeCartesianCoordinate(RotationAngle, Radius);
Point OuterArcEndPoint = Utilities.ComputeCartesianCoordinate(RotationAngle + WedgeAngle, Radius);
Point EndLineEndPoint = Utilities.ComputeCartesianCoordinate(RotationAngle + WedgeAngle, InnerRadius);
innerArcStartPoint.X += CentreX;
innerArcStartPoint.Y += CentreY;
ButtomLineEndPoint.X += CentreX;
ButtomLineEndPoint.Y += CentreY;
OuterArcEndPoint.X += CentreX;
OuterArcEndPoint.Y += CentreY;
EndLineEndPoint.X += CentreX;
EndLineEndPoint.Y += CentreY;
PathFigure path = new PathFigure();
path.StartPoint = innerArcStartPoint;
ArcSegment InnerArc = new ArcSegment();
InnerArc.Size = innerArcSize;
InnerArc.SweepDirection = SweepDirection.Counterclockwise;
InnerArc.Point = innerArcStartPoint;
InnerArc.IsLargeArc = largeArc;
LineSegment ButtomLine = new LineSegment();
ButtomLine.Point = ButtomLineEndPoint;
ArcSegment OuterArc = new ArcSegment();
OuterArc.SweepDirection = SweepDirection.Clockwise;
OuterArc.Point = OuterArcEndPoint;
OuterArc.Size = outerArcSize;
OuterArc.IsLargeArc = largeArc;
LineSegment EndLine = new LineSegment();
EndLine.Point = EndLineEndPoint;
path.Segments.Add(ButtomLine);
path.Segments.Add(OuterArc);
path.Segments.Add(EndLine);
path.Segments.Add(InnerArc);
PathGeometry myPath = new PathGeometry();
myPath.Figures.Add(path);
return myPath;
}
This code will Draw a Pie slice for you as I was building a PieChart it Containsn Lines Curves etc. It will save lots of your time

Python GUI (tkinter.ttk) application slow

I've got (working) application done with ttk. It uses self-created module for showing a comport-related controls and a canvas which draws a few graphs on it. When I crate an instance of my object, it starts a thread in which processes serial input and appends this to a list (one list per graph). When I have 3-6 graphs, the application gets noticeably slow. It also has got a few bugs, but I will adress them when I'm done with the general concept.
Things that may help you help me:
comport is an instance of a self-written object that derives from
LabelFrame and Serial.Serial
coordinates for graphs are stored in a dictionary of lists:
self.graphs = {} self.graphs['name1']=[] number of coordinates stored
is up to the width of canvas, so about 1000-2000 per graph. Have six
graphs - please multiply by 6
With every new coordinate arriving I pop(0) from the list and
append() the new coordinate
I forgot, I also store timing of each new set of coordinates arriving
in a separate list
I use a preiodic call function to process the lists: self.after(100,
func=self.periodicCall)Thus every 100ms I delete(ALL) from the canvas
and I draw every graph with theset of lines. So if I have 1000 coords
in 6 graps, I draw 6000 small lines
Plus some service info of course such as a few rulers
So I guess the idea is clear. I want to figure out what would be the better approach. I'm just a started in python and in programming as well, so I'm asking for your excuse for the code that I'm going to post and for the pain in your eyes it's gonna cause. I don't have any programming style and I want to fix it. At least a bit. So any other comments on anything you'll se in the code are welcome.
#-------------------------------------------------------------------------------
# Name: dataVisualizer
# Purpose:
#
# Author: dccharacter
#
# Created: 23.03.2012
# Copyright: (c) dccharacter 2012
# Licence: <your licence>
#-------------------------------------------------------------------------------
#!/usr/bin/env python
from tkinter import *
from tkinter.ttk import *
from robowidgets.serialPortGui import *
import threading
import re
import atexit
import random
from datetime import datetime
import time
class dataVisualizer(LabelFrame):
def __init__(self, master, comport , cnf={}, **kw):
self.master = master
self.comport = comport
LabelFrame.__init__(self, *cnf, **kw)
self.messageVar = StringVar()
Label(self, text="Message format regexp:").pack()
self.messagePattern = Entry(self, width = 20, text = 234234, textvariable = self.messageVar);
self.messageVar.set(r'(-*\d+),(-*\d+),(-*\d+),(-*\d+),(-*\d+),(-*\d+)')
self.messagePattern.pack()
Button(self, text = "Pause", command = self.pause).pack()
self.pauseFlag = TRUE
self.canvWidth, self.canvHeight = 1000, 700
self.density = 1 ##width of pixel - the bigger, the wider graph
self.numOfDots = self.canvWidth//self.density
self.graphs = {}
self.graphs['name1']=[]
self.graphs['name2']=[]
self.graphs['name3']=[]
self.graphs['name4']=[]
self.graphs['name5']=[]
self.graphs['name6']=[]
self.timings = []
self.zeroTiming = datetime.now()
self.colors = ['red', 'blue', 'green', 'orange', 'violet', 'black', 'cyan']
self.canv = Canvas(self, width = self.canvWidth, height = self.canvHeight)
self.canv.pack()
self.thread = threading.Thread(target = self.workerThread)
self.thread.start()
self.serialData = []
self.periodicCall()
def pause(self):
self.pauseFlag = ~self.pauseFlag
def redraw(self):
self.canv.delete(ALL)
colorIndex = 0
for graphName in self.graphs:
runningAverage = sum(self.graphs[graphName][-10:])//10
text = str(runningAverage)
self.canv.create_text(self.canvWidth-60, 20*(colorIndex+1), text = text,
fill = self.colors[colorIndex], anchor = W)
prev_xxx, prev_yyy = 0, 0
for yyy in self.graphs[graphName]:
self.canv.create_line(prev_xxx, prev_yyy, prev_xxx+self.density, self.canvHeight//2 - yyy,
width = 1.4, fill = self.colors[colorIndex])
prev_xxx, prev_yyy = prev_xxx+self.density, self.canvHeight//2 - yyy
colorIndex = colorIndex + 1
self.drawMesh()
def drawMesh(self):
self.canv.create_rectangle(3, 3, self.canvWidth,
self.canvHeight, outline = 'black', width = 2)
self.canv.create_line(0, self.canvHeight/2, self.canvWidth,
self.canvHeight/2, fill="black", width = 1)
mouseX = self.canv.winfo_pointerx() - self.canv.winfo_rootx()
mouseY = self.canv.winfo_pointery() - self.canv.winfo_rooty()
if mouseY < 60: aaa = -1
else: aaa = 1
if mouseX > self.canvWidth - 200 : bbb = -12
else: bbb = 1
try:
self.canv.create_rectangle(mouseX + 10*bbb - 5, mouseY - 20*aaa +10,
mouseX + 10*bbb + 115, mouseY - 20*aaa - 30, outline = "black",
fill = "red")
self.canv.create_text(mouseX + 10*bbb, mouseY - 40*aaa,
text = "t="+str(self.timings[mouseX//self.density]),
anchor = W)
self.canv.create_text(mouseX + 10*bbb, mouseY - 20*aaa,
text = "value="+str(self.canvHeight//2 - mouseY),
anchor = W)
except IndexError:
pass
self.canv.create_line(mouseX, 0, mouseX,
self.canvHeight, fill="blue", dash = [4, 1, 2, 1], width = 1)
self.canv.create_line(0, mouseY, self.canvWidth,
mouseY, fill="blue", dash = [4, 1, 2, 1], width = 1)
def periodicCall(self):
self.redraw()
self.after(100, func=self.periodicCall)
def workerThread(self):
while (1):
try:
if self.comport.isOpen() and (self.pauseFlag == TRUE):
comLine = self.comport.readline()
if len(self.timings) == self.numOfDots:
self.timings.pop(0)
td = datetime.now() - self.zeroTiming
## b'271;-3:-50\r\n'
parsedLine = re.search(self.messagePattern.get(), str(comLine))
index = 1
if parsedLine:
self.timings.append(td)
for graphName in self.graphs:
if len(self.graphs[graphName]) == self.numOfDots:
self.graphs[graphName].pop(0)
try:
self.graphs[graphName].append(int(parsedLine.group(index)))
except IndexError:
self.graphs[graphName].append(0)
index = index + 1
else:
self.comport.flush();
time.sleep(1)
except TclError:
self.thread._stop()
def main():
root = Tk()
mainWindow = Frame(root)
mainWindow.pack()
port = comPortWidget(mainWindow)
port.pack()
dv = dataVisualizer(mainWindow, port)
dv.pack()
root.mainloop()
if __name__ == '__main__':
main()
And the serial part - may lag as well (used to lag when I used to reenumerate ports evey second or so...)
#-------------------------------------------------------------------------------
# Name: robowidgets
# Purpose:
#
# Author: dccharacter
#
# Created: 10.03.2012
# Copyright: (c) dccharacter 2012
# Licence: <your licence>
#-------------------------------------------------------------------------------
#!/usr/bin/env python
import serial
from serial.tools.list_ports_windows import comports
from tkinter import *
from tkinter.ttk import *
class comPortWidget(LabelFrame, serial.Serial):
commonComPortSpeeds = ["1200", "2400", "4800", "9600", "14400", "19200", "38400", "57600", "115200"]
def __init__(self, master=None, cnf={}, **kw):
"""Construct a comPortWidget widget with the parent MASTER.
STANDARD OPTIONS
borderwidth, cursor, font, foreground,
highlightbackground, highlightcolor,
highlightthickness, padx, pady, relief,
takefocus, text, background, class, colormap, container,
height, labelanchor, labelwidget,
visual, width
WIDGET-SPECIFIC OPTIONS
"""
self.master = master
LabelFrame.__init__(self, master, text="Serial settings", *cnf, **kw)
serial.Serial.__init__(self)
self.parent = master
self.draw()
def draw(self):
self.strVarComPort = StringVar()
self.comboComport = Combobox(self,
textvariable=self.strVarComPort)
self.comboComport.grid(row=0, column=1)
self.labelComportName = Label(self, text="Com port:")
self.labelComportName.grid(row=0, column=0)
self.strVarComSpeed = StringVar()
self.comboComSpeed = Combobox(self,
textvariable=self.strVarComSpeed, values=self.commonComPortSpeeds)
self.comboComSpeed.current(len(self.commonComPortSpeeds)-1)
self.comboComSpeed.grid(row=1, column=1)
self.labelComSpeed = Label(self, text="Com speed:")
self.labelComSpeed.grid(row=1, column=0)
self.buttonComOpen = Button(self, text="Open port", command=self.openPort)
self.buttonComOpen.grid(row=0, column=2)
self.buttonComClose = Button(self, text="Close port", command=self.closePort)
self.buttonComClose.grid(row=1, column=2)
self.buttonRefreshPorts = Button(self, text="Re", width=3, command=self.refreshComPortsCombo)
##self.buttonRefreshPorts.grid(row=0, column=2)
self.refreshComPortsCombo()
def refreshComPortsCombo(self):
listComs = self.enumerateComPorts()
if not listComs:
listComs.append("No com ports found")
self.disableControls(~self.isOpen())
self.buttonComClose.configure(state=DISABLED)
else:
self.disableControls(self.isOpen())
self.buttonRefreshPorts.configure(state=NORMAL)
self.comboComport.config(values=listComs)
self.comboComport.current(len(listComs)-1)
##self.after(500, func=self.refreshComPortsCombo)
def enumerateComPorts(self):
"""
Returns the list ofcom port names in the system or an empty list if
no ports found
"""
listComs = []
for port, desc, hwid in sorted(comports()):
listComs.append(port)
return listComs
def openPort(self):
if self.isOpen():
return
self.port = self.comboComport.get()
self.baudrate = int(self.comboComSpeed.get())
self.timeout = 1
try:
self.open()
self.disableControls(self.isOpen())
except IOError:
pass
def closePort(self):
if self.isOpen():
self.flush()
self.close()
self.disableControls(self.isOpen())
def disableControls(self, isConnected):
if isConnected:
self.labelComportName.configure(state=DISABLED)
self.labelComSpeed.configure(state=DISABLED)
self.comboComport.configure(state=DISABLED)
self.comboComSpeed.configure(state=DISABLED)
self.buttonComClose.configure(state=NORMAL)
self.buttonComOpen.configure(state=DISABLED)
self.buttonRefreshPorts.configure(state=DISABLED)
else:
self.labelComportName.configure(state=NORMAL)
self.labelComSpeed.configure(state=NORMAL)
self.comboComport.configure(state=NORMAL)
self.comboComSpeed.configure(state=NORMAL)
self.buttonComClose.configure(state=DISABLED)
self.buttonComOpen.configure(state=NORMAL)
self.buttonRefreshPorts.configure(state=NORMAL)
def main():
pass
if __name__ == '__main__':
main()
UPDATE: I did as Brian advised. Now I have two screen redraw functions. Difference between them is that first moves all the lines to the left adding new to the right and deleting those that fall off the canvas. The second one moves lines to the left and re-deploys elements that fall off the canvas to the right (without creating new ones). There's a huge improvement with any of these in respect to my initial variant, but I don't see big difference between the two wit the naked eye - mayme if I had more elements I would. The latter though works better specifically for my application as I don't have to track those who fall off the cliff.
Here the functions:
def drawGraph(self): ###needed for self.updateGraph2() only as it is creates the lines
for graphNum in range(0, self.numOfGraphs):
self.graphLines.append([])
self.graphData.append([0,]*self.numOfDots)
for iii in range(0,self.numOfDots):
self.graphLines[graphNum].append(
self.canv.create_line(0,0,0,0,fill=self.colors[graphNum],
width=1.2, tags=('graphLines', 'graph'+str(graphNum)))
)
def updateGraph2(self):
while not self.queue.empty():
iTuple = self.queue.get()
self.canv.move('graphLines', -self.density,0)
for graphNum in range(0, self.numOfGraphs):
try: self.graphData[graphNum].append(iTuple[graphNum])
except IndexError:
self.graphData[graphNum].append(0)
self.graphData[graphNum].pop(0)
self.graphLines[graphNum].append(self.graphLines[graphNum].pop(0))
self.canv.coords(self.graphLines[graphNum][-1],
self.canv.winfo_width()-self.density,
int(int(self.graphData[graphNum][-2])+int(self.canv.winfo_height()//2)),
self.canv.winfo_width(),
int(int(self.graphData[graphNum][-1])+int(self.canv.winfo_height()//2))
)
def updateGraph(self):
while not self.queue.empty():
self.timingIndex = self.timingIndex + 1
self.canv.move('graphLines', -self.density, 0)
iTuple = self.queue.get()
for iii in range(0, len(iTuple)):
yyy = int(iTuple[iii])+self.canv.winfo_height()//2
if yyy < 0: yyy = 0
if yyy > self.canv.winfo_height(): yyy = self.canv.winfo_height()
prev_yyy = int(self.prevTuple[iii])+self.canv.winfo_height()//2
if prev_yyy < 0: prev_yyy = 0
if prev_yyy > self.canv.winfo_height(): prev_yyy = self.canv.winfo_height()
self.canv.create_line(
self.canv.winfo_width()-self.density, prev_yyy,
self.canv.winfo_width(), yyy,
width = 1.4, fill = self.colors[iii], tags=('graphLines','graph'+str(iii)))
self.prevTuple = iTuple
self.canv.addtag_overlapping('todelete',-1,-1,-3,self.canv.winfo_height()+1)
self.canv.dtag('preserve','todelete')
self.canv.delete('todelete')
My understanding of the canvas is that the more element ids that have been allocated, the slower it gets. It can handle tens of thousands without much problem (and maybe even 100's of thousands), but if you're creating and deleting 6000 items every 100ms, that is likely your problem. Even though you are deleting the items, it still affects performance especially when you are creating 60,000 per second.
Instead of deleting all items every 100ms, simply move the items off screen and remember them, then reuse them by using the coords method to change their coordinates for the new graph.

Resources