3 Replies - 1977 Views - Last Post: 21 November 2012 - 07:36 AM Rate Topic: -----

#1 alexr1090  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 44
  • View blog
  • Posts: 124
  • Joined: 08-May 11

Tkinter Windows Question

Posted 18 November 2012 - 08:03 AM

Hello all, I have limited gui experience so I'm hoping this isn't a hard one to solve. Basically I have a program that starts by calling a kitchenkomputer class that displays a main menu. In the main menu it creates a main menu window and a seperate window called weather. What I want is the main menu window to go away when an option is selected, but I want the weather window to always be present. Enlighten me.

#!/usr/bin/python
from bs4 import BeautifulSoup
from urllib2 import urlopen
import Tkinter, tkMessageBox
import threading
import time


class KitchenKomputerMain(Tkinter.Tk):
	def __init__(self, parent):
		Tkinter.Tk.__init__(self,parent)
		self.w = Tkinter.Toplevel()
		self.w.title('Weather')
		self.parent = parent
		self.initialize()
	def initialize(self):
		self.grid()

		label = Tkinter.Label(self, text="Welcome to the Kitchen Komputer",fg = "yellow", bg="black")
		
		label.grid(column=0,row=0, sticky="EW")
		
		timerButton = Tkinter.Button(self, text=u"Timer", command=self.timer)
		#button1 = Tkinter.Button(self, text=u"Radio", command = self.radio)
		timerButton.grid(column=0, row=1)
		#button1.grid(column=1,row=1)
		
		self.bind("0", self.timer)
		#self.bind("1", self.radio) 
		self.bind("<Escape>", self.kill)
		
		self.resizable(False,False)
		self.update()
		self.geometry(self.geometry())
	def kill(self, event):
		self.destroy()
	def timer(self,event='0'):
		self.kill(self)
		t = Timer(None)
		t.title("Timer")
	def weather(self):
		day_of_week_dic = {
				"Mon": "Monday",
				"Tue": "Tuesday",
				"Wed": "Wednesday",
				"Thu": "Thursday",
				"Fri": "Friday",
				"Sat": "Saturday",
				"Sun": "Sunday "
				}
		urlRead = urlopen('http://xml.weather.yahoo.com/forecastrss/63034_f.xml')
		soup = BeautifulSoup(urlRead, "lxml")
		print soup.title.string
		for weather in soup.html.body.find_all('yweather:forecast'):
			print weather.get('date')
			print 'day:'+day_of_week_dic[(weather.get('day'))]
			print 'high:'+(weather.get('high'))
			print 'low:'+(weather.get('low'))
			print 'conditions:'+(weather.get('text'))
		time.sleep(60*15)
		weather(self)	
class Timer(Tkinter.Tk):
	def __init__(self,parent):
		Tkinter.Tk.__init__(self,parent)
		self.parent = parent
		self.initialize()
		
	def initialize(self):
		#saying we want to use a grid layout
		self.grid()
		
		self.lblText = Tkinter.StringVar()
		label = Tkinter.Label(self, textvariable=self.lblText,fg = "yellow", bg="black")
		label.grid(column=0,row=0,columnspan=5, sticky="EW")
		self.lblText.set("Timer")
		
		button1 = Tkinter.Button(self,text="1",command=self.buttonclicked,width=4)
		button2 = Tkinter.Button(self,text="2",command=self.buttonclicked,width=4)
		button3 = Tkinter.Button(self,text="3",command=self.buttonclicked,width=4)
		button4 = Tkinter.Button(self,text="4",command=self.buttonclicked,width=4)
		button5 = Tkinter.Button(self,text="5",command=self.buttonclicked,width=4)
		button6 = Tkinter.Button(self,text="6",command=self.buttonclicked,width=4)
		button7 = Tkinter.Button(self,text="7",command=self.buttonclicked, width=4)
		button8 = Tkinter.Button(self,text="8",command=self.buttonclicked, width=4)
		button9 = Tkinter.Button(self,text="9",command=self.buttonclicked, width=4)
		button0 = Tkinter.Button(self,text="0",command=self.buttonclicked, width=4)
		MinutesButton = Tkinter.Button(self,text="Minutes",underline=0,command=self.buttonclicked,width=4)
		HoursButton = Tkinter.Button(self,text="Hours",underline=0,command=self.buttonclicked,width=4)
		ClrButton = Tkinter.Button(self,text="Clear",underline=0,command=self.buttonclicked,width=4)
		StartButton= Tkinter.Button(self,text="Start",underline=0,command=self.buttonclicked,width=4)
		PauseButton = Tkinter.Button(self,text="Pause",underline=0,command=self.buttonclicked,width=4)
		
		button1.grid(column=0, row=1)
		button2.grid(column=1, row=1)
		button3.grid(column=2, row=1)
		button4.grid(column=0, row=2)
		button5.grid(column=1, row=2)
		button6.grid(column=2, row=2)
		button7.grid(column=0, row=3)
		button8.grid(column=1, row=3)
		button9.grid(column=2, row=3)
		button0.grid(column=0, row=4)
		MinutesButton.grid(column=1,row=4)
		HoursButton.grid(column=2, row=4)
		ClrButton.grid(column=0,row=5)
		StartButton.grid(column=1,row=5)
		PauseButton.grid(column=2,row=5)
		
		
		self.bind("0", self.buttonPressed)
		self.bind("1", self.buttonPressed)
		self.bind("2", self.buttonPressed)
		self.bind("3", self.buttonPressed)
		self.bind("4", self.buttonPressed)
		self.bind("5", self.buttonPressed)
		self.bind("6", self.buttonPressed)
		self.bind("7", self.buttonPressed)
		self.bind("8", self.buttonPressed)
		self.bind("9", self.buttonPressed)
		self.bind("p", self.buttonPressed)
		self.bind("m", self.buttonPressed)
		self.bind("h", self.buttonPressed)
		self.bind("s", self.buttonPressed)
		#self.bind("<Return>",self.buttonPressed)
		self.bind("c", self.buttonPressed)
		self.bind("<Escape>", self.kill)
		
		self.resizable(False,False)
		self.update()
		self.geometry(self.geometry())
		
	def kill(self, event):
		self.destroy()		
	def buttonclicked(self):
		pass
	def buttonPressed(self,event):
		#check if labels text is the default text first
		txt = self.lblText.get()
		char = event.char

		if char == 'c':
			self.lblText.set('')
		elif txt == "Timer":
			if char == 'm':
				tkMessageBox.showerror('Error','must enter digits before entering minutes.')
			elif char == 's':
				tkMessageBox.showerror('Error', 'Must enter digits, minutes and/or hours before starting timer.')
			elif char == 'h':
				tkMessageBox.showerror('Error', 'Must enter digits before entering hours.')
			else:
				self.lblText.set(event.char)
		#if get this far text != timer
		else:
			if 'hours' in txt and 'minutes' in txt:
				#popup error box
				tkMessageBox.showerror('Error','Hours and minutes already selected. Must clear screen or start timer.')
				return
			if char!= 'm' and char!='s' and char!='h':
				self.lblText.set(txt+char)
				return
			elif char == 'm':
				if 'minutes' in txt:
					tkMessageBox.showerror('Error','Minutes already accounted for. Must clear screen, enter hours, or start timer.')
					return
				else:
					if 'hours' in txt:
						j = txt.split('hours')
						self.lblText.set(j[0]+' hours & '+j[1]+' minutes')
						return
					else:
						self.lblText.set(txt+' minutes ')
						return
			elif char == 'h' :
				if 'minutes' in txt:
					j = txt.split('minutes')
					self.lblText.set(j[1]+' hours & '+j[0]+' minutes')
					return
				elif 'hours' in txt:
					tkMessageBox.showerror('Error','Hours already selected. Start program or select minutes.')
					return
				else:	
					self.lblText.set(txt+' hours ')
					return
					
			else:
				minutes = 0
				if 'hours' in txt and 'minutes' in txt:
					h = txt.split('hours')
					minutes= 60 * int(h[0])
					m = txt.split('minutes')
					mintues+=int(m[0])
					self.timer(minutes)
					return
				elif 'hours' in txt:
					h = txt.split('hours')
					minutes = 60 * 60* int(h[0])
					self.timer(minutes)
					return
				elif 'minutes' in txt:
					m = txt.split('minutes')
					minutes = int(m[0])
					self.timer(minutes*60)
					return
	def timer(self, remaining = None):
		if remaining is not None:
			self.remaining = remaining

		if self.remaining <= 0:
			self.lblText.set("time's up!")
		else:
			self.lblText.set("%d seconds" % self.remaining)
			self.remaining = self.remaining - 1
			self.after(1000, self.timer)

main = KitchenKomputerMain(None)
main.title = "Komputer"
main.mainloop()




Is This A Good Question/Topic? 0
  • +

Replies To: Tkinter Windows Question

#2 alexr1090  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 44
  • View blog
  • Posts: 124
  • Joined: 08-May 11

Re: Tkinter Windows Question

Posted 20 November 2012 - 05:35 PM

I solved it so for anyone who may want to know. What I did was, first, abandon using toplevel and, second, create a weather class and in the main page at the very bottom initialize the weather class. Where to initialize it was huge. I can't do it in the main program because it's running an infinite loop for the main page. As a result weather doesn't pop up until the main page is closed, or vice versa. I hope this makes sense and will help someone down the line. Here's my work-in-progress code with the weather thing solved.

[edit] ok so I thought I had this solved. But it turns out Tkinter doesn't like drawing things on multiple windows. Well, at least I don't know enough about it to make it work. Sooo I believe I'm going to rewrite my program to just be one window with multiple frames. one frame is going to be a main menu, another frame will be weather, and another will be what the user selects from the main menu.

#!/usr/bin/python
from bs4 import BeautifulSoup
from urllib2 import urlopen
import Tkinter, tkMessageBox, Image, ImageTk
import thread
import time


class KitchenKomputerMain(Tkinter.Tk):
	def __init__(self, parent):
		Tkinter.Tk.__init__(self,parent)
		self.parent = parent
		self.initialize()
		
	def initialize(self):
		self.grid()

		label = Tkinter.Label(self, text="Welcome to the Kitchen Komputer",fg = "yellow", bg="black")
		
		label.grid(column=0,row=0, sticky="EW")
		
		timerButton = Tkinter.Button(self, text=u"Timer", command=self.timer)
		#button1 = Tkinter.Button(self, text=u"Radio", command = self.radio)
		timerButton.grid(column=0, row=1)
		#button1.grid(column=1,row=1)
		
		self.bind("0", self.timer)
		#self.bind("1", self.radio) 
		self.bind("<Escape>", self.kill)
		
		self.resizable(False,False)
		self.update()
		self.geometry(self.geometry())
		self.weather()
	def kill(self, event):
		self.destroy()
	def weather(self):
		weather = Weather(None)
		weather.title('Weather')
		weather.mainloop()
	def timer(self,event='0'):
		self.kill(self)
		t = Timer(None)
		t.title("Timer")
class Weather(Tkinter.Tk):
	def __init__(self, parent):
		Tkinter.Tk.__init__(self,parent)
		
		self.parent = parent
		self.initialize()
	def initialize(self):
		self.sun_data = Image.open('Sunny-128.png')	
		self.lightning_data = Image.open('lightning-128.png')
		self.light_snow_data = Image.open('light-snow.png')
		self.light_showers_data = Image.open('Light-Showers-128.png')
		self.hail_data = Image.open('hail-128.png')
		self.cloudy_data = Image.open('Cloudy-128.png')
		self.fog_data = Image.open('fog-128.png')
		self.light_rain_data = Image.open('light-rain-128.png')
		self.overcast_data = Image.open('Overcast-128.png')
		self.rain_data= Image.open('rain-128.png')
		self.showers_data = Image.open('Showers-128.png')
		self.snow_data = Image.open('snow-128.png')
		self.sunny_period_data = Image.open('Sunny-Period-128.png')
		self.thunder_data = Image.open('thunder-128.png')
		self.thunderstorms_data = Image.open('Thunderstorms-128.png')
		
		self.light_snow_image = ImageTk.PhotoImage(self.light_snow_data)
		self.lightning_image = ImageTk.PhotoImage(self.lightning_data)
		self.light_showers_image = ImageTk.PhotoImage(self.light_showers_data)
		self.hail_image = ImageTk.PhotoImage(self.hail_data)
		self.cloudy_image = ImageTk.PhotoImage(self.cloudy_data)
		self.fog_image = ImageTk.PhotoImage(self.fog_data)
		self.light_rain_image = ImageTk.PhotoImage(self.light_rain_data)
		self.overcast_image = ImageTk.PhotoImage(self.overcast_data)
		self.rain_image = ImageTk.PhotoImage(self.rain_data)
		self.showers_image= ImageTk.PhotoImage(self.showers_data)
		self.snow_image = ImageTk.PhotoImage(self.snow_data)
		self.sunny_period_image = ImageTk.PhotoImage(self.sunny_period_data)
		self.thunder_image = ImageTk.PhotoImage(self.thunder_data)
		self.thunderstorms_image = ImageTk.PhotoImage(self.thunderstorms_data)
		self.sunny_image = ImageTk.PhotoImage(self.sun_data)
		
		thread.start_new_thread(self.weather,())
	def weather(self):
		
		day_of_week_dic = {
				"Mon": "Monday",
				"Tue": "Tuesday",
				"Wed": "Wednesday",
				"Thu": "Thursday",
				"Fri": "Friday",
				"Sat": "Saturday",
				"Sun": "Sunday "
				}
		urlRead = urlopen('http://xml.weather.yahoo.com/forecastrss/63034_f.xml')
		soup = BeautifulSoup(urlRead, "lxml")
		print soup.title.string
		for weather in soup.html.body.find_all('yweather:forecast'):
			col+=1
			rowSize+=1
			self.day_label = Tkinter.Label(text = day_of_week_dic[(weather.get('day'))])
			self.high_label = Tkinter.Label(text = weather.get('high'))
			self.low_label = Tkinter.Label(text=weather.get('low'))
			try:
				self.conditions_pic = Tkinter.Label(image=weather.get('text'))
			except Exception:
				self.conditions_pic = Tkinter.Label(text = weather.get('text'))
		
		time.sleep(60*15)
		weather(self)	
class Timer(Tkinter.Tk):
	def __init__(self,parent):
		Tkinter.Tk.__init__(self,parent)
		self.parent = parent
		self.initialize()
		
	def initialize(self):
		#saying we want to use a grid layout
		self.grid()
		
		self.lblText = Tkinter.StringVar()
		label = Tkinter.Label(self, textvariable=self.lblText,fg = "yellow", bg="black")
		label.grid(column=0,row=0,columnspan=5, sticky="EW")
		self.lblText.set("Timer")
		
		button1 = Tkinter.Button(self,text="1",command=self.buttonclicked,width=4)
		button2 = Tkinter.Button(self,text="2",command=self.buttonclicked,width=4)
		button3 = Tkinter.Button(self,text="3",command=self.buttonclicked,width=4)
		button4 = Tkinter.Button(self,text="4",command=self.buttonclicked,width=4)
		button5 = Tkinter.Button(self,text="5",command=self.buttonclicked,width=4)
		button6 = Tkinter.Button(self,text="6",command=self.buttonclicked,width=4)
		button7 = Tkinter.Button(self,text="7",command=self.buttonclicked, width=4)
		button8 = Tkinter.Button(self,text="8",command=self.buttonclicked, width=4)
		button9 = Tkinter.Button(self,text="9",command=self.buttonclicked, width=4)
		button0 = Tkinter.Button(self,text="0",command=self.buttonclicked, width=4)
		MinutesButton = Tkinter.Button(self,text="Minutes",underline=0,command=self.buttonclicked,width=4)
		HoursButton = Tkinter.Button(self,text="Hours",underline=0,command=self.buttonclicked,width=4)
		ClrButton = Tkinter.Button(self,text="Clear",underline=0,command=self.buttonclicked,width=4)
		StartButton= Tkinter.Button(self,text="Start",underline=0,command=self.buttonclicked,width=4)
		PauseButton = Tkinter.Button(self,text="Pause",underline=0,command=self.buttonclicked,width=4)
		
		button1.grid(column=0, row=1)
		button2.grid(column=1, row=1)
		button3.grid(column=2, row=1)
		button4.grid(column=0, row=2)
		button5.grid(column=1, row=2)
		button6.grid(column=2, row=2)
		button7.grid(column=0, row=3)
		button8.grid(column=1, row=3)
		button9.grid(column=2, row=3)
		button0.grid(column=0, row=4)
		MinutesButton.grid(column=1,row=4)
		HoursButton.grid(column=2, row=4)
		ClrButton.grid(column=0,row=5)
		StartButton.grid(column=1,row=5)
		PauseButton.grid(column=2,row=5)
		
		
		self.bind("0", self.buttonPressed)
		self.bind("1", self.buttonPressed)
		self.bind("2", self.buttonPressed)
		self.bind("3", self.buttonPressed)
		self.bind("4", self.buttonPressed)
		self.bind("5", self.buttonPressed)
		self.bind("6", self.buttonPressed)
		self.bind("7", self.buttonPressed)
		self.bind("8", self.buttonPressed)
		self.bind("9", self.buttonPressed)
		self.bind("p", self.buttonPressed)
		self.bind("m", self.buttonPressed)
		self.bind("h", self.buttonPressed)
		self.bind("s", self.buttonPressed)
		#self.bind("<Return>",self.buttonPressed)
		self.bind("c", self.buttonPressed)
		self.bind("<Escape>", self.kill)
		
		self.resizable(False,False)
		self.update()
		self.geometry(self.geometry())
		
	def kill(self, event):
		self.destroy()		
	def buttonclicked(self):
		pass
	def buttonPressed(self,event):
		#check if labels text is the default text first
		txt = self.lblText.get()
		char = event.char

		if char == 'c':
			self.lblText.set('')
		elif txt == "Timer":
			if char == 'm':
				tkMessageBox.showerror('Error','must enter digits before entering minutes.')
			elif char == 's':
				tkMessageBox.showerror('Error', 'Must enter digits, minutes and/or hours before starting timer.')
			elif char == 'h':
				tkMessageBox.showerror('Error', 'Must enter digits before entering hours.')
			else:
				self.lblText.set(event.char)
		#if get this far text != timer
		else:
			if 'hours' in txt and 'minutes' in txt:
				#popup error box
				tkMessageBox.showerror('Error','Hours and minutes already selected. Must clear screen or start timer.')
				return
			if char!= 'm' and char!='s' and char!='h':
				self.lblText.set(txt+char)
				return
			elif char == 'm':
				if 'minutes' in txt:
					tkMessageBox.showerror('Error','Minutes already accounted for. Must clear screen, enter hours, or start timer.')
					return
				else:
					if 'hours' in txt:
						j = txt.split('hours')
						self.lblText.set(j[0]+' hours & '+j[1]+' minutes')
						return
					else:
						self.lblText.set(txt+' minutes ')
						return
			elif char == 'h' :
				if 'minutes' in txt:
					j = txt.split('minutes')
					self.lblText.set(j[1]+' hours & '+j[0]+' minutes')
					return
				elif 'hours' in txt:
					tkMessageBox.showerror('Error','Hours already selected. Start program or select minutes.')
					return
				else:	
					self.lblText.set(txt+' hours ')
					return
					
			else:
				minutes = 0
				if 'hours' in txt and 'minutes' in txt:
					h = txt.split('hours')
					minutes= 60 * int(h[0])
					m = txt.split('minutes')
					mintues+=int(m[0])
					self.timer(minutes)
					return
				elif 'hours' in txt:
					h = txt.split('hours')
					minutes = 60 * 60* int(h[0])
					self.timer(minutes)
					return
				elif 'minutes' in txt:
					m = txt.split('minutes')
					minutes = int(m[0])
					self.timer(minutes*60)
					return
	def timer(self, remaining = None):
		if remaining is not None:
			self.remaining = remaining

		if self.remaining <= 0:
			self.lblText.set("time's up!")
		else:
			self.lblText.set("%d seconds" % self.remaining)
			self.remaining = self.remaining - 1
			self.after(1000, self.timer)

main = KitchenKomputerMain(None)
main.title("Komputer")
main.mainloop()



This post has been edited by alexr1090: 20 November 2012 - 06:45 PM

Was This Post Helpful? 0
  • +
  • -

#3 Python_4_President  Icon User is offline

  • D.I.C Regular

Reputation: 53
  • View blog
  • Posts: 321
  • Joined: 13-August 11

Re: Tkinter Windows Question

Posted 20 November 2012 - 10:01 PM

Wow. That's some pretty hard to read code.

When I launch the app (the first set of code, the second contains references to images I don't have), I get a bigger box with nothing in it, title = 'Weather', then another box title='tk', a label "Welcome to the kitchen komputer" with single button "Timer" on it.

Clicking timer kills both windows. None of those things looked like a main menu to me...

Maybe if you could paint a better picture of what you're trying to do I could give you some better advice.


EDIT: I think I get what you're saying. You want a weather thing (displays info about the weather, etc) to always be present somewhere on the screen, and you also want other stuff going on, too. Keep them separate. You could share data between the weather thing and the rest of the app via a database (eg, sqlite), or a file, or by getter methods in the weather class that allow you to access the data you need.

But for now, here's a pretty fun (though probably just as hard to read) GUI I built a while ago in Tkinter that does some nifty stuff you might be interested in. Particularly "introduce", "configure", "add_dynamic_check_buttons", and "remove_dynamic_check_buttons"


Introduce seemed like a good idea at the time because I wanted the user to do something in a Tk window before the main app started if the user had not already done that stuff (IE, specify the paths to various inbound and outbound data directories so I could create check buttons to allow easy access to some but not others, or all at once, or none at all, and where they'd like to store things, and which text editor they use to look at data. )

It launches an independent Tk window from the main app. The main app calls it again when the user wants to edit their config file. So, this could be a way to achieve your main menu deal, by utilizing independent Tk windows that can call each other.

Note that I call mainloop from inside the class constructor, whereas you initialize the class first and then enter the mainloop. Doing it inside the constructor makes it easier to start a Tk app in a new thread.

#!/usr/bin/python

#Track number of files scanned
#Track directories currently being scanned
#Add feature to specify num bytes to read from file for use in searching contents
import os
import sys
import re
import time
import math
from Tkinter import *
import threading    
import shutil

blank_line = re.compile("^\s+(?!\S+)")
check_button = re.compile("checkbutton[0-9]+")
config_file = ".\\ls4.conf"


def config_file_as_str():
    global config_file
    return open(config_file, 'r').read()
        

def write_config_file(config):
    conf = open(".\\ls4.conf", 'w')
    conf.write(config)
    conf.close()


def reload_config_file():
    global checks
    with open(".\\ls4.conf", 'r') as f:
        checks = [str(x.strip()).replace("\\", "\\\\") for x in f if "#" not in x and x != "\n"]
        
def config_save(root, introduction_message):
    my_config = introduction_message.get("1.0", END)
    write_config_file(my_config)
    reload_config_file()
    root.destroy()


def introduce():
    msg = """
#edit your config file below. 
#lines beginning with a # are comments (IE, they won't be read by the program)


OUTGOING:
#eg, c:\\outgoing. No default because I don't know your drive lettering.

INCOMING:
#eg, c:\\incoming. No default because I don't know your drive lettering.

STORAGE:
#eg, c:\\where_i_store_data. Default is the directory you run the program from.
.\\edi_data

EDITOR:
#eg, c:\\windows\\system32\\notepad.exe. Default is notepad, can use /usr/bin/mousepad etc
c:\\windows\\system32\\notepad.exe
"""
    rooti = Tk()
    rooti.title("Editing your configuration file.")
    introFrame = Frame(rooti)
    introduction_label = Label(introFrame, text = "Configure your stuff: ")
    introduction_message = Text(introFrame, width=80, height=20)
    save_button = Button(introFrame, text = "Save Config", command=lambda: config_save(rooti,introduction_message))
    introFrame.grid(row=0, column = 0)
    introduction_label.grid(row=0, column = 0)
    introduction_message.grid(row = 0, column = 2)
    save_button.grid(row = 4, column = 2, sticky=W)
    if not os.path.isfile(config_file):
        introduction_message.insert(END, msg)
        rooti.mainloop()
    else:
        introduction_message.insert(END, config_file_as_str())
        rooti.mainloop(1)
    

try:    
    checks = [str(x.strip()).replace("\\", "\\\\") for x in open(".\\ls4.conf", 'r').readlines() if "#" not in x and x != "\n"]
except:
    introduce()
    checks = [str(x.strip()).replace("\\", "\\\\") for x in open(".\\ls4.conf", 'r').readlines() if "#" not in x and x != "\n"]
    
directories = []


#CONSTANTS
AOA = "X:\\as2\\outgoing\\archive\\"
AIA = "X:\\as2\\incoming\\archive\\"

UOA = "U:\\outgoing\\archive\\"
UIA = "U:\\incoming\\archive\\"


config_divisions = ['EDITOR:', 'STORAGE:', 'INCOMING:', 'OUTGOING:']



class MainUI(object):           
        
    stop = False
    filename_list = []
            

            
    def __init__(self):
        self.master = Tk()
        self.master.title("Company Search Utility v1.0A")
        global directories 
        self.no_dump = False
        self.no_editor = False
        self.show_contents = False
        self.ISAonly = False
        self.reconfiguring = False
        self.check_buttons = []
        self.nexti = 0
        self.dict = {}
        
        self.after_time = None
        self.before_time = None
        
        
        
        self.InputFrame = Frame(self.master, bd=1, relief=SUNKEN)
        self.InputFrame.grid(row=0, column=0)
        self.InputFrame.rowconfigure(0, weight=1)
        self.InputFrame.columnconfigure(0, weight=1)
        
        self.filename_label = Label(self.InputFrame, text="Filename:")
        self.filename_label.grid(row=0, column=0)
        self.filename_entry = Entry(self.InputFrame, width=122)
        self.filename_entry.grid(row=0, column=1, stick=W+E)
        
        
        self.contents_label = Label(self.InputFrame, text="Contents:")
        self.contents_label.grid(row = 1, column = 0)     
        self.contents_entry = Entry(self.InputFrame, width=50)
        self.contents_entry.grid(row=1, column=1, stick=W+E)
        
        
        self.directory_label = Label(self.InputFrame, text="Directories:")
        self.directory_label.grid(row = 2, column = 0)
        self.directory_entry = Entry(self.InputFrame, width=50)
        self.directory_entry.grid(row=2, column=1, sticky=W+E)
        
        self.shortcut_instructions_label = Label(self.InputFrame, text="aoa<yyyymm>, aia<yyyymm>, uoa<yyyymm>, uia<yyyymm> are shortcuts to protocol\\archive\\<yyyymm>")
        self.shortcut_instructions_label.grid(row = 3, column = 1)
        
        self.config_button = Button(self.InputFrame, text="Edit Config", width = 10, command=self.reconfigure)
        self.config_button.grid(row=3, column=0)
        
        self.search_button = Button(self.InputFrame, text="Search", width=10, command=self.search_callback)
        self.search_button.grid(row=0, column=2)
        
        self.stop_button = Button(self.InputFrame, text="Stop", width=10, command=self.stop_search)
        self.stop_button.grid(row=1, column=2)
        
        self.clear_button = Button(self.InputFrame, text="Clear", width=10, command=self.clear_textbox)
        self.clear_button.grid(row=2, column=2)
        
        self.report_status_button = Button(self.InputFrame, text="Edit", width=10, command=self.select_filenames)
        self.report_status_button.grid(row=3, column=2)
        
#        self.toggle_contents_button = Checkbutton(self.InputFrame, text="Toggle Contents", width=10, command=self.toggle_contents)
#        self.toggle_contents_button.grid(row = 4, column = 2)

        self.date_range_entry_label = Label(self.InputFrame, text="Date Range: ")
        self.date_range_entry_label.grid(row=4, column=1, sticky=NE)
        
        self.date_range_entry = Entry(self.InputFrame, width=25)
        self.date_range_entry.grid(row=4, column=2, sticky=NE)
        
        self.toggle_ISAONLY_button = Checkbutton(self.InputFrame, text="ISA Only", width=10, command=self.searchISAonly)
        self.toggle_ISAONLY_button.grid(row=5, column=2)
        
        
        
        
        self.masterCheckboxFrame = Frame(self.InputFrame, bd=1, relief=SUNKEN)
        self.masterCheckboxFrame.grid(row=4, column=1)
        
        self.Checkbox_Frame_outgoing = Frame(self.masterCheckboxFrame, bd=1, relief=SUNKEN)
        self.Checkbox_Frame_outgoing.grid(row=1, column=0, sticky=W)
                
        
        self.Checkbox_Frame_incoming = Frame(self.masterCheckboxFrame, bd=1, relief=SUNKEN)
        self.Checkbox_Frame_incoming.grid(row=1, column=1, sticky=E)
        
        self.configure()
        
        
        self.output_frame = Frame(self.master, bd=1, relief=SUNKEN)
        self.output_frame.grid(row=1, column=0, sticky=N)
        self.output_frame.rowconfigure(0, weight=1)
        self.output_frame.columnconfigure(0, weight=1)
        
        
        self.results_box_frame = Frame(self.output_frame, bd=1, relief=SUNKEN)
        self.results_box_frame.grid(row=0, column=1, sticky=W)
        self.results_box_frame.rowconfigure(0, weight=1)
        self.results_box_frame.columnconfigure(0, weight=1)
        
        self.results_box = Text(self.results_box_frame, width = 100, height=6)
        self.results_box.grid(row=1, column=1, sticky=S, pady = 5)
        self.results_box.rowconfigure(0, weight=1)
        self.results_box.columnconfigure(0, weight=1)
        self.results_scrollbar = Scrollbar(self.results_box_frame)
        self.results_scrollbar.grid(row=1, column=2, sticky=N+S)
        self.results_scrollbar.config(command=self.results_box.yview) 
        self.results_box.config(yscrollcommand=self.results_scrollbar.set)
        
        self.status_box = Text(self.output_frame, width=10, height=14)
        self.status_box.grid(row=0, column=2)
                
        self.report_frame = Frame(self.output_frame, bd=1, relief=SUNKEN) 
        self.report_frame.grid(row=0, column=1, sticky=NW)   
        self.status_updates = Text(self.report_frame, height=3, width=100)
        self.status_updates.grid(row=1, column=1, pady = 5, sticky=NW)
                        
        
        listbox_row = 1
        self.list_box_scroll = Scrollbar(self.output_frame)
        self.list_box = Listbox(self.output_frame, width=132, height=5, selectmode = EXTENDED, yscrollcommand=self.list_box_scroll)
        self.list_box.grid(row=listbox_row, column=1, sticky=NW, rowspan=5)
        self.list_box_scroll.config(command=self.list_box.yview)
        self.list_box_scroll.grid(row=listbox_row, column=1, sticky=E+N+S, rowspan = 5)
        
        self.move_files_to_holding_tank = Button(self.output_frame, text="Move", command=self.mt_move_to_tank)
        self.move_files_to_holding_tank.grid(row=listbox_row, column=2, sticky=W+N+S, rowspan = 5)
        
        self.as2_label = Label(self.output_frame, text="AS2/VAN outgoing: .x12 file extension")
        self.van_label = Label(self.output_frame, text="VAN/AS2 incoming: ._IE/.as2van file extension")
        self.as2_label.grid(row=6, column=1, sticky=S)
        self.van_label.grid(row=7, column=1, sticky=S)
        
        mainloop()
        
   
   

        
    def quit(self):
        self.rooti.destroy()
        
    def config_save(self):
        my_config = self.introduction_message.get("1.0", END)
        write_config_file(my_config)
    
    def error(self, msg):
        threading.Thread(target=self.error, args=[msg]).start()
            
    def _error(self, msg):
        root = Tk()
        rootFrame = Frame(root)
        error_label = Label(rootFrame, text = "Error: ")
        error_message = Text(rootFrame, width=50, height=10)
        exit_button = Button(rootFrame, text = "Quit", command=self.quit)
        
        error_message.insert(END, msg)
        rootFrame.grid(row=0, column = 0)
        error_label.grid(row=0, column = 0)
        error_message.grid(row = 0, column = 2)
        exit_button.grid(row = 2, column = 1)


    def reconfigure(self):
        global checks
        introduce()
        self.configure(removing_checkbuttons = True)
        self.configure()
                       
    def configure(self, removing_checkbuttons = False):
        global checks
        global directories
        directories = []
        config_frame = None
        adding_checkbuttons = False
        _dict = {}
        config_frames = []
        if not removing_checkbuttons:
            for i in xrange(len(checks)):
                if checks[i] == "OUTGOING:":
                    self.config_division = "OUTGOING"
                    print "Configuring ", self.config_division
                    adding_checkbuttons = True
                    config_frame = "self.Checkbox_Frame_outgoing"
                    _dict[config_frame] = []
                    config_frames.append(config_frame)             
                elif checks[i] == "INCOMING:":
                    self.config_division = "INCOMING"
                    print "Configuring ", self.config_division
                    adding_checkbuttons = True
                    config_frame = "self.Checkbox_Frame_incoming"
                    _dict[config_frame] = []
                    config_frames.append(config_frame)
                elif checks[i] == "STORAGE:":
                    adding_checkbuttons = False
                    if blank_line.search(checks[i+1]) == None and checks[i+1] not in config_divisions:
                        self.dumping_ground = self.sanitize(checks[i+1])
                    else:
                        self.no_dump = True
                        continue
                    
                    
                    if not os.path.isdir(self.dumping_ground):
                        os.mkdir(self.dumping_ground)
                        
                    if not os.path.isdir("{0}\\bak".format(self.dumping_ground)):
                        os.mkdir("{0}\\bak".format(self.dumping_ground))
                        
                elif checks[i] == "EDITOR:":
                    adding_checkbuttons = False
                    try:
                        if blank_line.search(checks[i+1]) == None and checks[i+1] not in config_divisions:
                            self.editor = "{0}".format(self.sanitize(checks[i+1]))
                            if not os.path.isfile(self.sanitize(checks[i+1])):
                                self.error("{0} doesn't exist.".format(self.editor))
                        else:
                            self.no_editor = True
                            self.error("No editor specified.")
                            continue
                    except:
                        self.no_editor = True
                        self.error("No editor specified.")
                        continue
                        
                elif blank_line.search(checks[i]):
                    print "BLANK!"
                    pass        
                else:
                    if adding_checkbuttons:
                        _dict[config_frame].append(checks[i])
        
        if config_frames != []:
            for frame in config_frames:
                try:
                    self.dict[frame] = _dict[frame]
                except:
                    self.dict[frame] = None
                    
        if not removing_checkbuttons:
            self.add_dynamic_check_buttons(_dict)
            self.toggle_empty_frame()
        else:
            self.remove_dynamic_check_buttons(_dict)

    def add_dynamic_check_buttons(self, dict):
        button_number = 0
        for key in dict.keys():
            i = 0
            j = 0
            k = 0
            for i in xrange(0, len(dict[key])):
                #print "self.add_dynamic_check_button({0}, {1}, {2}, {3}, {4})".format(button_number, j, k, key, dict[key][i])
                self.check_buttons.append(button_number)
                self.add_dynamic_check_button(button_number, j, k, key, dict[key][i])
                button_number += 1
                if j == 10:
                    j = 0
                    k += 1
                else:
                    j += 1
    
    
    def remove_dynamic_check_buttons(self, dict):
        for button in self.check_buttons:
            self.remove_dynamic_check_button(button)
        self.check_buttons = []
                
    def toggle_empty_frame(self):
        
        nframes = len(self.dict.keys())
        nframes_forgotten = 0
        for frame in self.dict:
            if self.dict[frame] == []:
                print "forgot: ", frame
                exec(compile("{0}.grid_forget()".format(frame), "<string>", "exec"))
                nframes_forgotten += 1
            else:
                if "incoming" in frame:
                    exec(compile("{0}.grid(row=1, column=0)".format(frame), "<string>", "exec"))
                elif "outgoing" in frame:
                    exec(compile("{0}.grid(row=1, column=1)".format(frame), "<string>", "exec"))
                else:
                    pass
        if nframes == nframes_forgotten:
            exec(compile("self.masterCheckboxFrame.grid_forget()", "<string>", "exec"))
        else:
            exec(compile("self.masterCheckboxFrame.grid(row=4, column=1)", "<string>", "exec"))
            
    def add_dynamic_check_button(self, ID, row, column, frame, dir):
        objective1 = compile("""def check_checkbutton{0}():
                                    dir = \"{1}\"
                                    if dir in directories:
                                            directories.remove(dir)
                                    else:
                                            directories.append(dir)
                                    print directories""".format(ID, dir), "<string>", "exec")
        
        objective2 = compile("self.checkbutton{0} = Checkbutton({1}, text=\"{2}\", command=check_checkbutton{0})".format(ID, frame, dir), "<string>", "exec")
        print "ADDING: self.checkbutton{0}.grid(row={1}, column={2}, sticky=W) for: {3}".format(ID, row, column, dir)
        objective3 = compile("self.checkbutton{0}.grid(row={1}, column={2}, sticky=W)".format(ID, row, column), "<string>", "exec")
        exec(objective1)
        exec(objective2)
        exec(objective3)
    
    def remove_dynamic_check_button(self, ID):
        exec(compile("self.checkbutton{0}.destroy()".format(ID), "<string>", "exec"))
        
        
        
    def clear_checkbuttons(self):
            self.configure()
        
    def sanitize(self, msg):
        return msg.replace("\\", "\\\\")
    
    def push_status_update(self, msg):
        try:
            self.status_updates.insert(END, msg + "\n")
            self.status_updates.yview(END)
        except:
            pass
                
    def push_files(self, message):
        try:
            self.list_box.insert(END, message)
            self.list_box.yview(END)
        except:
            pass
        
    def push_message(self, message):
        try:
            self.results_box.insert(END, message)
            self.results_box.yview(END)
        except:
            pass
    
    def push_report(self, message):
        try:
            self.status_box.insert(END, message)
            self.status_box.yview(END)
        except:
            pass
        
#    def push_dirs(self, message):
#        try:
#            #self.searching_directories.insert(END, message)
#            pass
#        except:
#            pass
        
    def searchISAonly(self):
        if(self.ISAonly == False):
            self.ISAonly = True
        else:
            self.ISAonly = False
            
    def select_filenames(self):
        #print [self.list_box.get(index) for index in  self.list_box.curselection()]
        filename_list = ["LAUNCH"]
        for filename in [self.list_box.get(index) for index in  self.list_box.curselection()]:
            filename_list.append(filename.rstrip().lstrip())
        self.open_in_nppp(filename_list)
        
    def toggle_contents(self):
        if not self.show_contents:
            self.show_contents = True
        else:
            self.show_contents = False
            
            
            
    def mt_move_to_tank(self):
        if self.no_dump:
            pass
        else:
            threading.Thread(target=self.move_to_tank).start()
        
           
    def move_to_tank(self):
        try:
            filenames = [self.list_box.get(index) for index in self.list_box.curselection() if index is not None]
        except TclError:
            try:
                filenames = [self.list_box.get(index) for index in self.list_box.curselection() if index is not None]
            except TclError:
                pass
                
        self.cleanup_tank()
        for filename in filenames:
            try:
                shutil.copy(filename, self.dumping_ground)
                self.push_status_update("Copied {0} to {1}".format(filename, self.dumping_ground))
            except:
                self.configure()
                try:
                    shutil.copy(filename, self.dumping_ground)
                    self.push_status_update("Copied {0} to {1}".format(filename, self.dumping_ground))
                except:
                    self.push_status_update("Failed to Copy {0} to {1}".format(filename, self.dumping_ground))
                    pass
                pass
        
    def cleanup_tank(self):
        print self.dumping_ground
        try:
            for file in os.listdir(self.dumping_ground):
                if file in os.listdir("{0}\\bak".format(self.dumping_ground)):
                    if os.path.isfile("{0}\\{1}".format(self.dumping_ground, file)):
                    #os.system("del /F {0}\\{1}".format(self.dumping_ground, file))
                        os.remove("{0}\\{1}".format(self.dumping_ground, file))
                        self.push_status_update("Removed {0}".format(file))
                    else:
                        pass
                else:
                    if os.path.isfile("{0}\\{1}".format(self.dumping_ground, file)):
                        shutil.move("{0}\\{1}".format(self.dumping_ground, file), "{0}\\bak\\{1}".format(self.dumping_ground,file))
                        self.push_status_update("Moved {0} to bak".format(file))
                    else:
                        pass
        except WindowsError:
            self.configure()
            self.move_to_tank()

        
    def file_accessor(self, directory, file_pattern = re.compile("[a-zA-Z0-9-_.]*"), content_pattern = re.compile("\S*"), threadIDx = 0):
        start_time = time.clock()
        assert directory is not str, "Directory is " + str(type(directory))
        filename_list_from_dir = [x for x in os.listdir(directory) if os.path.isfile(os.path.join(directory, x)) and os.stat(os.path.join(directory, x)).st_ctime >= self.after_time and os.stat(os.path.join(directory, x)).st_ctime <= self.before_time]
        print filename_list_from_dir
        try:
            for filename in filename_list_from_dir:
                
                if(self.stop):
                    self.push_report("T%d:Stopped\n" % threadIDx)
                    sys.exit()
                    
                if file_pattern.search(filename):
                    if(self.stop):
                        self.push_report("T%d:Stopped\n" % threadIDx)
                        sys.exit()
                    #print "Found [", file_pattern.pattern, "] in (", filename, ")"
                    #print file_pattern_str + " is in " + filename
                    if(os.path.isfile(directory+os.path.sep+filename)):
                        #print directory+ "\\" + file_pattern.search(filename).group() + "----> Is a file"
                        file_path = (directory + os.path.sep + filename)
                        if(self.ISAonly):
                            if(self.stop):
                                self.push_report("T%d:Stopped\n" % threadIDx)
                                sys.exit()
                            file_contents = re.sub("\n","",open(file_path, 'r').read(106))
                        else:
                            file_contents = re.sub("\n","",open(file_path, 'r').read())
                            
                        if(content_pattern.search(file_contents)):
                            if(self.stop):
                                self.push_report("T%d:Stopped\n" % threadIDx)
                                sys.exit()                            
                            elapsed_time = time.clock() - start_time
                            abspath = directory + os.path.sep + filename
                            if(self.show_contents == False): 
                                file_contents = ""
                            message = "FOUND: {0}\n".format(abspath)
                            self.push_files(abspath)
                            self.push_message(message)
                            self.filename_list.append(abspath)
    #                            print message
    #                            message_list.append(message)
                        else:
                            #patter not matched
                            pass
        #                            
                else:
                    if(self.stop):
                        self.push_report("T%d:Stopped\n" % threadIDx)
                        sys.exit()
                    pass
            elapsed_time = time.clock() - start_time
            report = "T%d:%.1fs\n" % (threadIDx, elapsed_time) 
            self.push_report(report)
        except WindowsError:
            if(self.stop):
                self.push_report("T%d:Stopped\n" % threadIDx)
                sys.exit()
            try:
                self.file_accessor(directory, file_pattern, content_pattern, threadIDx)
            except:
                pass

    def get_date_range(self):
        try:
            date_range = self.date_range_entry.get()
            sp = date_range.split("-")
            only_this_day = False
            pattern = '%m/%d/%Y %H:%M:%S'
            if sp[0] != '' and len(sp) == 1:
                if "*" in sp[0]:
                    only_this_day = True
                    sp[0] = sp[0].strip("*")
                date_time = sp[0]+" 0:0:0"
                pattern = '%m/%d/%Y %H:%M:%S'
                self.after_time = int(time.mktime(time.strptime(date_time, pattern)))
                if only_this_day:
                    self.before_time = self.after_time + 86399
                else:
                    self.before_time = time.time()
            elif len(sp) == 2:
                date_time0 = sp[0]+" 0:0:0"
                self.after_time = int(time.mktime(time.strptime(date_time0, pattern)))
                date_time1 = sp[1]+" 0:0:0"
                self.before_time = int(time.mktime(time.strptime(date_time1, pattern)))
            else:
                self.after_time = int(time.mktime(time.strptime(time.strftime("01/01/1970 0:0:0"), pattern)))
                self.before_time = int(time.mktime(time.strptime(time.strftime("%m/%d/%Y %H:%M:%S"), pattern)))
        except:
            self.after_time = int(time.mktime(time.strptime(time.strftime("01/01/1970 0:0:0"), pattern)))
            self.before_time = int(time.mktime(time.strptime(time.strftime("%m/%d/%Y %H:%M:%S"), pattern)))
            
        print "after ", time.ctime(self.after_time)
        print "before ", time.ctime(self.before_time)
        
    def search_callback(self):
        self.stop = False
        self.filenames = re.compile(self.filename_entry.get())
        self.contents = re.compile(self.contents_entry.get())
        self.date_range = self.get_date_range()
        global directories
        
        directories.extend([entry.strip() for entry in self.directory_entry.get().split(",") if entry not in directories])
        for i in range(0, len(directories)):
            if not ":" in directories[i]:
                if "aoa" in directories[i].lower():
                    dir = AOA+directories[i][3:]
                    if not os.path.isdir(dir):
                        directories.pop(i)
                    else:
                        directories[i] = dir
                elif "aia" in directories[i].lower():
                    dir = AIA+directories[i][3:]
                    if not os.path.isdir(dir):
                        directories.pop(i)
                    else:
                        directories[i] = dir
                elif "uoa" in directories[i].lower():
                    dir = UOA+directories[i][3:]
                    if not os.path.isdir(dir):
                        directories.pop(i)
                    else:
                        directories[i] = dir
                elif "uia" in directories[i].lower():
                    dir = UIA+directories[i][3:]
                    if not os.path.isdir(dir):
                        directories.pop(i)
                    else:
                        directories[i] = dir
                        
                        
                        
        temp = directories
        directories = []
        for i in temp:
            if i not in directories:
                directories.append(i)
            
        for thread_no in xrange(len(directories)):
            if not directories[thread_no] == "":
                threading.Thread(target=self.file_accessor, args=[directories[thread_no], self.filenames, self.contents, thread_no]).start()
                self.push_report("T%d:Started\n" % thread_no )
            else:
                directories.pop(thread_no)
                 
                 
    def stop_search(self):
        self.stop = True
        
    def clear_textbox(self):
        global directories
        self.clear_checkbuttons()
        directories = []      
        self.filename_list = []
        self.results_box.delete("%d.%d" % (1, 0), END)
        self.status_box.delete("%d.%d" % (1, 0), END)
        #self.searching_directories.delete("%d.%d" % (1,0), END)
        self.list_box.delete(0, END)
        self.status_updates.delete("1.0", END)
        #self.active_files.delete("%d.%d" % (1,0), END)
    
    def launch_nppp(self, path, args):
        #os.execl(path, args)
        if "notepad.exe" in self.editor:
            for arg in args[1:]:
                arg = ["notepad", arg]
                os.spawnv(os.P_NOWAIT, path, arg)
        else:
            os.spawnv(os.P_NOWAIT, path, args)
        #os.system("{0} {1}".format(path, args))
        
    def open_in_nppp(self, filename_str):
        if self.no_editor:
            pass
        else:
            nppp_thread = threading.Thread(target=self.launch_nppp, args=[self.editor, filename_str])
            nppp_thread.start()     
    
                
            
MainUI()
    


This post has been edited by Python_4_President: 20 November 2012 - 10:38 PM

Was This Post Helpful? 1
  • +
  • -

#4 alexr1090  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 44
  • View blog
  • Posts: 124
  • Joined: 08-May 11

Re: Tkinter Windows Question

Posted 21 November 2012 - 07:36 AM

View PostPython_4_President, on 20 November 2012 - 11:01 PM, said:

Wow. That's some pretty hard to read code.

When I launch the app (the first set of code, the second contains references to images I don't have), I get a bigger box with nothing in it, title = 'Weather', then another box title='tk', a label "Welcome to the kitchen komputer" with single button "Timer" on it.

Clicking timer kills both windows. None of those things looked like a main menu to me...

Maybe if you could paint a better picture of what you're trying to do I could give you some better advice.


EDIT: I think I get what you're saying. You want a weather thing (displays info about the weather, etc) to always be present somewhere on the screen, and you also want other stuff going on, too. Keep them separate. You could share data between the weather thing and the rest of the app via a database (eg, sqlite), or a file, or by getter methods in the weather class that allow you to access the data you need.

But for now, here's a pretty fun (though probably just as hard to read) GUI I built a while ago in Tkinter that does some nifty stuff you might be interested in. Particularly "introduce", "configure", "add_dynamic_check_buttons", and "remove_dynamic_check_buttons"


Introduce seemed like a good idea at the time because I wanted the user to do something in a Tk window before the main app started if the user had not already done that stuff (IE, specify the paths to various inbound and outbound data directories so I could create check buttons to allow easy access to some but not others, or all at once, or none at all, and where they'd like to store things, and which text editor they use to look at data. )

It launches an independent Tk window from the main app. The main app calls it again when the user wants to edit their config file. So, this could be a way to achieve your main menu deal, by utilizing independent Tk windows that can call each other.

Note that I call mainloop from inside the class constructor, whereas you initialize the class first and then enter the mainloop. Doing it inside the constructor makes it easier to start a Tk app in a new thread.

Thanks for the code. And yes, what I wanted to do was always have a weather window open. However I'm thinking about changing that up now that I had some problems with running code with multiple windows. So basically disregard my earlier code. I'm going to switch that around. So right now my buddy and I are both making the same program. I've included a screen shot of his to give you a better idea about what I'm trying to do and also to show off his great work. If you have any suggestions on how to accomplish this please let me know.

Also I'm a tkinter noob so I'm reading an online book right now that has a lot of good stuff in it. I'd suggest it to other tkinter noobs as well.

Attached image(s)

  • Attached Image

This post has been edited by alexr1090: 21 November 2012 - 07:38 AM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1