Text widgets as List - python

I'm trying to create tabs with Text using tkinter Notebook widget and Text widget.
I have created list of tabs and Text widgets everything going as far as good except add_tabs method. Whenever I press control-n for adding new tabs only for first time i got this exception:
I have no idea how can i fix this problem thanks for helping me.
Thank you.
C:\Users\Imtiyaz\Desktop>python maintabtest.py
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python34\lib\tkinter\__init__.py", line 1482, in __call__
return self.func(*args)
File "maintabtest.py", line 32, in add_tabs
self.nb.add(self._tabs[self.i],text="untitled")
IndexError: list index out of range
code:
import tkinter.ttk as ttks
from tkinter import BOTH,LEFT
class mainbody:
def __init__(self,master):
self.master = master
self._tabs = []
self._text = []
self.i = 1
self.body = ttks.Frame(self.master)
self.nb = ttks.Notebook(self.master)
self.nb.pack(fill=BOTH,expand=1)
self.body.pack(fill=BOTH,expand=1)
self.initial_tab = ttks.Frame(self.nb)
self.Inittext = ttks.tkinter.Text(self.initial_tab)
self.Inittext.pack(fill=BOTH,expand=1)
self.initial_tab.pack(fill=BOTH,expand=1)
self._text.append()
self.nb.add(self.initial_tab,text="first_tab")
self.File_name = ttks.Entry(self.master)
self.File_name.pack(side=LEFT)
self.sbtn = ttks.Button(self.master,text="save_btn",command=lambda:self.save_())
self.sbtn.pack(side=LEFT)
self.master.bind('<Control-n>',self.add_tabs)
def add_tabs(self,event):
self._tabs.append(ttks.Frame())
self.nb.add(self._tabs[self.i],text="untitled")
self._text.append(ttks.tkinter.Text(self._tabs[self.i]))
self._text[self.i].pack(fill=BOTH,expand=1)
self.i = self.i + 1
def save_(self):
self.fname = self.File_name.get()
self._txt_id = self.nb.index('current')
self.get_input = self._text[self._txt_id].get("1.0","end-1c")
with open(self.fname,'w') as f:
f.write(self.get_input)
if __name__ == "__main__":
root = ttks.tkinter.Tk()
mainbody(root)
root.mainloop()

Your problem is that self.i did not synchronize with the size of the 2 lists: self._tabs and self._text.
Basically you don't need the self.i to track the index to the last item of the 2 lists. Just use -1 instead of self.i in add_tabs() to refer the last item in the list as below:
import tkinter.ttk as ttks
from tkinter import BOTH,LEFT
from tkinter.messagebox import showinfo
class mainbody:
def __init__(self,master):
self.master = master
self._tabs = []
self._text = []
self.body = ttks.Frame(self.master)
self.nb = ttks.Notebook(self.master)
self.nb.pack(fill=BOTH, expand=1)
self.body.pack(fill=BOTH, expand=1)
self.add_tabs("first_tab") # add the initial tab
self.File_name = ttks.Entry(self.master)
self.File_name.pack(side=LEFT)
self.sbtn = ttks.Button(self.master, text="save_btn", command=self.save_file)
self.sbtn.pack(side=LEFT)
self.master.bind('<Control-n>', lambda e:self.add_tabs())
def add_tabs(self, name="untitled"):
self._tabs.append(ttks.Frame())
self.nb.add(self._tabs[-1], text=name)
self._text.append(ttks.tkinter.Text(self._tabs[-1]))
self._text[-1].pack(fill=BOTH, expand=1)
def save_file(self):
self.fname = self.File_name.get().strip()
if not self.fname == '':
self._txt_id = self.nb.index('current')
self.get_input = self._text[self._txt_id].get("1.0","end-1c")
with open(self.fname, 'w') as f:
f.write(self.get_input)
else:
showinfo('Warning', 'Please input filename')
if __name__ == "__main__":
root = ttks.tkinter.Tk()
mainbody(root)
root.mainloop()

Related

Threading tkinter serial reading function

I'm making an application that reads serial data coming from the sensors on an arduino board. Problems arise when trying to use the matplotlib.animation class to make a live graph of said data. The GUI widgets become unresponsive when the plotting is taking place. As far as i've understood, making the serial reading process run on its own thread could potentially solve the issue. I'm having trouble understanding how this could be made so that it is compatible with the FuncAnimation-subclass.
def read_serial_data(port, bauds=9600):
s = serial.Serial(port, bauds)
line = s.readline()[0:-2]
return line
def getPorts():
return [port.device for port in serial.tools.list_ports.comports(include_links=False)]
class GUI():
def __init__(self):
self.root = Tk.Tk()
self._fig = plt.figure()
self.root.title('Measurement Dashboard')
self.root.state('normal')
self.root.config(background='#ffffff')
self._canvas = FigureCanvasTkAgg(self._fig, self.root)
self._canvas.get_tk_widget().grid(column = 1, row = 1)
self._canvas.draw()
self._animate = None
self._ax = self._fig.add_subplot(111)
self._ax.yaxis.grid(True, color = 'black', linestyle='--')
self._ax.xaxis.grid(True, color = 'black', linestyle='--')
self._ax.set_xlabel('time')
self._ax.set_ylabel('CO2')
self.filename = Tk.StringVar()
self.entry = ttk.Entry(self.root, textvariable = self.filename)
self.entry.grid(column = 2, row = 2)
self.info_var = Tk.StringVar()
self.info_entry = ttk.Entry(self.root, textvariable = self.info_var)
self.info_entry.grid(column = 2, row = 3)
self.port = Tk.StringVar()
self.ports = getPorts()
self._cb = ttk.Combobox(self.root, textvariable= self.port, values = self.ports)
self._cb.grid(column = 2, row = 1)
self.start_button = Tk.Button(self.root, text = 'Start', command = self.plot)
self.start_button.grid(column = 1, row = 2)
self.save_button = Tk.Button(self.root, text = 'Save info', command = self.save_info)
self.save_button.grid(column = 2, row = 4)
def save_info(self):
global info
info = self.info_var.get()
def start(self):
self.root.mainloop()
def plot(self):
if self._animate is None:
self.scope = Scope(self._ax, self.filename.get())
self._canvas.draw_idle()
self._animate = animation.FuncAnimation(self._fig, self.scope.animate, frames = self.update, interval=2000, blit=False)
def update(self):
line = read_serial_data(self.port.get())
data = line.decode('utf-8')
yield data
time = datetime.now()
duration = time - start_time
measurement = {'time': time, 'dur': duration.seconds, 'CO2': data, 'info': info}
write_csv_line(self.filename.get(), measurement)
self.root.after(10000, self.update)
if __name__ == "__main__":
gui = GUI()
gui.start()
thread = Thread(target=read_serial_data,args=(gui.port,))
thread.start()
You don't really need another thread but can do this using non-blocking IO on th eserial port and make use of the Tkinter after call to manage the poll interval. pyserial provices inWaiting to test if the device has input waiting to be read. If there are bytes waiting, read just those.
Here is an example reader class to read lines from a serial device and post them to the application handler method once a complete line is read.
class Reader(Serial):
def __init__(self, *args, **kwargs):
self.buffer = ''
super(Reader, self).__init__(*args, **kwargs)
def monitor(self, w, interval=10):
n = self.inWaiting()
if n != 0:
data = self.read(n).decode('ascii')
self.buffer = self.buffer + data
ndx = self.buffer.find("\n")
if ndx != -1:
line = self.buffer[:ndx+1]
self.buffer = self.buffer[ndx+1:]
w.after_idle(lambda: w.parse_input(line))
w.after(interval, lambda: self.monitor(w, interval))
Used like:
app = <tkinter application class instance>
reader = Reader(port, baudrate, timeout=0)
reader.flushInput()
reader.monitor(app, 10)
app.mainloop()
In this case, it will call a parse_input method on the app instance whenever a line is read (delimited by a newline).
If you decide to use a thread, then you need a Queue to pass the data to the Tkinter UI thread and must ensure you don't call Tkinter methods from the worker thread.

(Python) Text widget line numbers

I have some code working that displays line numbers along the left side of a text widget. For the most part, it works well, but there are a couple of issues that I need some pointers on how to fix.
I have posted the code below.
Here is a link to a test file with 200 lines (each line is numbered).
test file here
Issues:
(1) You can mouse wheel (scroll) up and down the file. However, this causes the line numbers to get out of sync with the text widget line numbers. If someone can point me in the right direction to look at, I probably can figure it out and fix it.
(2) The PgDn/PgUp keys have a similar effect. I can hit PgDn a few times and notice the Text widget lines get out of sync with the line numbers, as well as losing their horizontal alignment. Again, if someone can point me in the right direction to look at, I'm sure I can figure out what's happening in the code and fix it.
from tkinter import *
class LineNumbers(Text):
def __init__(self, master, text_widget, **kwargs):
super().__init__(master, **kwargs)
self.text_widget = text_widget
self.text_widget.bind('<KeyRelease>', self.on_key_release)
self.text_widget.bind('<FocusIn>', self.on_key_release)
self.text_widget.bind('<MouseWheel>', self.on_key_release)
self.insert(1.0, '1')
self.configure(state='disabled')
def on_key_release(self, event=None):
p, q = self.text_widget.index("#0,0").split('.')
p = int(p)
final_index = str(self.text_widget.index(END))
num_of_lines = final_index.split('.')[0]
line_numbers_string = "\n".join(str(p + no) for no in range(int(num_of_lines)))
width = len(str(num_of_lines))
self.configure(state='normal', width=width)
self.delete(1.0, END)
self.insert(1.0, line_numbers_string)
self.configure(state='disabled')
if __name__ == '__main__':
win = Tk()
win.title("Line Numbers Test")
win.geometry("800x600+1000+300")
txt = Text(win)
ln = LineNumbers(win, txt, width=2)
f = open("line_test.txt", 'r')
lines = f.readlines()
for line in lines:
txt.insert(END, line)
f.close()
ln.pack(side=LEFT, fill=BOTH)
txt.pack(expand=True, fill=BOTH)
txt.focus()
win.mainloop()
class LineNumbers(Text):
def __init__(self, master, text_widget, **kwargs):
super().__init__(master, **kwargs)
self.busy = False
self.master = master
self.text_widget = text_widget
self.text_widget.bind('<KeyRelease>', self.on_key_release)
self.text_widget.bind('<FocusIn>', self.on_key_release)
self.text_widget.bind('<MouseWheel>', self.on_mousew)
self.text_widget.bind('<Button-4>', self.on_mousew)
self.text_widget.bind('<Button-5>', self.on_mousew)
self.insert(1.0, '1')
self.configure(state='disabled')
def on_mousew(self, event=None):
self.after( 50, self.bogus)
def on_key_release(self, event=None):
self.after( 50, self.bogus)
return "break"
def bogus( self):
self.text_widget.update()
p, q = self.text_widget.index("#0,0").split('.')
np = int(p)
self.text_widget.see( "#" + str( np+1) + ","+ q)
final_index = str(self.text_widget.index(END))
num_of_lines = final_index.split('.')[0]
line_numbers_string = "\n".join( str(np+no) for no in range(0, int(num_of_lines)))
width = len(str(num_of_lines))
self.configure(state='normal', width=width)
self.delete(1.0, END)
self.insert(1.0, line_numbers_string)
self.configure(state='disabled')

Python, Tkinter - root is not defined

I have a class - code below - which works flawlessly when called from if __name__ == '__main__': but when I call it from another .py file it spits an error. Apologies if this is a basic mistake on my part, but I tried a LOT of stuff and nothing seemed to work, would really love some help!
The error:
File "c:\Users\...\test.py", line 6, in <module>
app = Screenshot(root)
File "c:\Users\...\Screenshot.py", line 18, in __init__
self.master_screen = Toplevel(root)
NameError: name 'root' is not defined
The Class:
import time
from tkinter import Toplevel, Canvas, Frame, BOTH, YES, Tk
import pyautogui
import datetime
class Screenshot():
def __init__(self, master):
self.master = master
self.rect = None
self.x = self.y = 0
self.start_x = None
self.start_y = None
self.curX = None
self.curY = None
self.master_screen = Toplevel(root)
self.master_screen.title("Fenify")
self.master_screen.attributes("-transparent", "blue")
self.picture_frame = Frame(self.master_screen)
self.picture_frame.pack(fill=BOTH, expand=YES)
self.createScreenCanvas()
#TakeScreenshot
def takeBoundedScreenShot(self, x1, y1, x2, y2):
...
#Window
def createScreenCanvas(self):
...
#Controls
def on_button_press(self, event):
...
def on_button_release(self, event):
...
def on_right_click(self, event):
...
def on_move_press(self, event):
...
#Exit
def exit_screenshot(self):
...
if __name__ == '__main__':
root = Tk()
app = Screenshot(root)
root.mainloop()
example of calling from another class:
import tkinter
from Screenshot import Screenshot
root = tkinter.Tk()
app = Screenshot(root)
root.mainloop()
You want to set the parent/master of your Toplevel to self.master instead of root. root isn't defined in the scope of the Screenshot class, but the input argument master does point to it.
self.master_screen = Toplevel(self.master)

How to get Tkinter GUI working for serial data

Im trying to display serial data in a simple gui. The serial data is dynamic (temperature sensor).
This is the code which I wrote for the GUI in tkinter, but its throwing error for line number 9.
from time import sleep
import threading
import serial
from tkinter import *
serialdata = []
data = True
class SensorThread(threading.Thread):
def run(self):
seri_=serial.Serial('/dev/ttyACM0', 9600)
try:
i = 0
while True:
serialdata.append(seri_.readline())
i += 1
sleep(1)
except KeyboardInterrupt:
exit()
class Gui(object):
def __init__(self):
self.root = Tk()
self.lbl = Label(self.root, text="")
self.updateGUI()
self.readSensor()
def run(self):
self.lbl.pack()
self.lbl.after(1000, self.updateGUI)
self.root.mainloop()
def updateGUI(self):
msg = "Data is True" if data else "Data is False"
self.lbl["text"] = msg
self.root.update()
self.lbl.after(1000, self.updateGUI)
def readSensor(self):
self.lbl["text"] = serialdata[-1]
self.root.update()
self.root.after(527, self.readSensor)
if __name__ == "__main__":
SensorThread().start()
Gui().run()
While running this code Im getting this error. Can anyone please help resolve it :-
Traceback (most recent call last):
File "ts.py", line 39, in <module> Gui().run() File "ts.py", line 23, in __init__ self.readSensor() File "ts.py", line 34, in readSensor self.lbl["text"] = serialdata[0] IndexError: list index out of range
The following example will make is work. Just added a try except IndexError to make sure serialdata is not empty. I replaced the SensorThread with a function that adds a random int between 0 and 100 to the serialdata every one second.
import random
from time import sleep
import threading
from tkinter import *
serialdata = []
data = True
class SensorThread(threading.Thread):
def run(self):
while True:
sleep(1)
serialdata.append(random.randint(0, 100))
class Gui(object):
def __init__(self):
self.root = Tk()
self.lbl = Label(self.root, text="")
self.updateGUI()
self.readSensor()
def run(self):
self.lbl.pack()
self.lbl.after(1000, self.updateGUI)
self.root.mainloop()
def updateGUI(self):
msg = "Data is True" if data else "Data is False"
self.lbl["text"] = msg
self.root.update()
self.lbl.after(1000, self.updateGUI)
def readSensor(self):
try:
self.lbl["text"] = serialdata[-1]
except IndexError:
pass
self.root.update()
self.root.after(527, self.readSensor)
if __name__ == "__main__":
SensorThread().start()
Gui().run()

Python tkinter Doubling Checkboxes

I'm currently working on a Synthesizer inside Python for a school project and currently have a really troublesome issue. I have a Stack of Checkboxes which mark when a note is played and on which pitch inside a sequencer. My Problem is that whenever I open two Oscillators inside my synthesizer and put in the Values inside the checkboxes, the checkboxes duplicate their value over the multiple windows, but don't do it for the actual sequence, as in I have two lists with correct values, but the values aren't properly displayed inside the window.
To Replicate the Problem hit "new Oscillator" then click on "Arpeggio", do this a second time and click any Checkbox on the Arppeggio Windows
I know this might be a confusing explanation, but I'm going to link the complete code in the bottom so you can try it out and might know what I'm talking about.
The problem occurs inside the "Arpeggio" Class
import numpy # used for Waveform Calculation
from functools import partial # used for Command Combining
from tkinter import * # used for GUI
from tkinter import ttk # used for GUI
from tkinter import filedialog # used for GUI
np = numpy # Simplifying Libraries
tk = ttk # Simplifying Libraries
fd = filedialog # Simplifying Libraries
root = Tk()
#StartupFunction
def StartUp():
print("")
print("Starting Startup")
app = App(root) # Initializing GUI
print("Finished Startup")
main()
("Exiting Startup")
#Main Program Function
def main():
print("Executing Main")
root.mainloop()
print("Finished Main")
return 0
class Oscillator():
pass
class App(tk.Frame):
OscillatorWindowList = []
OscillatorList = []
SoundInputArrayList = []
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
root.title("PySynth")
root.geometry("984x300")
root.resizable(False, True)
root.maxsize(984,720)
btnNewOscillator = Button(root,text="New Oscillator",command=self.NewOscillator,relief=RIDGE,bg="#2d2d2d",fg="white")
btnNewOscillator.place(x = 8, y = 8+128)
def NewOscillator(self):
print("AddingOscillator")
self.OscillatorList.append(Oscillator())
self.SoundInputArrayList.append(SoundInputArray(root,len(self.OscillatorList)-1,len(self.OscillatorList)-1))
print(self.OscillatorList)
self.OscillatorWindowList.append(OscillatorGUI(root,self.OscillatorList[len(self.OscillatorList)-1],len(self.OscillatorList)))
def EXIT(self):
root.destroy()
#$SoundInputArray
class SoundInputArray():
actv = []
CheckbuttonList = []
CheckButtonFreq = []
i=0
ButtonCount = 32
VolumeSlider = None
btnArpeggio = None
Arpeggio = None
hasArpeggio = False
LFO = None
ArpeggioList = [(0,0)]
def __init__(self,master,oscillatorCount,number):
btnArpeggio = Button(master,text="Arpeggio",command=self.OpenArpeggio,relief=RIDGE,bg="#2d2d2d",fg="white")
btnArpeggio.place(x = 8, y = (1+oscillatorCount)*48 +128 )
def OpenArpeggio(self):
if self.Arpeggio == None:
self.Arpeggio = Arpeggio()
def GetArpeggio(self):
return self.Arpeggio
#$Arpeggio
class Arpeggio():
SoundValueList = None
def __init__(self):
GUI = Toplevel(root)
GUI.title("Arpeggio")
GUI.geometry("480x320")
GUI.resizable(False, False)
GUI.configure(bg="#171717")
self.SoundValueList = np.arange(0,16)
self.DrawUI(GUI)
self.ClearList()
def DrawUI(self,frame):
Button(frame,text="display", command= self.PrintSound, width=11,bg="#171717",).place(x = 4, y = 4)
Button(frame,text="empty", command= self.ClearList).place(x = 96, y = 4)
y = 1
x = 1
checkbuttonList = []
for y in range(1,13):
for x in range(0,16):
updatecommand = partial(self.UpdateList,x,y)
checkbuttonList.append(Checkbutton(frame, variable=self.SoundValueList[x], onvalue=y, offvalue=0,command = updatecommand))
checkbuttonList[len(checkbuttonList)-1].place(x = x*24 + 96, y= y*24 + 8)
def ClearList(self):
for i in range(0,16):
self.SoundValueList[i] = 0
def UpdateList(self,x,value):
if (self.SoundValueList[x] == value):
self.SoundValueList[x] = 0
else:
self.SoundValueList[x] = value
self.PrintSound()
def PrintSound(self):
print(self.SoundValueList)
def GetList(self):
print(self.SoundValueList)
return self.SoundValueList
StartUp() # Initiate Program

Categories