Automatically add a scroll in Multiline when the number of rows is exceeded - scrollbar

Tell me how to automatically connect scrolling in PySimpleGUI Multiline when the number of lines entered exceeds. For example, to enable scrolling when there are more than 5 lines
sg.Multiline(size=(42, 5))

import PySimpleGUI as sg
def begin_window():
layout = [[sg.Multiline(size=(42, 5), autoscroll=True)]]
# Create the Window
window = sg.Window('Title', layout)
# window['-INDEX-'].bind("<Return>", "_Enter")
# Event Loop to process "events" and get the "values" of the inputs
while True:
event, values = window.read()
if event == sg.WIN_CLOSED or event == '-CANCEL-': # if user closes window or clicks cancel
break
window.close()
return
if __name__ == '__main__':
begin_window()
Scroll appears immediately, when the field is empty

The method is to hack the method set for the tk.Scrollbar, and it is only work for the vertical scrollbar for following code.
import PySimpleGUI as sg
def scrollbar_set(self, lo, hi):
if float(lo) <= 0.0 and float(hi) >= 1.0:
self.pack_forget()
elif self.cget("orient") != sg.tk.HORIZONTAL:
self.pack(side=sg.tk.RIGHT, fill=sg.tk.Y)
self.old_set(lo, hi)
sg.tk.Scrollbar.old_set = sg.tk.Scrollbar.set
sg.tk.Scrollbar.set = scrollbar_set
layout = [
[sg.Text('Auto hide scrollbar', size=25)],
[sg.Multiline(size=(20, 5), expand_x=True, expand_y=True, key='-ML-')]]
window = sg.Window('Title', layout, finalize=True)
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
window.close()

Related

Is there a way to prevent scrolling of an iPython notebook, when calling 'mouse_scroll' event over a plot figure inside a cell?

Is there a way to prevent the entire notebook from scrolling, when scrolling over the figure to cycle through the images as shown in this example (and reproduced below): https://matplotlib.org/stable/gallery/event_handling/image_slices_viewer.html
import numpy as np
import matplotlib.pyplot as plt
# Fixing random state for reproducibility
np.random.seed(19680801)
class IndexTracker:
def __init__(self, ax, X):
self.ax = ax
ax.set_title('use scroll wheel to navigate images')
self.X = X
rows, cols, self.slices = X.shape
self.ind = self.slices//2
self.im = ax.imshow(self.X[:, :, self.ind])
self.update()
def on_scroll(self, event):
print("%s %s" % (event.button, event.step))
if event.button == 'up':
self.ind = (self.ind + 1) % self.slices
else:
self.ind = (self.ind - 1) % self.slices
self.update()
def update(self):
self.im.set_data(self.X[:, :, self.ind])
self.ax.set_ylabel('slice %s' % self.ind)
self.im.axes.figure.canvas.draw_idle()
def plot(X):
mpl.rc('image', cmap='gray')
fig, ax = plt.subplots(1, 1)
plot = IndexTracker(ax, X)
fig.canvas.mpl_connect('scroll_event', plot.scroll)
plt.show()
On VS Code, using the,
%matplotlib widget
backend, I call the following function in a new code cell:
X = np.random.rand(20, 20, 40)
plot(X)
I am able to successfully generate an interactive plot in an iPython notebook, whereby scrolling over the figure scrolls through the "image slices". The scroll event only works if I hover the cursor over the plot/figure (as it should), however the entire notebook scrolls as well, thereby moving the cursor out of the figure frame.
Is there a way to prevent the notebook from scrolling when the cursor is hovering over a figure in the cell output?

bokeh selected.on_change not working for my current setup

Basically, this is an interactive heatmap but the twist is that the source is updated by reading values from a file that gets updated regularly.
dont bother about the class "generator", it is just for keeping data and it runs regularly threaded
make sure a file named "Server_dump.txt" exists in the same directory of the script with a single number greater than 0 inside before u execute the bokeh script.
what basically happens is i change a number inside the file named "Server_dump.txt" by using echo 4 > Server_dump.txt on bash,
u can put any number other than 4 and the script automatically checks the file and plots the new point.
if u don't use bash, u could use a text editor , replace the number and save, and all will be the same.
the run function inside the generator class is the one which checks if this file was modified , reads the number, transforms it into x& y coords and increments the number of taps associated with these coords and gives the source x,y,taps values based on that number.
well that function works fine and each time i echo a number , the correct rectangle is plotted but,
now I want to add the functionality of that clicking on a certain rectangle triggers a callback to plot a second graph based on the coords of the clicked rectangle but i can't even get it to trigger even though i have tried other examples with selected.on_change in them and they worked fine.
*if i increase self.taps for a certain rect by writing the number to the file multiple times, color gets updated but if i hover over the rect it shows me the past values and not the latest value only .
my bokeh version is 1.0.4
from functools import partial
from random import random,randint
import threading
import time
from tornado import gen
from os.path import getmtime
from math import pi
import pandas as pd
from random import randint, random
from bokeh.io import show
from bokeh.models import LinearColorMapper, BasicTicker, widgets, PrintfTickFormatter, ColorBar, ColumnDataSource, FactorRange
from bokeh.plotting import figure, curdoc
from bokeh.layouts import row, column, gridplot
source = ColumnDataSource(data=dict(x=[], y=[], taps=[]))
doc = curdoc()
#sloppy data receiving function to change data to a plottable shape
class generator(threading.Thread):
def __init__(self):
super(generator, self).__init__()
self.chart_coords = {'x':[],'y':[],'taps':[]}
self.Pi_coords = {}
self.coord = 0
self.pos = 0
self.col = 0
self.row = 0
self.s = 0
self.t = 0
def chart_dict_gen(self,row, col):
self.col = col
self.row = row+1
self.chart_coords['x'] = [i for i in range(1,cla.row)]
self.chart_coords['y'] = [i for i in range(cla.col, 0, -1)] #reversed list because chart requires that
self.chart_coords['taps']= [0]*(row * col)
self.taps = [[0 for y in range(col)] for x in range(row)]
def Pi_dict_gen(self,row,col):
key = 1
for x in range(1,row):
for y in range(1,col):
self.Pi_coords[key] = (x,y)
key = key + 1
def Pi_to_chart(self,N):
x,y = self.Pi_coords[N][0], self.Pi_coords[N][1]
return x,y
def run(self):
while True:
if(self.t == 0):
self.t=1
continue
time.sleep(0.1)
h = getmtime("Server_dump.txt")
if self.s != h:
self.s = h
with open('Server_dump.txt') as f:
m = next(f)
y,x = self.Pi_to_chart(int(m))
self.taps[x][y] += 1
# but update the document from callback
doc.add_next_tick_callback(partial(update, x=x, y=y, taps=self.taps[x][y]))
cla = generator()
cla.chart_dict_gen(15,15)
cla.Pi_dict_gen(15, 15)
x = cla.chart_coords['x']
y = cla.chart_coords['y']
taps = cla.chart_coords['taps']
#gen.coroutine
def update(x, y, taps):
taps += taps
print(x,y,taps)
source.stream(dict(x=[x], y=[y], taps=[taps]))
colors = ["#CCEBFF","#B2E0FF","#99D6FF","#80CCFF","#66c2FF","#4DB8FF","#33ADFF","#19A3FF", "#0099FF", "#008AE6", "#007ACC","#006BB2", "#005C99", "#004C80", "#003D66", "#002E4C", "#001F33", "#000F1A", "#000000"]
mapper = LinearColorMapper(palette=colors, low= 0, high= 15) #low = min(cla.chart_coords['taps']) high = max(cla.chart_coords['taps'])
TOOLS = "hover,save,pan,box_zoom,reset,wheel_zoom"
p = figure(title="Tou",
x_range=list(map(str,x)),
y_range=list(map(str,reversed(y))),
x_axis_location="above",
plot_width=900, plot_height=400,
tools=TOOLS, toolbar_location='below',
tooltips=[('coords', '#y #x'), ('taps', '#taps%')])
p.grid.grid_line_color = "#ffffff"
p.axis.axis_line_color = "#ef4723"
p.axis.major_tick_line_color = "#af0a36"
p.axis.major_label_text_font_size = "7pt"
p.xgrid.grid_line_color = None
p.ygrid.grid_line_color = None
p.rect(x="x", y="y",
width=0.9, height=0.9,
source=source,
fill_color={'field': 'taps', 'transform': mapper},
line_color = "#ffffff",
)
color_bar = ColorBar(color_mapper=mapper,
major_label_text_font_size="7pt",
ticker=BasicTicker(desired_num_ticks=len(colors)),
formatter=PrintfTickFormatter(format="%d%%"),
label_standoff=6, border_line_color=None, location=(0, 0))
curdoc().theme = 'dark_minimal'
def ck(attr, old, new):
print('here') #doesn't even print hi in the terminal if i click anywhere
source.selected.on_change('indices', ck)
p.add_layout(color_bar, 'right')
doc.add_root(p)
thread = cla
thread.start()
i wanted even to get a printed hi in the terminal but nothing
You have not actually added any selection tool at all to your plot, so no selection is ever made. You have specified:
TOOLS = "hover,save,pan,box_zoom,reset,wheel_zoom"
Those are the only tools that will be added, and none of them make selections, there for nothing will cause source.selection.indices to ever be updated. If you are looking for selections based on tap, you must add a TapTool, e.g. with
TOOLS = "hover,save,pan,box_zoom,reset,wheel_zoom,tap"
Note that there will not be repeated callbacks if you tap the same rect multiple times. The callback only fires when the selection changes and clicking the same glyph twice in a row results in an identical selection.

How do I connect buttons to right answers in a dictionary with pygame? [duplicate]

I was wondering how to write code that would detect the mouse clicking on a sprite. For example:
if #Function that checks for mouse clicked on Sprite:
print ("You have opened a chest!")
I assume your game has a main loop, and all your sprites are in a list called sprites.
In your main loop, get all events, and check for the MOUSEBUTTONDOWN or MOUSEBUTTONUP event.
while ... # your main loop
# get all events
ev = pygame.event.get()
# proceed events
for event in ev:
# handle MOUSEBUTTONUP
if event.type == pygame.MOUSEBUTTONUP:
pos = pygame.mouse.get_pos()
# get a list of all sprites that are under the mouse cursor
clicked_sprites = [s for s in sprites if s.rect.collidepoint(pos)]
# do something with the clicked sprites...
So basically you have to check for a click on a sprite yourself every iteration of the mainloop. You'll want to use mouse.get_pos() and rect.collidepoint().
Pygame does not offer event driven programming, as e.g. cocos2d does.
Another way would be to check the position of the mouse cursor and the state of the pressed buttons, but this approach has some issues.
if pygame.mouse.get_pressed()[0] and mysprite.rect.collidepoint(pygame.mouse.get_pos()):
print ("You have opened a chest!")
You'll have to introduce some kind of flag if you handled this case, since otherwise this code will print "You have opened a chest!" every iteration of the main loop.
handled = False
while ... // your loop
if pygame.mouse.get_pressed()[0] and mysprite.rect.collidepoint(pygame.mouse.get_pos()) and not handled:
print ("You have opened a chest!")
handled = pygame.mouse.get_pressed()[0]
Of course you can subclass Sprite and add a method called is_clicked like this:
class MySprite(Sprite):
...
def is_clicked(self):
return pygame.mouse.get_pressed()[0] and self.rect.collidepoint(pygame.mouse.get_pos())
So, it's better to use the first approach IMHO.
The MOUSEBUTTONDOWN event occurs once when you click the mouse button and the MOUSEBUTTONUP event occurs once when the mouse button is released. The pygame.event.Event() object has two attributes that provide information about the mouse event. pos is a tuple that stores the position that was clicked. button stores the button that was clicked. Each mouse button is associated a value. For instance the value of the attributes is 1, 2, 3, 4, 5 for the left mouse button, middle mouse button, right mouse button, mouse wheel up respectively mouse wheel down. When multiple keys are pressed, multiple mouse button events occur. Further explanations can be found in the documentation of the module pygame.event.
Use the rect attribute of the pygame.sprite.Sprite object and the collidepoint method to see if the Sprite was clicked.
Pass the list of events to the update method of the pygame.sprite.Group so that you can process the events in the Sprite class:
class SpriteObject(pygame.sprite.Sprite):
# [...]
def update(self, event_list):
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN:
if self.rect.collidepoint(event.pos):
# [...]
my_sprite = SpriteObject()
group = pygame.sprite.Group(my_sprite)
# [...]
run = True
while run:
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
run = False
group.update(event_list)
# [...]
Minimal example: repl.it/#Rabbid76/PyGame-MouseClick
import pygame
class SpriteObject(pygame.sprite.Sprite):
def __init__(self, x, y, color):
super().__init__()
self.original_image = pygame.Surface((50, 50), pygame.SRCALPHA)
pygame.draw.circle(self.original_image, color, (25, 25), 25)
self.click_image = pygame.Surface((50, 50), pygame.SRCALPHA)
pygame.draw.circle(self.click_image, color, (25, 25), 25)
pygame.draw.circle(self.click_image, (255, 255, 255), (25, 25), 25, 4)
self.image = self.original_image
self.rect = self.image.get_rect(center = (x, y))
self.clicked = False
def update(self, event_list):
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN:
if self.rect.collidepoint(event.pos):
self.clicked = not self.clicked
self.image = self.click_image if self.clicked else self.original_image
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
sprite_object = SpriteObject(*window.get_rect().center, (128, 128, 0))
group = pygame.sprite.Group([
SpriteObject(window.get_width() // 3, window.get_height() // 3, (128, 0, 0)),
SpriteObject(window.get_width() * 2 // 3, window.get_height() // 3, (0, 128, 0)),
SpriteObject(window.get_width() // 3, window.get_height() * 2 // 3, (0, 0, 128)),
SpriteObject(window.get_width() * 2// 3, window.get_height() * 2 // 3, (128, 128, 0)),
])
run = True
while run:
clock.tick(60)
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
run = False
group.update(event_list)
window.fill(0)
group.draw(window)
pygame.display.flip()
pygame.quit()
exit()
See further Creating multiple sprites with different update()'s from the same sprite class in Pygame
The current position of the mouse can be determined via pygame.mouse.get_pos(). The return value is a tuple that represents the x and y coordinates of the mouse cursor. pygame.mouse.get_pressed() returns a list of Boolean values ​​that represent the state (True or False) of all mouse buttons. The state of a button is True as long as a button is held down. When multiple buttons are pressed, multiple items in the list are True. The 1st, 2nd and 3rd elements in the list represent the left, middle and right mouse buttons.
Detect evaluate the mouse states in the Update method of the pygame.sprite.Sprite object:
class SpriteObject(pygame.sprite.Sprite):
# [...]
def update(self, event_list):
mouse_pos = pygame.mouse.get_pos()
mouse_buttons = pygame.mouse.get_pressed()
if self.rect.collidepoint(mouse_pos) and any(mouse_buttons):
# [...]
my_sprite = SpriteObject()
group = pygame.sprite.Group(my_sprite)
# [...]
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
group.update(event_list)
# [...]
Minimal example: repl.it/#Rabbid76/PyGame-MouseHover
import pygame
class SpriteObject(pygame.sprite.Sprite):
def __init__(self, x, y, color):
super().__init__()
self.original_image = pygame.Surface((50, 50), pygame.SRCALPHA)
pygame.draw.circle(self.original_image, color, (25, 25), 25)
self.hover_image = pygame.Surface((50, 50), pygame.SRCALPHA)
pygame.draw.circle(self.hover_image, color, (25, 25), 25)
pygame.draw.circle(self.hover_image, (255, 255, 255), (25, 25), 25, 4)
self.image = self.original_image
self.rect = self.image.get_rect(center = (x, y))
self.hover = False
def update(self):
mouse_pos = pygame.mouse.get_pos()
mouse_buttons = pygame.mouse.get_pressed()
#self.hover = self.rect.collidepoint(mouse_pos)
self.hover = self.rect.collidepoint(mouse_pos) and any(mouse_buttons)
self.image = self.hover_image if self.hover else self.original_image
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
sprite_object = SpriteObject(*window.get_rect().center, (128, 128, 0))
group = pygame.sprite.Group([
SpriteObject(window.get_width() // 3, window.get_height() // 3, (128, 0, 0)),
SpriteObject(window.get_width() * 2 // 3, window.get_height() // 3, (0, 128, 0)),
SpriteObject(window.get_width() // 3, window.get_height() * 2 // 3, (0, 0, 128)),
SpriteObject(window.get_width() * 2// 3, window.get_height() * 2 // 3, (128, 128, 0)),
])
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
group.update()
window.fill(0)
group.draw(window)
pygame.display.flip()
pygame.quit()
exit()
The pygame documentation for mouse events is here.
You can either use the pygame.mouse.get_pressed method in collaboration with the pygame.mouse.get_pos (if needed).
Remember to use the mouse click event via a main event loop. The reason why the event loop is better is due to "short clicks". You may not notice these on normal machines, but computers that use tap-clicks on trackpads have excessively small click periods. Using the mouse events will prevent this.
EDIT:
To perform pixel perfect collisions use pygame.sprite.collide_rect() found on their docs for sprites.
I was looking for the same answer to this question and after much head scratching this is the answer I came up with:
# Python 3.4.3 with Pygame
from sys import exit
import pygame
pygame.init()
WIDTH = HEIGHT = 300
window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Crash!')
# Draw Once
rectangle = pygame.draw.rect(window, (255, 0, 0), (100, 100, 100, 100))
pygame.display.update()
# Main Loop
while True:
# Mouse position and button clicking
pos = pygame.mouse.get_pos()
pressed1 = pygame.mouse.get_pressed()[0]
# Check if rectangle collided with pos and if the left mouse button was pressed
if rectangle.collidepoint(pos) and pressed1:
print("You have opened a chest!")
# Quit pygame
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()

Tooltip message when hovering on cell with mouse in wx.grid wxpython

I have a wx.grid table, I want to set a tooltip when I hover on a cell, I tried Mike Driscoll's recommendation below, it works, but I can't select multiple cells with mouse drag anymore, it allows me to select only 1 cell max, please help:
self.grid_area.GetGridWindow().Bind(wx.EVT_MOTION, self.onMouseOver)
def onMouseOver(self, event):
'''
Method to calculate where the mouse is pointing and
then set the tooltip dynamically.
'''
# Use CalcUnscrolledPosition() to get the mouse position
# within the
# entire grid including what's offscreen
x, y = self.grid_area.CalcUnscrolledPosition(event.GetX(),event.GetY())
coords = self.grid_area.XYToCell(x, y)
# you only need these if you need the value in the cell
row = coords[0]
col = coords[1]
if self.grid_area.GetCellValue(row, col):
if self.grid_area.GetCellValue(row, col) == "ABC":
event.GetEventObject().SetToolTipString("Code is abc")
elif self.grid_area.GetCellValue(row, col) == "XYZ":
event.GetEventObject().SetToolTipString("code is xyz")
else:
event.GetEventObject().SetToolTipString("Unknown code")
OK, I found the solution, I have to skip the event:
def onMouseOver(self, event):
'''
Method to calculate where the mouse is pointing and
then set the tooltip dynamically.
'''
# Use CalcUnscrolledPosition() to get the mouse position
# within the
# entire grid including what's offscreen
x, y = self.grid_area.CalcUnscrolledPosition(event.GetX(),event.GetY())
coords = self.grid_area.XYToCell(x, y)
# you only need these if you need the value in the cell
row = coords[0]
col = coords[1]
if self.grid_area.GetCellValue(row, col):
if self.grid_area.GetCellValue(row, col) == "ABC":
event.GetEventObject().SetToolTipString("Code is abc")
elif self.grid_area.GetCellValue(row, col) == "XYZ":
event.GetEventObject().SetToolTipString("code is xyz")
else:
event.GetEventObject().SetToolTipString("Unknown code")
event.Skip()
Thanks
best regards
# GreenAsJade
Since I cannot comment due to reputation i am asnwering your question here!
why: what was going wrong, how does this fix it?
If you check the difference between your event Hanlder and #alwbtc's event Handler only difference is event.Skip()
Any time the wx.EVT_xx is bind with custom method in code, wxpython override default definition. For that reason event handling ends in onMouseOver. event.Skip() will propagate the event to _core of wxptyhon allowing it to execute default event handlers.
Hope this answers your question!

Python 3.3 Button hiding coding doesnt work

In my game after pressing the start button I wanted to hide the button to stop the user from pressing the start button again and making more bubbles appear. I tried to make a variable equal to 0. Then every time the start button was clicked the value would go up by 1. If the value was 2 then the start button would disappear or become inactive. By trying to add this 'hiding button code' its only showing one bubble on my screen when the start button is clicked. Oh and the started button doesn't hide... ._.
from tkinter import *
import random
import tkinter as tk
# Message box for score to tell user if they have won or lost
from tkinter.messagebox import showinfo
class BubbleFrame:
def __init__(self, root, name):
self.name = name
root.title("Math Bubbles")
self.bubbles = {} # this will hold bubbles ids, positions and velocities
self.score = 0
self.buttonclick = 0 # Newly added in attempt to hide button
Button(root, text="Start", width=8, bg="Pink", command=self.make_bubbles).pack() # This button starts the game, making the bubbles move across the screen
Button(root, text="Quit", width=8, bg="Yellow",command=quit).pack()
self.canvas = Canvas(root, width=800, height=650, bg='#afeeee')
self.canvas.create_text(400, 30, fill="darkblue", font="Times 20 italic bold", text="Click the bubbles that are answers in the two times tables.")
#Shows score at beinging of the game
self.current_score = self.canvas.create_text(200, 60, fill="darkblue", font="Times 15 italic bold", text="Your score is: 0")
self.canvas.pack()
def make_bubbles(self):
for each_no in range(1, 21):
self.buttonclick += 1 #Newly added in attempt to hide button
xval = random.randint(5, 765)
yval = random.randint(5, 615)
COLOURS = ('#00ff7f', '#ffff00', '#ee82ee', '#ff69b4', '#fff0f5') # CAPS represents a constant variable
colour = random.choice(COLOURS) # This picks a colour randomly
oval_id = self.canvas.create_oval(xval, yval, xval + 60, yval + 60,fill=colour, outline="#000000", width=5, tags="bubble")
text_id = self.canvas.create_text(xval + 30, yval + 30, text=each_no, tags="bubble")
self.canvas.tag_bind("bubble", "<Button-1>", lambda x: self.click(x))
self.bubbles[oval_id] = (xval, yval, 0, 0, each_no, text_id) # add bubbles to dictionary
if buttonclick == 2: #Newly added in attempt to hide button
showinfo("Oh No", "Sorry %s, but the game is already started" % self.name)
def click(self, event):
if self.canvas.find_withtag(CURRENT):
item_uid = event.widget.find_closest(event.x, event.y)[0]
is_even = False
try: # clicked oval
self.bubbles[item_uid]
except KeyError: # clicked oval
for key, value in self.bubbles.iteritems():
if item_uid == value[5]: # comparing to text_id
if value[4] % 2 == 0:
is_even = True
self.canvas.delete(key) # deleting oval
self.canvas.delete(item_uid) # deleting text
else:
if self.bubbles[item_uid][4] % 2 == 0:
is_even = True
self.canvas.delete(item_uid) # deleting oval
self.canvas.delete(self.bubbles[item_uid][5]) # deleting text
if is_even:
self.score += 1
else:
self.score -= 1
showinfo("Oh no!", "%s! You clicked the wrong bubble, please start again." % self.name)
if self.score == 10:
#Tells user You won! if score is 10
showinfo("Winner", "You won %s!" % self.name)
self.canvas.delete(self.current_score)
#Shows updated score on canvas
self.current_score = self.canvas.create_text(200, 60, fill="darkblue", font="Times 15 italic bold", text="Your score is: %s"%self.score)
def bubble_move(self, root):
for oval_id, (x, y, dx, dy, each_no, text_id) in self.bubbles.items():
# update velocities and positions
dx += random.randint(-1, 1)
dy += random.randint(-1, 1)
# dx and dy should not be too large
dx, dy = max(-5, min(dx, 5)), max(-5, min(dy, 5))
# bounce off walls
if not 0 < x < 770:
dx = -dx
if not 0 < y < 620:
dy = -dy
# apply new velocities
self.canvas.move(oval_id, dx, dy)
self.canvas.move(text_id, dx, dy)
self.bubbles[oval_id] = (x + dx, y + dy, dx, dy, each_no, text_id)
# have mainloop repeat this after 100 ms
root.after(100, self.bubble_move, root)
if __name__ == "__main__":
root = Tk()
root.title("Welcome")
Label(root, text="Welcome to Math bubbles, what is your name?").pack()
name = Entry(root)
name.pack()
def submit(name, root):
root.destroy()
root = Tk()
Label(root, text="Hello %s, press the Start button to begin.\n" % name).pack()
BubbleFrame(root, name).bubble_move(root)
Button(root, text="Ok", command=lambda: submit(name.get(), root)).pack()
root.mainloop()
I faced IndentationError in line 103 and NameError in 39 (it is self.buttonclick not buttonclick). Next time check your code before posting.
Well, you invoke 20 buttonclicks each time you click on Start button and the value checking happens in the middle of the process.
I fixed the make_bubbles method for you abit.
def make_bubbles(self):
self.buttonclick += 1 #Newly added in attempt to hide button
if self.buttonclick == 2: #Newly added in attempt to hide button
self.startbutton.configure(state="disabled")
showinfo("Oh No", "Sorry %s, but the game is already started" % self.name)
return
for each_no in range(1, 21):
...
The logic to enable the button at the end of game is missing or perhaps no need at all. Maybe I would rather clear all bubbles and generate 20 new ones each time I click the Start button. Anyway, I recommend this site for tkinter programming.
There is some more things to fix here too. No more iteritems() in Python 3. Use self.bubbles.items() instead. Good luck!

Resources