The goal of this program is to generate a sine wave using a 12 bit DAC. The code is as follows:
from Tkinter import *
import smbus, time, math, random
bus = smbus.SMBus(1)
address = 0x60
t=time.time()
class RPiRFSigGen:
# Build Graphical User Interface
def __init__(self, master):
self.start
frame = Frame(master, bd=10)
frame.pack(fill=BOTH,expand=1)
# set output frequency
frequencylabel = Label(frame, text='Frequency (Hz)', pady=10)
frequencylabel.grid(row=0, column=0)
self.frequency = StringVar()
frequencyentry = Entry(frame, textvariable=self.frequency, width=10)
frequencyentry.grid(row=0, column=1)
# Start button
startbutton = Button(frame, text='Enter', command=self.start)
startbutton.grid(row=1, column=0)
def start(self):
#self.low_freq=IntVar
low_freq = float(self.frequency.get())
out = 4095/2 + (math.sin(2*math.pi*low_freq*t))
#out = math.floor(out)
int(math.floor(out))
print (out)
bus.write_byte_data(address,0,out)
sendFrequency(low_freq)
# Assign TK to root
root = Tk()
# Set main window title
root.wm_title('DAC Controller')
root.geometry('250x150+650+250')
# Create instance of class RPiRFSigGen
app = RPiRFSigGen(root)
# Start main loop and wait for input from GUI
root.mainloop()
When I run the program I receive the following error after the value "out" is printed:
2046.18787764
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1437, in __call__
return self.func(*args)
File "/home/pi/DAC Controller.py", line 40, in start
bus.write_byte_data(address,0,out)
TypeError: integer argument expected, got float
It would appear that int(math.floor(out)) is not converting out to an integer because "out" is being printed as float still. Any suggestions?
int(math.floor(out))
This will create an integer version of out, but you aren't assigning it to anything, so it just gets discarded. If you want the changes to be reflected in the value of out, try:
out = int(math.floor(out))
Related
Part of a GUI program I'm building needs to be able to convert times given into seconds. The frame class in my GUI that does this is giving me some trouble. I created an instance variable of type combobox to hold the options for types of time period to convert. I bound the combobox selection to convert the input time into seconds. I want to tie entering values into the entry box to doing the same thing. I tried to call my conversion function in my validation command function for the entry box, but it's telling me that my frame object "PERIODIC" doesn't have an attribute "period_type". I'm confused because I named the combobox as an instance variable, and it should be accessible to everything in the class. "self.period_type" is right there in my init. Why can't I access this variable? Am I missing something painfully obvious?
The Traceback I'm getting is:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\4D_User\AppData\Local\Programs\Python\Python38-32\lib\tkinter\__init__.py", line 1883, in __call__
return self.func(*args)
File "C:/Users/PycharmProjects/LoggerProject/Scripts/ProblemExample.py", line 37, in ValidateTime
self.convert_time(self.period_type.get())
AttributeError: 'PERIODIC' object has no attribute 'period_type'<
This is my code:
from tkinter import ttk
import re
root = tk.Tk()
class PERIODIC(tk.Frame):
def __init__(self, container, **kwargs):
super().__init__(container, **kwargs)
self.time_unconverted = tk.DoubleVar()
self.time_converted = tk.DoubleVar()
self.periodic_label = tk.Label(self, text="PERIODIC")
self.periodic_label.grid(row=0, columnspan=2, sticky="NSEW")
trigger_option_label = ttk.Label(self, text="Trigger Every: ")
trigger_option_label.grid(row=1, column=0)
vcmd = (self.register(self.ValidateTime), '%P')
self.num_period = tk.Entry(self,textvariable=self.time_unconverted, validate="key", validatecommand=vcmd)
self.num_period.grid(row=1, column=1)
self.period_type = ttk.Combobox(self, values=["seconds", "minutes", "hours", "days"])
self.period_type.bind("<<ComboboxSelected>>", lambda y: self.convert_time(self.period_type.get()))
self.period_type.grid(row=1, column=2)
def convert_time(self, type):
if type == 'seconds':
self.time_converted.set(self.time_unconverted.get())
if type == 'minutes':
self.time_converted.set(self.time_unconverted.get() * 60)
if type == 'hours':
self.time_converted.set(self.time_unconverted.get() * 3600)
if type == 'days':
self.time_converted.set(self.time_unconverted.get() * 86400)
def ValidateTime(self, P):
test = re.compile('^[0-9]{1,3}?\.?[0-9]?$')
if test.match(P):
self.convert_time(self.period_type.get())
return True
else:
return False
frame = PERIODIC(root)
frame.grid(row=0,column=0)
root.mainloop()
Your validation command is being triggered when you create the entry, and it's using self.period_type which hasn't been defined yet.
The simple solution is to move the creation of the combobox prior to creating the entry.
I am constructing a simple hex editor in Python using Tkinter. I want to be able to indicate selection of a value (the "0000"s) in the GUI by changing the colors of a pressed button.
To implement this, I instantiated each button as a class inside a for loop and put each instance into a list to reference them later in the code. When I attempt to alter the Button API properties of the classes in the while loop, the IDLE prints an error when the hex editor is closed:
Traceback (most recent call last):
File "C:/Users/Administrator/Desktop/Files/Python/hex editor.py", line 64, in <module>
ml.button.configure(bg='#00FF00', fg='#000000')
File "C:\Users\Administrator\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py", line 1479, in configure
return self._configure('configure', cnf, kw)
File "C:\Users\Administrator\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py", line 1470, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: invalid command name ".!frame.!frame.!button"
The intended behavior of the button is that when any "0000" is clicked, the selected button will become green and remain so until another button is pressed. (i.e the pressed button will turn green and the previously selected button will turn black)
Source Code:
from tkinter import *
selected_i = 0
selected_j = 0
data_w = 16
address_w = 8
class mem_location:
def __init__(self, row, column, data):
self.row = row
self.column = column
self.data = data
self.button = Button(subframe,
text=self.data,
font=('Consolas',9),
bd=0,
command=self.select)
self.button.pack(side=LEFT)
self.button.configure(bg='#000000', fg='#00FF00')
def select(self):
selected_i = self.row
selected_j = self.column
root = Tk()
root.configure(bg="black")
root.title('Hex Editor')
frame = Frame(root,
padx=30,
pady=10,
bg='black')
frame.pack()
ml_list = []
for i in range((1<<address_w)>>4):
subframe = Frame(frame,
padx=10,
bg='black')
subframe.pack()
addr_label = Label(subframe,
text=hex(i<<4)+" ",
bg='black',
fg='#00FF00',
width=5)
addr_label.pack(side = LEFT)
for j in range(16):
ml_list.append(mem_location(i, j, '0000'))
root.mainloop()
while True:
for ml in ml_list:
if selected_i == ml.row and selected_j == ml.column:
ml.button.configure(bg='#00FF00', fg='#000000')
else:
ml.button.configure(bg='#000000', fg='#00FF00')
I am currently in the process of learning about Tkinter. Why can't I modify the class's Button configuration and how can this be fixed?
The code after root.mainloop will not run until the window is not closed, so you are trying to modify the buttons after the window has been closed.
Instead, you could move the code in the submit method like this:
from tkinter import *
# You don’t need selected_i and selected_j anymore
data_w = 16
address_w = 8
class MemLocation:
def __init__(self, data):
# Same code without self.row and self.column
def select(self):
for ml in [ml_ for ml_ in ml_list if ml_ is not self]: # All elements except this one
ml.button.config(bg='#000', fg='#0F0')
self.button.config(bg='#0F0', fg='#000')
# Same code here
root.mainloop()
#END
Note that I renamed the class according to PEP 8 but there are many other improvement possible like making MemLocation inherit from Frame or Button, or adding event listeners to make the code cleaner.
I am trying to bind this function self.copyTextToClipboard(self,t) to multiple different trees to make it more flexible (please see binding below).
from tkinter.ttk import Treeview
from tkinter import *
class App:
def __init__(self, master):
self.master = master
frame = Frame(master)
master.geometry("{}x{}".format(master.winfo_screenwidth() - 100, master.winfo_screenheight() - 100))
master.resizable(False, False)
self.leftFrame = Frame(master, bg="#DADADA", width=375, relief=SUNKEN)
self.leftFrame.pack_propagate(0)
self.leftFrame.pack(side=LEFT, fill=Y, padx=1)
# This table (TreeView) will display the partitions in the tab
self.partitionsOpenDiskTree = Treeview(self.leftFrame, columns=("#"), show="headings", selectmode="browse", height=23)
yscrollB = Scrollbar(self.leftFrame)
yscrollB.pack(side=RIGHT, fill=Y)
self.partitionsOpenDiskTree.column("#", width=50)
self.partitionsOpenDiskTree.heading("#", text="#")
self.partitionsOpenDiskTree.configure(yscrollcommand=yscrollB.set)
# Bind left click on text widget to copy_text_to_clipboard() function
self.partitionsOpenDiskTree.bind("<ButtonRelease-1>", lambda t=self.partitionsOpenDiskTree: self.copyTextToClipboard(self,t))
# Adding the entries to the TreeView
for i in range(3):
self.partitionsOpenDiskTree.insert("", "end", i, values=(i), tags=str(i))
self.partitionsOpenDiskTree.pack(anchor=NW, fill=Y)
#todo: figure out where this is getting called and put in tree
def copyTextToClipboard(self, tree, event=None):
print(type(tree))
# triggered off left button click on text_field
root.clipboard_clear() # clear clipboard contents
textList = tree.item(tree.focus())["values"]
line = ""
for text in textList:
if line != "":
line += ", " + str(text)
else:
line += str(text)
root.clipboard_append(line) # append new value to clipbaord
root = Tk()
app = App(root)
root.mainloop()
However, I am unable to bind it to a TreeView object it seems; when I run the code, I get:
Exception in Tkinter callback
<class '__main__.App'>
Traceback (most recent call last):
File "C:\Users\user1\Anaconda3\lib\tkinter\__init__.py", line 1699, in __call__
return self.func(*args)
File "C:/Users/user1/main_merged.py", line 56, in <lambda>
lambda t=self.partitionsOpenDiskTree: self.copyTextToClipboard(self,t))
File "C:/Users/user1/main_merged.py", line 70, in copyTextToClipboard
textList = tree.item(tree.focus())["values"]
AttributeError: 'App' object has no attribute 'item'
If I try to print out tree type, I get that it's a not a TreeView object. Any ideas on how I can get a TreeView object, so that I can figure out which item was selected?
Thanks!
-FF
So, apparently, taking out the self call seemed to work:
from tkinter.ttk import Treeview
from tkinter import *
class App:
def __init__(self, master):
self.master = master
frame = Frame(master)
master.geometry("{}x{}".format(master.winfo_screenwidth() - 100, master.winfo_screenheight() - 100))
master.resizable(False, False)
self.leftFrame = Frame(master, bg="#DADADA", width=375, relief=SUNKEN)
self.leftFrame.pack_propagate(0)
self.leftFrame.pack(side=LEFT, fill=Y, padx=1)
# This table (TreeView) will display the partitions in the tab
self.partitionsOpenDiskTree = Treeview(self.leftFrame, columns=("#"), show="headings", selectmode="browse", height=23)
yscrollB = Scrollbar(self.leftFrame)
yscrollB.pack(side=RIGHT, fill=Y)
self.partitionsOpenDiskTree.column("#", width=50)
self.partitionsOpenDiskTree.heading("#", text="#")
self.partitionsOpenDiskTree.configure(yscrollcommand=yscrollB.set)
# Bind left click on text widget to copy_text_to_clipboard() function
self.partitionsOpenDiskTree.bind("<ButtonRelease-1>", lambda event, t=self.partitionsOpenDiskTree: self.copyTextToClipboard(t))
# Adding the entries to the TreeView
for i in range(3):
self.partitionsOpenDiskTree.insert("", "end", i, values=(i), tags=str(i))
self.partitionsOpenDiskTree.pack(anchor=NW, fill=Y)
#todo: figure out where this is getting called and put in tree
def copyTextToClipboard(self, tree, event=None):
print(type(tree))
# print(type(tree.partitionsOpenDiskTree))
# triggered off left button click on text_field
root.clipboard_clear() # clear clipboard contents
textList = tree.item(tree.focus())["values"]
line = ""
for text in textList:
if line != "":
line += ", " + str(text)
else:
line += str(text)
root.clipboard_append(line) # append new value to clipbaord
print(line)
root = Tk()
app = App(root)
root.mainloop()
Output:
0
When you use bind, the callback function must have an event as its first argument, custom arguments should be put after. But as your callback does not need the event parameters, you may mask it with your lambda. So you have to change both the binding and the def of your callback:
self.partitionsOpenDiskTree.bind("<ButtonRelease-1>", lambda event, t=self.partitionsOpenDiskTree: self.copyTextToClipboard(t))
...
def copyTextToClipboard(self, tree):
should solve the problem
I've been trying to build a simple serial monitor based on this code:
https://aboesch.wordpress.com/2013/04/28/python-code-gui-for-controlling-serial-port-and-live-monitoring/
It's utilizing threading which i've head does not always play nice with Tkinter. I'm new to this and trying to do something pretty basic (send serial back and forth communicate between an RPi and an Arduino). Yesterday this exact code was running with no errors
Exception in thread Updating:
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner
self.run()
File "/home/pi/sendstring_NEWTEST.py", line 21, in run
UpdateC.set(self.update_count)
File "/home/pi/sendstring_NEWTEST.py", line 37, in run
c_var.set(self.request_dict["c"])
RuntimeError: main thread is not in main loop
I'll paste my code below, it still sends properly, but it will only read once or twice if at all. (And for whatever confusing reason, the same code ran all day yesteday with no error). I understand that there's an issue with running your GUI outside of your main thread, but i'm not knowledgeable enough to understand how to fix it. I'm looking for some direction, preferably some assistance in modifying this current code, but open to new directions. I've looked into queue's a bit, i'm just starting out using serial and am looking for a stable way to read and write strings. Thanks for reading
import Tkinter as tk # for the GUI
import ttk # for nicer GUI widgets
import tkMessageBox # for GUI testbox
import serial # for communication with serial port
import time # for time stuff
import threading # for parallel computing
class myThread (threading.Thread):
def __init__(self, name, ser):
threading.Thread.__init__(self)
self.name = name
self.ser = ser
self.request_dict = {"c": ''}
# gets called when thread is started with .start()
def run(self):
self.update_count = 0
while self.ser.isOpen():
self.update_count += 1
# UpdateC.set(self.update_count)
for request in self.request_dict:
try:
# self.ser.write(request)
time.sleep(0.1)
# create string for the answer
self.request_dict[request] = ''
# as long as an answer byte is waiting, read the byte
while self.ser.inWaiting() > 0:
self.request_dict[request] += self.ser.read(1)
except:
# do nothing if command could not be send
pass
# set the label variables with the answeres receiced
c_var.set(self.request_dict["c"])
time.sleep(1)
# sending commands to the MWG
def mSend(command):
try:
ser.write(command)
except:
print "Could not send command. Port closed?"
# clear all entry widgets on the GUI
C_sendvar.set('')
return
# ===========================
# Begin of the main program
# ===========================
ser = serial.Serial()
ser.port = "/dev/ttyUSB0"
ser.baudrate = 9600
ser.timeout = 1
if ser.isOpen() == False:
ser.open()
root = tk.Tk()
root.geometry("600x500")
root.title("SERIAL MONITOR")
c_var = tk.StringVar()
C_sendvar = tk.StringVar()
#UpdateC = tk.StringVar()
mHeader = ttk.Label(root, text = "").grid(row=0, column=2)
mStatus = ttk.Label(root, text = "Serial port open: "+str(ser.isOpen())).grid(row=1, column=2)
mSeperate = ttk.Label(root, text = "").grid(row=2, column=1)
mSubhead_l = ttk.Label(root, text = "Receive Protocol:").grid(row=6, column=1)
mSeperate2 = ttk.Label(root, text = "").grid(row=5, column=1)
c_Show = ttk.Label(root, textvariable = c_var).grid(row=6, column=2)
c_Entry = ttk.Entry(root, textvariable = C_sendvar).grid(row=4, column=2)
c_Button = ttk.Button(root, text ="Send Protocol", command = lambda: mSend(C_sendvar.get())).grid(row=4, column=1)
time.sleep(1)
# call and start update-thread
thread1 = myThread("Updating", ser)
thread1.start()
# start GUI
root.mainloop()
I'm very new to Python, sort of following Dive into Python 2 and wanted to dabble with some Tkinter programming. I've tried to make a little program that takes 3 sets of words and makes combinations of each word in the 3 sets to make keywords for websites. When I run the script, the GUI appears as expected, but I get the following error when I click on the Create Combinations button
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python2.6/lib-tk/Tkinter.py", line 1413, in __call__
return self.func(*args)
File "combomaker.py", line 34, in makeCombinations
primaryraw = primaryKeyWordsBox.get()
AttributeError: 'NoneType' object has no attribute 'get'
The code I'm trying to fix
#!/usr/bin/env python
from Tkinter import *
primaryKeyWordsLabel = None
primaryKeyWordsBox = None
secondaryKeyWordsLabel = None
secondaryKeyWordsBox = None
tertiaryKeyWordsLabel = None
tertiaryKeyWordsBox = None
class Application(Frame):
def __init__(self, master=None, padx = 10, pady= 10):
Frame.__init__(self, master)
self.grid()
self.createWidgets()
def createWidgets(self):
self.primaryKeyWordsLabel = LabelFrame(text="Primary Key Words", padx=10, pady=10)
self.primaryKeyWordsLabel.grid()
self.primaryKeyWordsBox = Text(primaryKeyWordsLabel, autoseparators=True, height=5, undo=True)
self.primaryKeyWordsBox.grid()
self.secondaryKeyWordsLabel = LabelFrame(text="Secondary Key Words", padx=10, pady=10)
self.secondaryKeyWordsLabel.grid()
self.secondaryKeyWordsBox = Text(secondaryKeyWordsLabel, autoseparators=True, height=5, undo=True)
self.secondaryKeyWordsBox.grid()
self.tertiaryKeyWordsLabel = LabelFrame(text="Tertiary Key Words", padx=10, pady=10)
self.tertiaryKeyWordsLabel.grid()
self.tertiaryKeyWordsBox = Text(tertiaryKeyWordsLabel, autoseparators=True, height=5, undo=True)
self.tertiaryKeyWordsBox.grid()
self.goButton = Button(text="Create Combinations", command=makeCombinations)
self.goButton.grid()
def makeCombinations():
primaryraw = primaryKeyWordsBox.get()
primary = primaryraw.split(', ')
secondaryraw = secondaryKeyWordsBox.get()
secondary = secondaryraw.split(', ')
tertiaryraw = tertiaryKeyWordsBox.get()
tertiary = tertiaryraw.split(', ')
output=[]
filename = "output.txt"
for i in range(len(primary)):
for j in range(len(secondary)):
for k in range(len(tertiary)):
rawcombo=str(primary[i])+" "+str(secondary[j])+" "+str(tertiary[k])
output.append(rawcombo)
FILE = open(filename, w)
for combo in output:
FILE.write(combo+",\n")
FILE.close()
app = Application()
app.master.title("Keyword Generator")
app.mainloop()
I may have thrown myself into GUI programming too fast, this is my first attempt at any GUI work but not my first time programming.
Many thanks in advance :)
You're trying to access
primaryKeyWordsBox
outside the class Application in the (free) function makeCombinations(..).
You could make makeCombinations(..) a member of Application by indenting it like the other member functions and add the self argument:
def makeCombinations(self):
You should modify the binding of the makeCombinations(..) to the button:
...,command = self.makeCombinations)
Then you'll have to add self. when you're trying to access the members of this class:
primaryraw = self.primaryKeyWordsBox.get(1.0,END)
...
secondaryraw = self.secondaryKeyWordsBox.get(1.0,END)
...
tertiaryraw = self.tertiaryKeyWordsBox.get(1.0,END)
(I found the examples how to use get here).
If you want to open a file for writing, you should do:
FILE = open(filename, "w")
instead of
FILE = open(filename, w)