I am a newbie in kivy. I made a tic tac toe game, but when one of the player wins I want the game to restart so, the players can play it again. How can I make this in kivy or should I reset the buttons and lists that the game based on? I have tried many things like
self.clear_widgets()
but it didn't work
this is the main.py
from kivy.app import App
from kivy.properties import OptionProperty, ObjectProperty
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
class Option():
p1 = []
p2 = []
activeplayer = 1
class TicTable(BoxLayout):
pass
class EntryButton(Button):
opt = Option()
obj = ObjectProperty()
a = ObjectProperty()
def setButton(self, p):
self.obj.text = p
self.obj.disabled = True
def show_winner(self, win_player):
if win_player:
popup = Popup(title="There is a Winner", content=Label(text=win_player), size_hint=(None, None), size=(200, 200))
popup.open()
def check_winner(self):
p1_list = set(self.opt.p1)
p2_list = set(self.opt.p2)
winner = None
winning = [{1, 2, 3}, {4, 5, 6}, {7, 8, 9},
{1, 4, 7}, {2, 5, 8}, {3, 6, 9}]
for i in winning:
if p1_list.intersection(i) == i:
winner = "Player X is the Winner"
self.show_winner(winner)
break
elif p2_list.intersection(i) == i:
winner = "Player O is the Winner"
self.show_winner(winner)
break
def play(self):
if self.opt.activeplayer == 1:
self.setButton("X")
self.opt.p1.append(self.obj.n)
self.check_winner()
self.opt.activeplayer =2
elif self.opt.activeplayer ==2:
self.setButton("O")
self.opt.p2.append(self.obj.n)
self.check_winner()
self.opt.activeplayer = 1
class TicTacToeApp(App):
pass
if __name__ == '__main__':
TicTacToeApp().run()
and this is the tictactoe.kv
<EntryButton>:
obj: obj
id: obj
on_press: root.play()
<TicTable>:
orientation: "vertical"
BoxLayout:
EntryButton:
n:1
text: ""
EntryButton:
n:2
text: ""
EntryButton:
n:3
text:""
BoxLayout:
EntryButton:
n:4
text: ""
EntryButton:
n:5
text: ""
EntryButton:
n:6
text: ""
BoxLayout:
EntryButton:
n:7
text: ""
EntryButton:
n:8
text: ""
EntryButton:
n:9
text: ""
TicTable:
Your TicTacToeApp should have the build method that returns a widget.
I will give you an example, where it is a quiz app, when the correct button is pressed (or released, in kivy lang), the app will update its quiz.
import random
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
Q_sets = ["1+1=...","1+6=...","77-43=..."];
Opt_sets = [["2", "23"], ["4","7"], ["34","66"]];
Ans_sets = ["2","7", "34"];
class Option(Button):
def __init__(self, label):
super().__init__(self);
self.text = label;
def on_release(self):
super().on_release(self);
if self.text == self.parent.answer:
self.parent.parent.clear_widgets();
index = int(random.uniform(0, 3));
New = Quiz(Q_sets[index], Opt_sets[index][0], Opt_sets[index][1], Ans_sets[index]);
self.parent.parent.add_widget(New);
class Quiz(GridLayout):
def __init__(self, question, opt1, opt2, correct):
super().__init__(self, rows=3, cols=1);
self.question_label = Label(text=question);
self.opt1_button = Option(label=opt1);
self.opt2_button = Option(label=opt2);
self.answer=correct;
self.add_widget(self.question_label);
self.add_widget(self.opt1_button);
self.add_widget(self.opt2_button);
class QuizzesApp(App):
def build(self):
Container = GridLayout();
Container.add_widget(Quiz(Q_sets[0], Opt_sets[0][0], Opt_sets[0][1], Ans_sets[0]));
return Container
You may study this, and then improvise for your own case. Is this okay?
Related
My program parses the site at the click of a button and displays the text of the book on the user's screen. But since I'm a beginner, I can't find the ScrollView argument that would repaint it in the color I want. Maybe I'm doing something wrong, I would be glad if someone could tell me how to display the text on the screen ScrollView, which is not black!
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from bs4 import BeautifulSoup
from kivy.uix.scrollview import ScrollView
import requests
class MyButton(Button):
color = (0, 0, 0, 1)
valign = 'bottom'
padding_y = 10
background_color = (.93, .91, .67, 1)
background_normal = ''
class Box(BoxLayout):
orientation = "vertical"
padding = [5,5,5,5]
spacing = 10
def on_kv_post(self, widget):
self.add_widget(MyButton(text='И. С. Тургенев. «Отцы и дети»', on_press=self.btn_press))
def btn_press(self, instance):
self.clear_widgets()
sc = ScrollView()
x = 1
data = ''
while True:
if x == 1:
url = "http://loveread.ec/read_book.php?id=12021&p=1"
elif x < 4:
url = "http://loveread.ec/read_book.php?id=12021&p=" + f'{x}'
else:
break
request = requests.get(url)
soup = BeautifulSoup(request.text, "html.parser")
teme = soup.find_all("p", class_="MsoNormal")
for temes in teme:
data += temes.text
x = x + 1
sc.add_widget(Label(text=f'{data}',color = (1,1,1,1)))
self.add_widget(sc)
class MyApp(App):
def build(self):
return Box()
if __name__ == "__main__":
MyApp().run()
You need your Label to adjust according to its text. The easiest way to do that is by using the kivy language. Here is one way to do it:
Add a new class MyLabel:
class MyLabel(Label):
pass
Use the new class in your python:
sc.add_widget(MyLabel(text=f'{data}',color = (1,1,1,1)))
Add use the kivy language to define the new class:
class MyApp(App):
def build(self):
Builder.load_string('''
<MyLabel>:
size_hint: 1, None
text_size: self.width, None
height: self.texture_size[1]
''')
return Box()
I have a program that parses text from a site and outputs it. But the problem is that when I put this text in ScrollView then at 4 pages of the book it is displayed, but at 57 pages it simply is not. Only if I change the value of font_size to 1, only then the entire text of the book is displayed, but obviously this is not what I need. How to solve this problem?
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from bs4 import BeautifulSoup
from kivy.uix.scrollview import ScrollView
import requests
Builder.load_string('''
# Define the scroll view
<ScrollableLabel>:
Label:
id: label
color: (1,1,1,1)
font_size: 15
text_size: self.width, None
size_hint_y: None
height: self.texture_size[1]
''')
class MyButton(Button):
color = (0, 0, 0, 1)
valign = 'bottom'
padding_y = 10
background_color = (.93, .91, .67, 1)
background_normal = ''
class ScrollableLabel(ScrollView):
pass
class Box(BoxLayout):
color = (.98, .98, .82, 1)
orientation = "vertical"
padding = [5, 5, 5, 5]
spacing = 10
def on_kv_post(self, widget):
self.add_widget(MyButton(text='И. С. Тургенев. «Отцы и дети»', on_press=self.btn_press))
def btn_press(self, instance):
self.clear_widgets()
sc = ScrollableLabel()
x = 1
data = ''
while True:
if x == 1:
url = "http://loveread.ec/read_book.php?id=12021&p=1"
elif x < 4:
url = "http://loveread.ec/read_book.php?id=12021&p=" + f'{x}'
else:
break
request = requests.get(url)
soup = BeautifulSoup(request.text, "html.parser")
teme = soup.find_all("p", class_="MsoNormal")
for temes in teme:
data += temes.text
x = x + 1
sc.ids.label.text = data
self.add_widget(sc)
class MyApp(App):
def build(self):
return Box()
if __name__ == "__main__":
MyApp().run()
As a test, I prepared the following code,
Firstly, I set the button text from two function names add_front and add_back
Then, I get the function handle from the name and make a partial function to bind it to Buttons.
Though the binding seems ok, the results are random.
Anybody can help me out?
"""
#author:
#file:test-bind.py
#time:2022/01/3111:42
#file_desc
"""
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivy.clock import Clock
import random
from functools import partial
Builder.load_string("""
<MButton#Button>:
_cb:[]
<TestWin>:
inp1:inp1_
lbl1:lbl1_
btn1:btn1_
btn2:btn2_
orientation: 'vertical'
BoxLayout:
orientation:'horizontal'
TextInput:
id:inp1_
readonly: True
Label:
id:lbl1_
MButton:
id:btn1_
text: 'add_front'
MButton:
id:btn2_
text: 'add_back'
Button:
id:btn_cls_
text:'clear'
on_press:root.clear_elements()
Button:
id:btn_shuffle_
text:'Shuffle'
on_press:root.shuffle_btn()
TextInput:
multiline: True
text:'Usage: press <Shuffle> to randomize button function and set a random number in [0,9], press <add_front> or <add_back> buttons to insert to list'
""")
class Box:
def __init__(self):
self.elements =[]
def add(self,e,front=False):
if front:
self.elements.insert(0,e)
else:
self.elements.append(e)
def add_front(self,e):
print("add_front",e)
self.add(e,front=True)
def add_back(self,e):
print("add_back",e)
self.add(e,front=False)
class TestWin(BoxLayout):
inp1 = ObjectProperty()
lbl1 = ObjectProperty()
btn1 = ObjectProperty()
btn2 = ObjectProperty()
btn_bind = ObjectProperty()
def __init__(self, **kwargs):
super(TestWin, self).__init__(**kwargs)
self.box = Box()
Clock.schedule_interval(self.update_elements_display, 0.5)
def update_elements_display(self,*args):
self.lbl1.text = "%s"%str(self.box.elements)
pass
def clear_elements(self):
self.box.elements=[]
def shuffle_btn(self):
btn_txt_ = ["add_front", "add_back"]
random.shuffle(btn_txt_)
self.btn1.text = btn_txt_[0]
self.btn2.text = btn_txt_[1]
v = random.randint(0,9)
self.inp1.text= "%d"%v
# bind func
for btn in [self.btn1,self.btn2]:
# clear old bind firstly
for cb in btn._cb:
btn.funbind("on_press",cb)
btn._cb = []
# The following codes give wrong result
#foo_ = getattr(self.box, btn.text)
#foo = lambda elem, instance: foo_(elem)
#call_back_ = partial(foo, self.inp1.text)
# The following codes give correct result
if btn.text=="add_back":
call_back_ = partial(self.box.add_back, self.inp1.text)
elif btn.text =="add_front":
call_back_ = partial(self.box.add_front, self.inp1.text)
btn._cb.append(call_back_)
btn.fbind('on_press',call_back_)
print("bind to",call_back_)
class TestApp(App):
def build(self):
return TestWin()
if __name__ == '__main__':
TestApp().run()
Edit:
Modifying the following codes may give me correct result,
but I wonder why
# The following codes give wrong result
#foo_ = getattr(self.box, btn.text)
#foo = lambda elem, instance: foo_(elem)
#call_back_ = partial(foo, self.inp1.text)
# The following codes give correct result
if btn.text=="add_back":
call_back_ = partial(self.box.add_back, self.inp1.text)
elif btn.text =="add_front":
call_back_ = partial(self.box.add_front, self.inp1.text)
# The following doesn't work
# foo_ = getattr(self.box, btn.text)
# foo = lambda elem, instance: foo_(elem)
# call_back_ = partial(foo, self.inp1.text)
#this is ok
call_back_ = partial(getattr(self.box, btn.text), self.inp1.text)
I am trying to make a program that reads the dictionary after the user inputs their name and assigns a random selection based on weighted values in the dictionary. As of now the logic for selecting a random value from the dictionary is working but I have it printing to the console. I would like it to appear in a popup window (which i have but cannot get the output variable to show up there)
four.kv
WindowManager:
MainWindow:
<MainWindow>:
name:'main'
player_python:player_kv
GridLayout:
cols:1
GridLayout:
cols:2
Label:
text:'Player:'
TextInput:
id: player_kv
multiline: False
Button:
text: 'Random'
on_press: root.btn()
<P>:
output:output
FloatLayout:
Label:
id: output
main4.py
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import StringProperty
import random
#from Dict import *
#### example dictionary
character = {
'John':
{'Water': 2, #50%
'Fire': 1, #25%
'Earth': 1,}, #25%
'Bill':
{'Water': 1, #25%
'Fire': 2, #50%
'Earth': 1,}} #25%
####
class MainWindow(Screen):
player_python = ObjectProperty(None)
output = StringProperty('')
def btn(self):
show_popup()
player = self.player_python.text
weighted_list = []
for c in character[player]:
for w in range(character[player][c]):
weighted_list.append(c)
self.output= random.choice(weighted_list)
print(self.output) ###### instead of this printing to console I want it to display in popup window
self.player_python.text = ''
class P(FloatLayout):
pass
def show_popup():
show = P()
popupWindow = Popup(title='random character', content=show, size_hint=(None,None),size=(400,400) )
popupWindow.open()
class WindowManager(ScreenManager):
pass
kv = Builder.load_file('four.kv')
class FourApp(App):
def build(self):
return kv
if __name__ == '__main__':
FourApp().run()
https://gist.github.com/PatrickToole/00cc72cdd7ff5146976e5d92baad8e02
Thanks in advance
-P
I haven't tested this code, but try passing self.output to your show_popup() method. This would mean changing your btn() method to something like:
def btn(self):
player = self.player_python.text
weighted_list = []
for c in character[player]:
for w in range(character[player][c]):
weighted_list.append(c)
self.output= random.choice(weighted_list)
print(self.output) ###### instead of this printing to console I want it to display in popup window
self.player_python.text = ''
show_popup(self.output)
And in the show_popup() method:
def show_popup(output):
show = P()
show.output.text = output
popupWindow = Popup(title='random character', content=show, size_hint=(None,None),size=(400,400) )
popupWindow.open()
As I mentioned, I haven't tested this code, but something like this should work.
I am trying to use two sliders in layout. The code is given below. The problem is only the second slider responds to the mouse actions. The first slider (and also the button) does not respond at all to the mouse actions. How to make both sliders responsive?
Thanks in advance
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.slider import Slider
from kivy.uix.button import Button
def Exit(instance):
print('Exit the screen')
App.get_running_app().stop()
def MainScreen():
flt = FloatLayout()
button = Button(text='Hello Kivy World')
button.size = (200, 100)
button.size_hint = (None, None)
button.pos_hint = {'center_x': .5, 'center_y': .2}
button.bind(on_press=Exit)
flt.add_widget(button)
slid1 = Slider()
slid1.id = 'Slider 01'
slid1.size_hint_x = .75
slid1.pos_hint = {'x': .125, 'center_y': .5}
slid1.value_track = True
slid1.value_track_color = [0, 1, 0, 1]
slid1.sensitivity = 'handle'
flt.add_widget(slid1)
slid2 = Slider()
slid2.id = 'Slider 02'
slid2.size_hint_x = .75
slid2.pos_hint = {'x': .125, 'center_y': .75}
slid2.value_track = True
slid2.value_track_color = [1, 0, 0, 1]
slid2.sensitivity = 'handle'
flt.add_widget(slid2)
return flt
class MainApp(App):
def build(self):
return MainScreen()
if __name__ == "__main__":
app = MainApp()
app.run()
If you change your floatlayout to a boxlayout it works.
flt = BoxLayout(orientation='vertical')