Python 2.7 Tkinter Change Label text on button event - user-interface

Im very new to Python(2.7) im learning GUI design(Tkinter) and keep running into different syntax/no call method/global name not defined errors when trying to implement a simple label text change from a Entry object on button click. Can someone show me the correct syntax for the action
from Tkinter import *
class Part3:
def __init__(self, parent):
GUIFrame =Frame(parent,width= 300, height=200)
GUIFrame.pack(expand = False, anchor = CENTER)
entry = Entry(text="enter your choice")
entry.place(x=65, y = 10)
self.test = StringVar()
self.test.set('''Hi, I'm a Label :)''')
self.Label1 = Label(parent, textvariable = self.test)
self.Label1.place(x = 85, y = 100)
self.Button2 = Button(parent, text='edit',command=self.LabelChange)
self.Button2.place(x= 80, y = 60)
self.Button3 = Button(parent, text='exit', command= parent.quit)
self.Button3.place(x= 160, y = 60)
def LabelChange(self):
test = self.entry.get()
self.Label1(test)
root = Tk()
MainFrame =Part3(root)
root.title('Input Test')
root.mainloop()
The Idea being on the 'edit' (button2) click, the text of Label1 is changed to the text of entry.

Try:
self.entry = Entry(text="enter your choice")
...
test = self.entry.get()
self.test.set(test)
I know most tutorials give examples using textvariables, but in most cases you don't need them. You can get and set the values in the widget without using textvariable. Textvariables are mostly useful for doing traces on variables. Variable traces are a somewhat advanced technique that you will rarely need.

from Tkinter import *
class Part3:
def __init__(self, parent):
GUIFrame =Frame(parent,width= 300, height=200)
GUIFrame.pack(expand = False, anchor = CENTER)
self.entry = Entry(text="enter your choice") # this needs to be in self
self.entry.place(x=65, y = 10)
self.test = StringVar()
self.test.set('''Hi, I'm a Label :)''')
self.Label1 = Label(parent, textvariable = self.test)
self.Label1.place(x = 85, y = 100)
self.Button2 = Button(parent, text='edit',command=self.LabelChange)
self.Button2.place(x= 80, y = 60)
self.Button3 = Button(parent, text='exit', command= parent.quit)
self.Button3.place(x= 160, y = 60)
def LabelChange(self):
self.test.set(self.entry.get())
root = Tk()
MainFrame =Part3(root)
root.title('Input Test')
root.mainloop()
root.destroy()

Use can use a .after command. For example:
Lbl = Label(text='Hi')
def change():
Lbl.after(3000, lambda: Lbl.config(text="hola")
# Or you can use the one below to remove delay.
Lbl.config(text='hola')
return change
Btn = Button(command=change ())
Lbl.pack()
Btn.pack()

Related

Adding Multiple PhotoImage to TKinter Canvas

I'm running into a problem that makes me feel like I've been beating my head against a wall. I'm trying to get multiple (2 for starters) photoimages to be added to a tkinter canvas. Here is the code below:
def build_frame(screenHeight, screenWidth):
canvas = Canvas(root, bg = 'blue', height = screenHeight, width = screenWidth)
player = create_random_player()
enemy = create_random_enemy()
display_player_image(canvas, player)
display_enemy_image(canvas, enemy)
bind_all_keys(canvas, player)
canvas.pack()
def display_player_image(canvas, player):
tkImage = ImageTk.PhotoImage(Image.open(player.playerImgPath))
player.playerId = canvas.create_image(player.playerPositionX, player.playerPositionY, image = tkImage, anchor = NE)
player.playerImg = tkImage
def display_enemy_image(canvas, enemy):
tkImage = ImageTk.PhotoImage(Image.open(enemy.enemyImgPath))
enemy.enemyId = canvas.create_image(enemy.enemyPositionX, enemy.enemyPositionY, image = tkImage, anchor = NE)
enemy.enemyImg = tkImage
def create_random_player():
return Player(0, randint(0,9), randint(0,9), randint(0,9), PLAYER_IMAGE_PATH, 50, 0)
def create_random_enemy():
return Enemy(0, randint(0,9), randint(0,9), randint(0,9), ENEMY_IMAGE_PATH, 150, 150)
I've made sure that I store a reference to the image and I know that the images aren't at the same location but I can't understand why the second one isn't appearing. I've even put in print statements to check to make sure the image is being created and it is at the given coordinates along with an ID that I can use to identify it.

wxPython: Getting a EVT_KEY_DOWN event for CMD+[other key] on OSX

I'm trying to catch an event when the user presses COMMAND + [any other key] on OSX. Since these are actually two key presses I expect two events: One when COMMAND is pressed and one when the other key is pressed (without releasing the COMMAND key). This works fine for every modifier except COMMAND where I only get the first event. Why is that and how can I fix it?
Version: wxPython3.0-osx-cocoa-py2.7
Example code:
import wx
def OnKeyDown(e):
print "Modifiers: {} Key Code: {}".format(e.GetModifiers(), e.GetKeyCode())
app = wx.App()
frame = wx.Frame(None)
textctrl = wx.TextCtrl(frame, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.WANTS_CHARS)
textctrl.Bind(wx.EVT_KEY_DOWN, OnKeyDown)
frame.Show()
app.MainLoop()
For ALT + d the output is:
Modifiers: 1 Key Code: 307
Modifiers: 1 Key Code: 68
For SHIFT + d the output is:
Modifiers: 4 Key Code: 306
Modifiers: 4 Key Code: 68
Only for COMMAND + d the output is:
Modifiers: 2 Key Code: 308
Thanks for your help
Additional Information: I'm using OSX 10.8 on a virtual machine. As RobinDunn points out that it works on his laptop. So chances are that this is just a problem in my environment. wnnmaw provided a good workaround which works for me even on the virtual environment.
Alright, so it took me some time, but here's a working code block that does what you want.
Some Things to Note:
I did this on Windows (so there are some changes you can make such as adding support for wx.ACCEL_RAW_CTRL which corresponds to the actual control key on Mac whereas wx.ACCCEL_CTRL corresponds to the command key
This code definitely can (and needs to be) cleaned up
You'll have to add better error checking before you give this to a user
Whithout further adieu, here it is
import wx
from wx.lib.scrolledpanel import ScrolledPanel
class TestWindow(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Accelerator Table Test', size=(600, 400))
self.panel = ScrolledPanel(parent=self, id=-1)
self.panel.SetupScrolling()
#Create IDs to be used in initial acclerator table
self.functionList = [self.func1, self.func2, self.func3, self.func4, self.func5, self.OnAdd, self.OnDel]
self.functionListstr = ["self.func1", "self.func2", "self.func3", "self.func4", "self.func5", "self.OnAdd", "self.OnDel"]
self.IDDict = {i: wx.NewId() for i in range(len(self.functionList))}
self.IDDictrev = {val:key for key, val in self.IDDict.iteritems()}
self.aTableList = [(wx.ACCEL_ALT, ord('S'), self.IDDict[0]),(wx.ACCEL_CTRL, ord('Q'), self.IDDict[1])]
#Set up initial accelerator table
aTable = wx.AcceleratorTable(self.aTableList)
self.SetAcceleratorTable(aTable)
#Bind inital accelerator table IDs to functions
for i in range(len(self.functionList)):
self.Bind(wx.EVT_MENU, self.functionList[i], id=self.IDDict[i])
#Set up control widgets on GUI
self.flexgrid = wx.FlexGridSizer(cols=3, hgap=10, vgap=5)
cmdkeylbl = wx.StaticText(self.panel, -1, "Command Key")
self.cmdkey = wx.ComboBox(self.panel, style=wx.CB_READONLY)
cmdkeylist = ["Alt", "Control/Command", "Shift", "OSX Control", "None"]
self.cmdkeyconstants = {"Alt":wx.ACCEL_ALT, "Control/Command":wx.ACCEL_CTRL, "Shift":wx.ACCEL_SHIFT, "None":wx.ACCEL_NORMAL}
self.cmdkeyconstantsrev = {val:key for key, val in self.cmdkeyconstants.iteritems()}
self.cmdkey.SetItems(cmdkeylist)
hotkeylbl = wx.StaticText(self.panel, -1, "HotKey (single letter only)")
self.hotkey = wx.TextCtrl(self.panel, size=(50,-1))
funclbl = wx.StaticText(self.panel, -1, "Function")
self.func = wx.ComboBox(self.panel, style=wx.CB_READONLY)
self.func.SetItems(self.functionListstr)
self.hbox = wx.BoxSizer(wx.HORIZONTAL)
addBtn = wx.Button(self.panel, -1, "Add")
delBtn = wx.Button(self.panel, -1, "Delete")
self.Bind(wx.EVT_BUTTON, self.OnAdd, addBtn)
self.Bind(wx.EVT_BUTTON, self.OnDel, delBtn)
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.curATable = wx.StaticText(self.panel, -1, self._DisplayATable())
self.flexgrid.Add(cmdkeylbl)
self.flexgrid.Add(hotkeylbl)
self.flexgrid.Add(funclbl)
self.flexgrid.Add(self.cmdkey)
self.flexgrid.Add(self.hotkey)
self.flexgrid.Add(self.func)
self.hbox.Add((20, 20), 0)
self.hbox.Add(addBtn)
self.hbox.Add((0, 0), 0)
self.hbox.Add(delBtn)
self.hbox.Add((20, 20), 0)
self.vbox.Add(self.flexgrid, flag=wx.TOP|wx.BOTTOM|wx.LEFT|wx.RIGHT|wx.EXPAND, border = 5)
self.vbox.Add(self.hbox, flag=wx.TOP|wx.BOTTOM|wx.LEFT|wx.RIGHT|wx.EXPAND, border = 5)
self.vbox.Add(self.curATable, flag=wx.TOP|wx.BOTTOM|wx.LEFT|wx.RIGHT|wx.EXPAND, border = 5)
self.panel.SetSizer(self.vbox)
self.panel.Layout()
#Class Functions
def _DisplayATable(self):
aTablelbl = ""
for cmdKey, hotKey, Func in self.aTableList:
aTablelbl += "{} + {} calls {}\n".format(self.cmdkeyconstantsrev[cmdKey], chr(hotKey), self.functionListstr[self.IDDictrev[Func]])
return aTablelbl
def OnAdd(self, event):
self.aTableList.append((self.cmdkeyconstants[self.cmdkey.GetValue()], ord(self.hotkey.GetValue()[:1].title()), self.IDDict[self.func.GetSelection()]))
print "Added {} + {} as a shortcut for {}!".format(self.cmdkey.GetValue(), self.hotkey.GetValue()[:1].title(), self.functionListstr[self.func.GetSelection()])
aTable = wx.AcceleratorTable(self.aTableList)
self.SetAcceleratorTable(aTable)
self.curATable.SetLabel(self._DisplayATable())
self.panel.Layout()
def OnDel(self, event):
if (self.cmdkeyconstants[self.cmdkey.GetValue()], ord(self.hotkey.GetValue()[:1].title()), self.IDDict[self.func.GetSelection()]) in self.aTableList:
self.aTableList.remove((self.cmdkeyconstants[self.cmdkey.GetValue()], ord(self.hotkey.GetValue()[:1].title()), self.IDDict[self.func.GetSelection()]))
aTable = wx.AcceleratorTable(self.aTableList)
self.SetAcceleratorTable(aTable)
self.curATable.SetLabel(self._DisplayATable())
self.panel.Layout()
else:
dlg = wx.MessageDialog(self, "ERROR: That combination is not in the accelerator table!", "Error", style=wx.OK)
dlg.ShowModal()
dlg.Destroy()
def func1(self, event):
dlg = wx.MessageDialog(self, "Func1", "Func1", style=wx.OK)
dlg.ShowModal()
dlg.Destroy()
def func2(self, event):
dlg = wx.MessageDialog(self, "Func2", "Func2", style=wx.OK)
dlg.ShowModal()
dlg.Destroy()
def func3(self, event):
dlg = wx.MessageDialog(self, "Func3", "Func3", style=wx.OK)
dlg.ShowModal()
dlg.Destroy()
def func4(self, event):
dlg = wx.MessageDialog(self, "Func4", "Func4", style=wx.OK)
dlg.ShowModal()
dlg.Destroy()
def func5(self, event):
dlg = wx.MessageDialog(self, "Func5", "Func5", style=wx.OK)
dlg.ShowModal()
dlg.Destroy()
app = wx.App(False)
frame = TestWindow()
frame.Show()
app.MainLoop()
Hopefully this is what you're looking for, let me know if you have any questions

Works on Fedora but not on Windows, wx.Phyton

Well im quite a noob with wx and i started learning it 5 days ago. I'm trying to make a game like memory with cards like bitmap buttons but events don't want to bind on my cards. I searched the Internet and asked some people for help but they don't know why. I sent the program to one person who works in Linux Fedora and he says it works...
The problem is in class MyDialog, function Cards. I made a test program, similar to this one and binded the events in the for command where it worked properly.
Sorry if the answer exists somewhere on this website, I couldn't find it...
import random
import wx
global n
global ControlVar
ControlVar = False
class MyDialog(wx.Dialog):
def __init__(self, parent, id, title):
wx.Dialog.__init__(self, parent, id, title, size=(200, 150))
wx.StaticBox(self, -1, 'Card pairs', (5, 5), size=(180, 70))
wx.StaticText(self, -1, 'Number: ', (15, 40))
self.spin = wx.SpinCtrl(self, -1, '1', (65, 40), (60, -1), min=3, max=5)
self.spin.SetValue(4)
wx.Button(self, 2, 'Ok', (70, 85), (60, -1))
self.Bind(wx.EVT_BUTTON, self.OnClose, id=2)
self.Centre()
self.ShowModal()
self.Destroy()
def OnClose(self, event):
pair = self.spin.GetValue()
self.Close()
return(pair)
class MyMenu(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(1000, 700))
self.SetMinSize(wx.Size(400, 300))
self.panel = wx.Panel(self, wx.ID_ANY)
self.SetIcon(wx.Icon('computer.png', wx.BITMAP_TYPE_ANY))
bmp = wx.Image('wood.png', wx.BITMAP_TYPE_ANY).ConvertToBitmap()
bitmap = wx.StaticBitmap(self, -1, bmp, (0, 0))
menubar = wx.MenuBar()
file = wx.Menu()
edit = wx.Menu()
file.Append(101, '&New Game', 'Start a New Game')
file.AppendSeparator()
file.Append(105,'&Quit\tEsc', 'Quit the Application')
menubar.Append(file, '&File')
self.SetMenuBar(menubar)
self.statusbar = self.CreateStatusBar()
self.Centre()
self.Bind(wx.EVT_MENU, self.OnNew, id=101)
self.Bind(wx.EVT_MENU, self.OnQuit, id=105)
self.panel.Bind(wx.EVT_KEY_DOWN, self.OnKey)
def OnNew(self, event):
if ControlVar:
for i in range(n*2):
self.dugmad[i].Destroy()
md = MyDialog(None, -1, 'New Game')
n = md.OnClose(None)
self.statusbar.SetStatusText('You Selected {} Pairs.'.format(n))
self.Cards()
def OnButton(self, event):
print('ANYTHING PLEASE!')
## problem ahead!
def Cards(self):
image = wx.Image('cveteki.jpg', wx.BITMAP_TYPE_ANY).ConvertToBitmap()
self.dugmad = []
for i in range(2*n):
dugme = wx.BitmapButton(self, i, image)
self.dugmad.append(dugme)
self.Bind(wx.EVT_BUTTON, self.OnButton, id=i)
if n == 3:
self.Draw(2, 3)
if n == 4:
self.Draw(2, 4)
if n == 5:
self.Draw(2, 5)
def Draw(self,a, b):
gs = wx.GridSizer(a,b,40,40)
for i in range(n*2):
gs.Add(self.dugmad[i],0, wx.EXPAND)
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(gs, 1, wx.EXPAND | wx.ALL, 40)
self.SetSizer(vbox)
self.Layout()
self.Refresh()
global ControlVar
ControlVar=True
def OnKey(self, event):
keycode = event.GetKeyCode()
if keycode == wx.WXK_ESCAPE:
box = wx.MessageDialog(None, 'Are you sure you want to quit?', 'Quit', wx.YES_NO | wx.ICON_QUESTION)
if box.ShowModal() == wx.ID_YES:
self.Close()
def OnQuit(self, event):
box = wx.MessageDialog(None, 'Are you sure you want to quit?', 'Quit', wx.YES_NO | wx.ICON_QUESTION)
if box.ShowModal() == wx.ID_YES:
self.Destroy()
class MyApp(wx.App):
def OnInit(self):
frame = MyMenu(None, -1, 'Memory')
frame.Show(True)
return (True)
def main():
app = MyApp(False)
app.MainLoop()
main()
I tried to run your code but I don't have images with those names at the ready, and I can't understand all your globals, and I get an error about n not defined. So I made a simple test for you which I hope helps:
import wx
app = wx.App()
def onButton(evt):
print "button pressed!", evt.GetEventObject().GetLabel()
frm = wx.Frame(None)
for i in range(10):
but = wx.Button(frm, pos=(10, i*20), label="button %s" % i)
but.Bind(wx.EVT_BUTTON, onButton)
frm.Show()
app.MainLoop()
The but.Bind(...) could also be frm.Bind(...) if you really want. Note that I don't futz with the id's: I couldn't care less what id's wxPython assigned the buttons.
I'm not sure what's wrong with your code because I couldn't run it and didn't want to debug the other errors with it.
Again, I hope this helps.
But why are you destroying your MyDialog just after it is created? Check: there is self.Destroy() method call immediately after self.ShowModal().

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.

PyGTK Change Window Dimensions from Two Entry's

It's pretty simple what I'm trying to accomplish here.
Say you put down 320 in the left textbox. That means the width of the window will be 320px. Same applies for height, except for the right textbox.
However when I debug I get this error.
Traceback (most recent call last):
File "./app.py", line 37, in change_size
self.win.set_size_request(width,height)
TypeError: an integer is required
Here's the code.
#!/usr/bin/env python
import gtk
class app:
def __init__(self):
self.win = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.win.set_title("Change Dimensions")
self.win.set_default_size(235, 60)
self.win.connect("destroy", gtk.main_quit)
vbox = gtk.VBox(spacing=4)
hbox = gtk.HBox(spacing=4)
self.entry = gtk.Entry()
self.entry2 = gtk.Entry()
hbox.pack_start(self.entry)
hbox.pack_start(self.entry2)
hbox2 = gtk.HBox(spacing=4)
ok = gtk.Button("OK")
ok.connect("clicked", self.change_size)
hbox2.pack_start(ok)
exit = gtk.Button("Exit")
exit.connect("clicked", gtk.main_quit)
hbox2.pack_start(exit)
vbox.pack_start(hbox)
vbox.pack_start(hbox2)
self.win.add(vbox)
self.win.show_all()
def change_size(self, w):
width = self.entry.get_text()
height = self.entry2.get_text()
self.win.set_size_request(width,height)
app()
gtk.main()
That's because get_text returns a string, but set_size_request requires integers. Simple fix:
width = int(self.entry.get_text())
height = int(self.entry2.get_text())

Resources