How to create backspace button in calculator? - python

import tkinter as tk
from tkinter import *
root=tk.Tk()
root.geometry("720x1320")
root.title('Calculator')
root.resizable(0, 0)
root['background']='#444444'
def bclick(num):
global exp
exp= exp+str(num)
input.set(exp)
def bclear():
global exp
exp=""
input.set("")
def bequal():
global exp
result=str(eval(exp))
input.set(result)
exp=""
exp=""
input=StringVar()
input_frame = Frame(root, width = 312, height = 100, bd = 0, highlightbackground = "black", highlightcolor = "black", highlightthickness = 1)
input_frame.pack(side = TOP)
#label
dis=Entry(input_frame,textvariable=input,bg='#cccccc',fg='#000000',justify=RIGHT,font= ("sans-serif 16"))
dis.place(x=0,y=0)
dis.pack(ipady=197)
#0 row
bC=tk.Button(root,text='C',padx=166,pady=40,bg='#FFFFFF',font=('sans-serif, 14'),command=lambda:bclear())
bC.place(x=0,y=479)
bX=tk.Button(root,text='X',padx=78,pady=40,fg='#FFFFFF',bg='#d21405',font=('sans-serif, 14'))
bX.place(x=360,y=479)
bdiv=tk.Button(root,text='÷',padx=78,pady=40,fg='#ffffff',font=('sans-serif, 14'),command=lambda:bclick("/"),bg='#1138be')
bdiv.place(x=540,y=479)
#1 row done
b7=tk.Button(root,text='7',padx=78,pady=40,bg='#FFFFFF',font=('sans-serif, 14'),command=lambda:bclick("7"))
b7.place(x=0,y=631)
b8=tk.Button(root,text='8',padx=78,pady=40,bg='#FFFFFF',font=('sans-serif, 14'),command=lambda:bclick("8"))
b8.place(x=180,y=631)
b9=tk.Button(root,text='9',padx=78,pady=40,bg='#FFFFFF',font=('sans-serif, 14'),command=lambda:bclick("9"))
b9.place(x=360,y=631)
bmul=tk.Button(root,text='×',padx=78,pady=40,bg='#1138be',fg='#ffffff',font=('sans-serif, 14'),command=lambda:bclick("*"))
bmul.place(x=540,y=631)
#2 row
b4=tk.Button(root,text='4',padx=78,pady=40,bg='#FFFFFF',font=('sans-serif, 14'),command=lambda:bclick("4"))
b4.place(x=0,y=783)
b5=tk.Button(root,text='5',padx=80,pady=40,bg='#FFFFFF',font=('sans-serif, 14'),command=lambda:bclick("5"))
b5.place(x=180,y=783)
b6=tk.Button(root,text='6',padx=79,pady=40,bg='#FFFFFF',font=('sans-serif, 14'),command=lambda:bclick("6"))
b6.place(x=360,y=783)
badd=tk.Button(root,text='+',padx=80,pady=40,bg='#1138be',fg='#ffffff',font=('sans-serif, 14'),command=lambda:bclick("+"))
badd.place(x=540,y=783)
#3row
b1=tk.Button(root,text='1',padx=78,pady=40,bg='#FFFFFF',font=('sans-serif, 14'),command=lambda:bclick("1"))
b1.place(x=0,y=935)
b2=tk.Button(root,text='2',padx=78,pady=40,bg='#FFFFFF',font=('sans-serif, 14'),command=lambda:bclick("2"))
b2.place(x=180,y=935)
b3=tk.Button(root,text='3',padx=78,pady=40,bg='#FFFFFF',font=('sans-serif, 14'),command=lambda:bclick("3"))
b3.place(x=360,y=935)
bsub=tk.Button(root,text='-',padx=82,pady=40,bg='#1138be',fg='#ffffff',font=('sans-serif, 14'),command=lambda:bclick("-"))
bsub.place(x=540,y=935)
#4 row
b0=tk.Button(root,text='0',padx=165,pady=40,bg='#FFFFFF',font=('sans-serif, 14'),command=lambda:bclick("0"))
b0.place(x=0,y=1087)
bdot=tk.Button(root,text='.',padx=85,pady=40,bg='#FFFFFF',font=('sans-serif, 14'),command=lambda:bclick("."))
bdot.place(x=360,y=1087)
bans=tk.Button(root,text='=',padx=80,pady=40,bg='#002366',fg='#ffffff',font=('sans-serif, 14'),command=lambda:bequal())
bans.place(x=540,y=1087)
root.mainloop()

The way I would do it is the same way you've done the last buttons instead of using the text='1' or a number just change it to text='backspace'.
As for the logic behind it since you have a global exp variable storing the maths equation created using the calculator you will need to remove the last bit of the equation added. i.e. if the equation was 12 + 15 + 17 - 20 you'd need to look through it and remove 20 because that was the last part of the equation. If they hit backspace again you'd need to remove the -.
So you're looking to create a function called bRemove() which removes the last part of your equation.
hint : to look at the last character in your string you can access it doing exp[len(exp)-1], you can check if this is a number and if it is you can remove it and look at the next element in the string (exp[len(exp)-2]) and then see if that is also a number.
[
also some general stack overflow advice to help you on your programming journey, it's better to be more specific about the help requested - some background information is nice and if you are going tto paste code you should use the ``` notation which lets you turn code into blocked code like this
block of code
]

Try this in my example.
from tkinter import *
#definging height and width of button.
button_height=1
button_width=3
#defining height and width of screen
screen_width =450
screen_height= 600
root=Tk()
root.title("SIMPLE CALCULATOR")
root.geometry(f"{screen_width}x{screen_height}")
root.maxsize(screen_width,screen_height)
root.minsize(screen_width,screen_height)
root.configure(background="gray")
scvalue=StringVar()
scvalue.set("")
#This defines the function for click event
def click_me(event):
value=event= event.widget.cget("text")
#print(value)
if value == "=":
try:
if scvalue.get().isdigit():
finalvalue= int(scvalue.get())
else:
finalvalue=eval(scvalue.get())
scvalue.set(finalvalue)
screen.update()
except:
scvalue.set(scvalue.get()[:-1])
screen.update()
elif value=="<=": #for backspace
scvalue.set(scvalue.get()[:-1])
screen.update()
elif value =="1/x": #for calculating reciprocal
if scvalue.get().isdigit():
val=int(scvalue.get())
scvalue.set(1/val)
screen.update()
else:
scvalue.set("Unknown error")
elif value== "C": #for clearing the screen.
scvalue.set("")
screen.update()
else:
scvalue.set(scvalue.get() +value)
screen.update()
screen= Entry(root,textvar=scvalue,font="lucida 40 bold" ,justify=RIGHT)
screen.pack(fill=X,padx=5,pady=5)
#list of all the inputs
valuelist=["/","%","C","<=","7","8","9","*","4","5","6","-","1","2","3","+","1/x","0",".","="]
#this loop
for i in range(20):
if i%4 == 0:
frame=Frame(root,bg="gray")
frame.pack()
b=Button(frame,text=valuelist[i],height=button_height,width=button_width,font="lucida 35 bold")
b.bind("<Button-1>",click_me)
b.pack(side=LEFT,padx=3,pady=3)
root.mainloop()

Related

Python textbox only updates after loop is complete

I have a very long automation that uses a very long WHILE loop. I would like to be able to have the text in a textbox update as the process is progressing. However, based on the research I have done, this does not seem to be possible and all of the results "dump" at once when the WHILE loop completes. This is useless for me.
I would love it if the textbox could update as the SHELL updates as that follows along synchronously with the actual process.
I have made a simple TEST file to see if I can get it to work. Here is the code for the TEST file:
from tkinter import *
import time
import tkinter as tk
import tkinter.font as tkFont
root=Tk()
myFont = tkFont.Font(family = 'Helvetica', size = 18, weight = 'bold')
text_cell_bg="cyan" #TEXT CELL BACKGROUND COLOR
text_cell_fg="black" #TEXT CELL TEXT COLOR
text_cell_height=2 #TEXT CELL HEIGHT
text_cell_width=30 #TEXT CELL WIDTH
button_bg="blue" #BUTTON CELL BACKGROUND COLOR
button_fg="white" #BUTTON CELL TEXT COLOR
button_height=2 #BUTTON CELL HEIGTH
button_width=10 #BUTTON CELL WIDTH
textbox=Text(root)
textbox.insert(END, 'Default Text\n\n')
def count_print ():
count = 0
letter = "A"
while count < 5:
print("Count = ",count,". Letter = ",letter,".")
textbox_value = "Count = {}. Letter = {}.\n".format(count,letter)
textbox.insert(1.0, textbox_value)
count += 1
time.sleep(1)
textbox.pack()
button1=tk.Button(root, text='output', command=count_print, font = myFont,
height=button_height,
width=button_width,
bg = button_bg,
fg = button_fg)
button1.pack()
root.mainloop()
You can call your function using Tkinter from inside the function itself.
Tkinter has a special function for this.
from tkinter import *
import time
import tkinter as tk
import tkinter.font as tk_font
root = Tk()
myFont = tk_font.Font(family='Helvetica', size=18, weight='bold')
text_cell_bg = "cyan" # TEXT CELL BACKGROUND COLOR
text_cell_fg = "black" # TEXT CELL TEXT COLOR
text_cell_height = 2 # TEXT CELL HEIGHT
text_cell_width = 30 # TEXT CELL WIDTH
button_bg = "blue" # BUTTON CELL BACKGROUND COLOR
button_fg = "white" # BUTTON CELL TEXT COLOR
button_height = 2 # BUTTON CELL HEIGHT
button_width = 10 # BUTTON CELL WIDTH
textbox = Text(root)
textbox.insert(END, 'Default Text\n\n')
def count_print(n_times=0): # tell your function how many times you have called it
(on the first call this will be 0)
n = n_times + 1
letter = "A"
if n <= 5:
print("Count = ", n, ". Letter = ", letter, ".")# you can change the n var on these lines to n_times if you want 0-4 rather than printing 1-5.
textbox_value = "Count = {}. Letter = {}.\n".format(n, letter)
textbox.insert(1.0, textbox_value)
root.after(1000, count_print, n) # this number replaces and time.sleep it's set to 1 second right now it also call the count_print function passing n as a variable
textbox.pack()
button1 = tk.Button(root, text='output', command=count_print, font=myFont,
height=button_height,
width=button_width,
bg=button_bg,
fg=button_fg)
button1.pack()
root.mainloop()
I hope this helps. also, I don't use Tkinter for my projects and am definitely not very good at using it either so credit for me knowing about root. after goes to this answer,
Make Tkinter force a "refresh" a text widget in mid-command?

Reset Game designed with tkinter

I am a beginner in python and have designed a "Count the Colored Balls Game". I am now stuck at figuring out best way of restarting the game after one round is over.
Currently:
When you hit start game, it populates 25 random colored balls and then gives user 10 seconds to count red and green balls. It them shows the final answer as a messagebox. Once i dismiss the message box, the balls remain on screen and then when I hit start game, 25 more balls are piled on.
What I want:
How do I reset the game such that when I complete one round, and hit start game, it clears the existing game and restarts.
My Code
from tkinter import *
import random
import time
#from tkinter import ttk
from tkinter import messagebox
#creating a list of colors:
colors=["blue","red","yellow","green","red","pink","red","black","green","cyan"]
#creating global variables and inititializing:
global i
i=0
global redcount
redcount=0
global greencount
greencount=0
global canvas
def startgame():
global canvas
x=startclick()
if x==1:
time.sleep(5)
messagebox.showinfo("Answer"," number of red balls "+str(redcount)+
" and number of green balls is "+str(greencount))
#==============================================
def startclick():
global i
global canvas
global redcount
global greencount
for i in range(1,26):
m=random.randint(0,10)
if m == 1 or m ==4 or m==6:
redcount=redcount+1
if m == 3 or m==8:
greencount=greencount+1
try:
a=random.randint(50,250)
b=random.randint(50,350)
canvas.create_oval(a,b,a+50,b+50,outline="white",fill=colors[m],width=1)
canvas.update()
except:
print()
return(1)
#===============================================
#===============================================
root=Tk()
root.title("Count The Colors")
root.geometry("800x800+0+0") #dimension and position of main frame
#creating canvas of the game
canvas = Canvas(width=600,height=500, bg = "#d2b48c")
canvas.place(x=20, y=20)
w=Label(root,text="Can you count the number of red and green balls?",bg="black",fg="yellow")
w.place(x=30,y=500)
y=Label(root, text="You have 10 seconds to answer...press start to play",
bg="white", fg="black")
y.place(x=20,y=550)
b=Button(root,text = "Start", bg="#fd5f00", width=20, height=1,
font=("Times",12,"bold"), fg="brown", command = startgame)
b.place(x=20, y=600)
root.mainloop()
Appreciate some help on this
First you are not following up programming conventions but anyways you can clear canvas everytime your startclick() function is being called. Simply add canvas.delete("all") to delete the previously created ovals.
def startclick():
global i
global canvas
global redcount
global greencount
canvas.delete("all") #This is your solution
for i in range(1, 26):
m = random.randint(0, 10)
if m == 1 or m == 4 or m == 6:
redcount = redcount + 1
if m == 3 or m == 8:
greencount = greencount + 1
try:
a = random.randint(50, 250)
b = random.randint(50, 350)
canvas.create_oval(a, b, a + 50, b + 50, outline="white", fill=colors[m], width=1)
canvas.update()
except:
print()
return (1)

I'm trying to make a simple line drawing program with tkinter but it won't work

I'm trying to make this really simple program, all it does is store the current x/y pos of the mouse on the canvas and then use them to draw a line when you click for the second time. I've already bound it and I'm not getting any errors, it seems like it's not even being activated. Any help is greatly appreciated
from tkinter import *
main = Tk()
c = Canvas(main, width=600, height=600)
c.pack()
#For colored lines
presses = 0
def click(event):
if presses == 0:
initX = int(c.canvasx(event.x))
initY = int(c.canvasy(event.y))
presses == 1
elif presses == 1:
c.create_line(initX, initY,
int(c.canvasx(event.x)),
int(c.canvasy(event.y)))
presses == 0
c.bind("<Button-1>", click)
mainloop()
How does something like this work for you?
from tkinter import *
main = Tk()
c = Canvas(main, width=600, height=600)
c.pack()
line = []
def click(event):
global line
X = int(c.canvasx(event.x))
Y = int(c.canvasy(event.y))
line.append((X,Y))
if len(line) > 1:
startX,startY = line[-2]
c.create_line(startX, startY, X, Y)
c.bind("<Button-1>", click)
mainloop()
I've changed around your code a bit to store a list of the X,Y coordinates that have been clicked on. If more than 1 point on the screen has been clicked, it will draw a line between the current point clicked on and the last point clicked on.
Reason your code wasn't working was that initX and initY are forgotten in between calls on the the click function. Adding them to a list solves this.

Python: Connect 4 with TKinter

Alrighty I'm trying to implement a GUI for a game of Connect Four by using TKinter. Now I have the grid and everything set up what I'm having trouble with is getting the chip to show up on the board.
Here is my output:
What I'm trying to do is make it so when I click one of the bottom column buttons a chip appears (and since this is connect four it should go from bottom to top)
Here is my code:
from Tkinter import *
from connectfour import *
from minimax import *
from player import *
import tkMessageBox
class ConnectFourGUI:
def DrawGrid(self):
for i in range(0,self.cols+1):
self.c.create_line((i+1)*self.mag,self.mag,\
(i+1)*self.mag,(self.rows+1)*self.mag)
for i in range(0,self.rows+1):
self.c.create_line(self.mag,(i+1)*self.mag,\
self.mag*(1+self.cols),(i+1)*self.mag)
def __init__(self,wdw):
wdw.title("Connect Four")
self.mag = 60
self.rows = 6
self.cols = 7
self.c = Canvas(wdw,\
width=self.mag*self.cols+2*self.mag,\
height = self.mag*self.rows+2*self.mag,\
bg='white')
self.c.grid(row=1,column=1,columnspan=2)
rlabel=Label(root, text="Player1:")
rlabel.grid(row=0,column=0)
self.player1_type=StringVar(root)
options= ["Human", "Random", "Minimax"]
self.player1_type.set(options[2])
self.rowbox=OptionMenu(root, self.player1_type, *options)
self.rowbox.grid(row=0, column=1)
rlabel2=Label(root, text="Player2:")
rlabel2.grid(row=0,column=2)
self.player2_type=StringVar(root)
self.player2_type.set(options[0])
self.rowbox=OptionMenu(root, self.player2_type, *options)
self.rowbox.grid(row=0, column=3)
begin=Button(root, text="Start", command=self.game_start)
begin.grid(row=0, column=4)
self.c.grid(row=1, column=0, columnspan=7)
play_col=[]
for i in range(self.cols):
play_col.append(Button(root, text= "Col %d" %i, command=lambda col= i: self.human_play(col)))
play_col[i].grid(row=10,column="%d"%i)
## self.DrawCircle(1,1,1)
## self.DrawCircle(2,2,1)
## self.DrawCircle(5,3,2)
self.DrawGrid()
self.brd = ConnectFour()
def game_start(self):
self.board=ConnectFour()
print self.player1_type.get()
print self.player2_type.get()
if self.player1_type.get()=="Random":
self.player1 = RandomPlayer(playernum=1)
if self.player2_type.get()== "Random" or self.player2_type.get() == "Minimax":
tkMessageBox.showinfo("Bad Choice", "You Have to choose At least 1 Human Player")
else:
self.player
elif self.player1_type.get()=="Minimax":
self.player1=MinimaxPlayer(playernum=2, ply_depth=4, utility=SimpleUtility(5,1))
if self.player2_type.get()== "Random" or self.player2_type.get() == "Minimax":
tkMessageBox.showinfo("Bad Choice", "You Have to choose At least 1 Human Player")
elif self.player1_type.get()=="Human":
self.player1=Human(playernum=1)
if self.player2_type.get()=="Human":
self.player2=Human(playernum=2)
elif self.player2_type.get()=="Random":
self.player2=RandomPlayer(playernum=2)
elif self.player2_type.get()=="Minimax":
self.player2=MinimaxPlayer(playernum=2, ply_depth=4, utility=SimpleUitlity(5,1))
#self.currentplayer==1
#self.draw()
def human_play(self, col):
if self.player1_type.get()=="Human" and self.player2_type.get() =="Human":
while True:
self.DrawCircle(row,col,1)
if self.brd.is_game_over() is None:
self.DrawCircle(row,col,2)
if self.brd.is_game_over() is None:
pass
else:
print "Player 2 wins!"
break
else:
print "Player 1 wins!"
break
def DrawCircle(self,row,col,player_num):
if player_num == 1:
fill_color = 'red'
elif player_num == 2:
fill_color = 'black'
#(startx, starty, endx, endy)
self.c.create_oval(col*self.mag,row*self.mag,(col+1)*self.mag,(row+1)*self.mag,fill=fill_color)
root=Tk()
ConnectFourGUI(root)
root.mainloop()
I know I'm supposed to call the DrawCircle function in the Human Play function, I'm just unsure as to how I'm supposed to set it all up. any advice as to how I could go about this would be greatly appreciated!
Your code is depending on a few packages I don't have, so I can't be more specific, but the way I'd go about doing something like this is to track the X and Y coords of the chip works out the the row and column that it'd go into in the widget.
You'll need to create a location object, a tuple maybe, and then a way to translate the location object into a drawable location. Then it's just a matter of incrementing either X or Y and detecting if there's a chip below it.

In Tkinter how can I correct this loop/function so that each button changes a value to the value of the button?

In Tkinter how can I correct this loop/function so that each button changes a value to the value of the button?
This is a simplified version of my code, at the moment each button changes the value of size to 15 rather than the number on the button. I was wondering if there was anyway of fixing this loop without printing each individual button and value without a loop?
from Tkinter import *
size = 7
def AI():
AIBoard = Tk()
AIBoard.title("Board Select")
BoardSize = Label(AIBoard, text = "Please pick a board size: ", font = ('Helvetica',20))
BoardSize.pack(side = 'top')
for a in range(5,16,1):
sizeBut = Button(AIBoard, text = a, width = 5, command = lambda: inputBoardSize(a))
sizeBut.pack(side = 'left')
AIBoard.mainloop()
def inputBoardSize(x):
size = x
print size
AI()
Thanx
Change your lambda to bind the value at the time the anonymous function is created.
lambda a=a: inputBoardSize(a)

Categories

Resources