I am setting the state of the button to "down" in the py file (and later to "normal" on the event of touch_up), but the on_press in the kv file is not working, nothing happens when I click the button. How could I fix this so that I could make things happen when the button is pressed?
Python:
if touch.y/self.height > 0 and touch.y/self.height < 0.155 and 0.5 < touch.x/self.width < 1:
self.results.opacity = 0.5
self.results.state = "down"
#self.manager.current = "sc2"
touch.x = 0
touch.y = 0
Kv:
Button:
id: results
text: "See results"
on_press:
print("hey")
Related
I am writing an application on kivy, and I have a problem in the button. The problem is that when you click on the button - it does not work, but there is click in different places (buttons) it will start working
This is my kv code:
Button:
id:add_task
text:"Add"
size_hint:(1,1)
background_color:(135/255,194/255,133/255)
background_normal:''
on_press: root.add_new_task()
And here I implement the change of color and text of the button when you click on it:
def add_new_task(self):
if self.ctrl_add_tast == 0:
self.add_task.text = "Cancel"
self.add_task.background_color = (219/255, 74/255, 74/255)
self.input_add_task.opacity = 1
self.input_add_task.size = (395,175)
self.ctrl_add_tast = 1
elif self.ctrl_add_tast == 1:
self.add_task.text = "Add"
self.add_task.background_color = (135/255, 194/255, 133/255)
self.input_add_task.opacity = 0
self.input_add_task.size = (0,0)
self.ctrl_add_tast = 0
When executing this code, a button appears. When clicked, it changes the color and text.
Everything works as it should, but you need to click on the button in different places to make it work (to be more precise, it is necessary to click on the left, lower and upper sides, about 20 pixels)
And I do not use the "padding" properties in this place
I am looking to replace the windows title bar for a borderless app, I found some solutions on the internet that didn't quite work for me so I tried to do it myself.
Although the grabbing the screen and moving part works, once you release the click, the window continues to follow the cursor until eventually the program stops responding and the task is terminated.
This is an example of code that I prepared with some indications on how it works:
from kivy.app import App
from win32api import GetSystemMetrics
from kivy.lang.builder import Builder
from kivy.core.window import Window
from kivy.uix.widget import Widget
import pyautogui
import win32api
import re
Window.size=(600,300)
Window.borderless=True
#The following causes the window to open in the middle of the screen:
Window.top=((GetSystemMetrics(1)/2)-150)
Window.left=((GetSystemMetrics(0)/2)-300)
#####################################################################
Builder.load_string("""
<Grab>
GridLayout:
size:root.width,root.height
cols:2
Label:
id:label
text:'A label'
Button:
id:button
text:'The button that changes the window position'
on_press: root.grab_window()
""")
class Grab(Widget):
def grab_window(self):
#The following saves the mouse position relative to the window:
Static_Mouse_pos=re.findall('\d+',str(pyautogui.position()))
Mouse_y=int(Static_Mouse_pos[1])-Window.top
Mouse_x=int(Static_Mouse_pos[0])-Window.left
###############################################################
#The following is what causes the window to follow the mouse position:
while win32api.GetKeyState(0x01)<0: #In theory this should cause the loop to start as soon as it is clicked, I ruled out that it would start and end when the button was pressed and stopped being pressed because as soon as the screen starts to move, it stops being pressed.
Relative_Mouse_pos=re.findall('\d+',str(pyautogui.position()))
Window.left=(int(Relative_Mouse_pos[0])-Mouse_x)
Window.top=(int(Relative_Mouse_pos[1])-Mouse_y)
print(f'Mouse position: ({Mouse_x},{Mouse_y})') #To let you know the mouse position (Not necessary)
print(f'Window position: ({Window.top},{Window.left})') #To let you know the position of the window (Not necessary)
if win32api.GetKeyState(0x01)==0: #This is supposed to stop everything (Detects when you stop holding the click)
break
######################################################################
class app(App):
def build(self):
return Grab()
if __name__=='__main__':
app().run()
Is there a way to make it work fine? Or another way to grab a borderless window that might be effective?
I'm new to programming, so I apologize in advance for any nonsense you may read in my code.
EDIT: For some reason win32api.GetKeyState(0x01) is not updated once the click is done and the loop is started, nor does it help to make a variable take its value.
I've finally come up with a solution but this may not be the best one.
( Some places where I made changes are marked with comment [Modified] )
from kivy.app import App
from win32api import GetSystemMetrics # for getting screen size
from kivy.lang.builder import Builder
from kivy.core.window import Window
from kivy.uix.widget import Widget
import pyautogui
# import win32api
# import re
# set window size
# Window.size=(600,300)
# make the window borderless
Window.borderless = True
# The following causes the window to open in the middle of the screen :
Window.left = ((GetSystemMetrics(0) / 2) - Window.size[0] / 2) # [Modified] for better flexibility
Window.top = ((GetSystemMetrics(1) / 2) - Window.size[1] / 2) # [Modified] for better flexibility
#####################################################################
Builder.load_string("""
<Grab>
GridLayout:
size: root.width, root.height
rows: 2 # [modified]
Button:
id: button
text: "The button that changes the window position"
size_hint_y: 0.2
Label:
id: label
text: "A label"
""")
class Grab(Widget):
# I'm sorry I just abandoned this lol
"""
def grab_window(self):
#The following saves the mouse position relative to the window:
Static_Mouse_pos=re.findall('\d+',str(pyautogui.position()))
Mouse_y=int(Static_Mouse_pos[1])-Window.top
Mouse_x=int(Static_Mouse_pos[0])-Window.left
###############################################################
#The following is what causes the window to follow the mouse position:
while win32api.GetKeyState(0x01)<0: #In theory this should cause the loop to start as soon as it is clicked, I ruled out that it would start and end when the button was pressed and stopped being pressed because as soon as the screen starts to move, it stops being pressed.
Relative_Mouse_pos=re.findall('\d+',str(pyautogui.position()))
Window.left=(int(Relative_Mouse_pos[0])-Mouse_x)
Window.top=(int(Relative_Mouse_pos[1])-Mouse_y)
print(f'Mouse position: ({Mouse_x},{Mouse_y})') #To let you know the mouse position (Not necessary)
print(f'Window position: ({Window.top},{Window.left})') #To let you know the position of the window (Not necessary)
if win32api.GetKeyState(0x01)==0: #This is supposed to stop everything (Detects when you stop holding the click)
break
######################################################################
"""
def on_touch_move(self, touch):
if self.ids.button.state == "down": # down | normal
# button is pressed
# mouse pos relative to screen , list of int
# top left (0, 0) ; bottom right (max,X, maxY)
mouse_pos = [pyautogui.position()[0], pyautogui.position()[1]]
# mouse pos relative to the window
# ( normal rectangular coordinate sys. )
mouse_x = touch.pos[0]
mouse_y = Window.size[1] - touch.pos[1] # since the coordinate sys. are different , just to converse it into the same
# give up using touch.dx and touch.dy , too lag lol
Window.left = mouse_pos[0] - mouse_x
Window.top = mouse_pos[1] - mouse_y
class MyApp(App): # [Modified] good practice using capital letter for class name
def build(self):
return Grab()
if __name__ == "__main__":
MyApp().run()
I just gave up using button on_press or on_touch_down as suggested in the comment since it requires manual update for the mouse position.
Instead , I try using the Kivy built-in function ( ? ) on_touch_move.
It is fired when a mouse motion is detected inside the windows by the application itself. ( much more convenient compared with manual checking lol )
The concepts of window positioning are similar to yours , which is mouse pos relative to screen - mouse pos relative to app window. But the coordinate system used by Pyautogui and Kivy 's window are different , therefore I did some conversion for this as seen in the code above.
But I'm not sure whether the unit used by Pyautogui and Kivy for mouse positioning is the same or not ( ? ), so it would not be as smooth as expected / ideal case when drag-and-dropping the window via the button. Also the time delay for updating when on_touch_move of the kivy app. That's the reason why I think it may be no the best answer for your question.
Any other solutions / suggestions / improvements etc. are welcome : )
Simplified Code For Copy-Paste :
Edit : Added close / minimize window button ( at top-left corner )
#
# Windows Application
# Borderless window with button press to move window
#
import kivy
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.widget import Widget
from kivy.clock import Clock
from win32api import GetSystemMetrics # for getting screen size
import pyautogui # for getting mouse pos
# set window size
# Window.size = (600,300)
# make the window borderless
Window.borderless = True
# set init window pos : center
# screen size / 2 - app window size / 2
Window.left = (GetSystemMetrics(0) / 2) - (Window.size[0] / 2)
Window.top = (GetSystemMetrics(1) / 2) - (Window.size[1] / 2)
kivy.lang.builder.Builder.load_string("""
<GrabScreen>
Button:
id: close_window_button
text: "[b] X [b]"
font_size: 25
markup: True
background_color: 1, 1, 1, 0
size: self.texture_size[0] + 10, self.texture_size[1] + 10
border: 25, 25, 25, 25
on_release:
root.close_window()
Button:
id: minimize_window_button
text: "[b] - [b]"
font_size: 25
markup: True
background_color: 1, 1, 1, 0
size: self.texture_size[0] + 10, self.texture_size[1] + 10
border: 25, 25, 25, 25
on_release:
root.minimize_window()
Button:
id: move_window_button
text: "[b]. . .[/b]"
font_size: 25
markup: True
background_color: 1, 1, 1, 0
width: root.width / 3
height: self.texture_size[1] * 1.5
border: 25, 25, 25, 25
Label:
id: this_label
text: "Hello World !"
font_size: 25
size: self.texture_size
""")
class GrabScreen(Widget):
def close_window(self):
App.get_running_app().stop()
# def on_window_minimize(self, *args, **kwargs):
# print(args)
def minimize_window(self):
Window.minimize()
# Window.bind(on_minimize = self.on_window_minimize)
def maximize_window(self):
Window.size = [GetSystemMetrics(0), GetSystemMetrics(1)]
Window.left = 0
Window.top = 0
def update(self, dt):
# button for closing window
self.ids.close_window_button.top = self.top
# button for minimizing window
self.ids.minimize_window_button.x = self.ids.close_window_button.right
self.ids.minimize_window_button.top = self.top
# button for moving window
self.ids.move_window_button.center_x = self.center_x
self.ids.move_window_button.top = self.top
# label
self.ids.this_label.center = self.center
def on_touch_move(self, touch):
# when touching app screen and moving
if self.ids.move_window_button.state == "down": # down | normal
# (button move_window_button is pressed) and (mouse is moving)
# mouse pos relative to screen , list of int
# top left (0, 0) ; bottom right (maxX, maxY)
mouse_pos = [pyautogui.position()[0], pyautogui.position()[1]] # pixel / inch
# mouse pos relative to the window
# ( normal rectangular coordinate sys. )
# since the coordinate sys. are different , just to converse it to fit that of pyautogui
# Note :
# 1 dpi = 0.393701 pixel/cm
# 1 inch = 2.54 cm
"""
n dot/inch = n * 0.393701 pixel/cm
1 pixel/cm = 2.54 pixel/inch
n dot/inch = n * 0.393701 * 2.54 pixel/inch
"""
mouse_x = touch.x # dpi
mouse_y = self.height - touch.y # dpi
# update app window pos
Window.left = mouse_pos[0] - mouse_x
Window.top = mouse_pos[1] - mouse_y
# max / min window
if mouse_pos[1] <= 1:
self.maximize_window()
elif Window.size[0] >= GetSystemMetrics(0) and Window.size[1] >= GetSystemMetrics(1):
Window.size = [Window.size[0] / 2, Window.size[1] * 0.7]
class MyApp(App):
grabScreen = GrabScreen()
def build(self):
# schedule update
Clock.schedule_interval(self.grabScreen.update, 0.1)
return self.grabScreen
if __name__ == "__main__":
MyApp().run()
Reference
Kivy Motion Event
Imagine I have two buttons, if I press button1 I earn a point and if I press button2 I lose a point. When I reach 10 points, the game stops. The way I have been doing it right now calls the GameApp().run() every time I click a button to "re-launch" the interface, however if I do it too many times I get a RecursionError. I tried doing it with a while loop but the interface never launches.
Note: this is a simplified version of my program, it has to be done this way (essentially the number of button changes everytime a button is pressed, so the layout updates accordingly, that's why I call GameApp().run() over and over).
n = 0
class Game(GridLayout):
def __init__(self):
super(InterfacesGame, self).__init__()
global n
self.rows = 2
if n < 10:
button1 = Button(text="1")
button2 = Button(text="2")
button1.bind(on_press = self.add)
button2.bind(on_press = self.substract)
self.add_widget(button1)
self.add_widget(button2)
def add(self):
global n
n += 1
GameApp().run()
def substract(self):
global n
n -= 1
GameApp().run()
class GameApp(App):
def build(self):
return Game()
if __name__ == '__main__':
GameApp().run()
So eventually if I do too many clicks without reaching n = 10, I naturally get a
RecursionError: maximum recursion depth exceeded calling class app
error.
I tried doing this:
n = 0
class Game(GridLayout):
def __init__(self):
super(InterfacesGame, self).__init__()
global n
self.rows = 2
while n < 10:
button1 = Button(text="1")
button2 = Button(text="2")
button1.bind(on_press = self.add)
button2.bind(on_press = self.substract)
self.add_widget(button1)
self.add_widget(button2)
def add(self):
global n
n += 1
def substract(self):
global n
n -= 1
class GameApp(App):
def build(self):
return Game()
if __name__ == '__main__':
GameApp().run()
But the GUI doesn't run (it keeps loading and loading).
Based on your question and your following comment :
When 10 points are reached, the layout is remplaced by a single button with the text "You won!", and when clicking on it, the game starts all over again (with n = 0).
I implemented a complete example using Screens and ScreenManager.
from kivy.app import App
from kivy.lang import Builder
kv = ("""
ScreenManager:
id: sm
Screen:
name: "First Screen"
BoxLayout:
orientation: "vertical"
Button:
text: "1"
on_release: app.add_point()
Button:
text: "2"
on_release: app.subtract_point()
Screen:
name: "Second Screen"
Label:
text: "You Won"
font_size: "40sp"
Button:
text: "Play again"
size_hint: 0.75, None
height: "50dp"
pos_hint: {"center_x" : 0.5, "center_y" : 0.25}
on_release: sm.current = "First Screen"
""")
class GameApp(App):
count = 0
def build(self):
self.root = Builder.load_string(kv)
return self.root
def add_point(self):
if self.count < 10:
self.count += 1
else:
self.root.current = "Second Screen"
self.count = 0 # Reset counter.
def subtract_point(self):
if 0 < self.count: # Assuming only positive values.
self.count -= 1
GameApp().run()
I am trying to make a metronome with kivy, i got my +, -, start button and a label which refers to the tempo.
When i click start button i run:
self.event = Clock.schedule_interval(self.job, self.bpm)
to start my timer however there are some issues.
As i click the button the timer starts with tiny noticable delay as kivy.Clock relies on frames from what i read and i should be using "Free Clock" instead but i couldn't make it work and didn't understand what i should be doing.
Second issue is when i schedule a timer it starts and keeps on going with its initial interval value, changing bpm won't change the current timer's interval until i cancel and restart it.
I would appreciate your ideas to find a way around this. My Code:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.properties import StringProperty
from kivy.clock import Clock
Window.size = 320, 568
kv = Builder.load_file("test.kv")
class mainWindow(FloatLayout):
tempo = 60
bpm = 60/tempo
labelTempo = StringProperty(str(tempo))
interval = False
def startTimer(self, state):
if state == "startMtr" and self.interval == False:
self.event = Clock.schedule_interval(self.job, self.bpm)
elif state == "stop":
self.event.cancel()
def button(self, btn):
if btn == "+":
self.tempo += 1
self.labelTempo = str(self.tempo)
self.bpm = 60 / self.tempo
if btn == "-":
self.tempo -= 1
self.labelTempo = str(self.tempo)
self.bpm = 60 / self.tempo
if btn == "start":
self.startTimer("startMtr")
print("started")
if btn == "stop":
self.startTimer("stop")
print("stopped")
abc = 0
def job(self, dt):
self.abc += 1
print(self.abc)
print(App.get_running_app().root.bpm)
print(App.get_running_app().root.tempo)
class crApp(App):
def build(self):
return mainWindow()
if __name__ == '__main__':
crApp().run()
Kv file:
<mainWindow>:
FloatLayout:
minus: minus
start: start
plus: plus
Button:
id: minus
text: "-"
size_hint: 0.3, 0.25
pos_hint: {"x": 0.0, "top": 0.4}
on_release:
root.button("-")
ToggleButton:
id: start
text: "start"
size_hint: 0.4, 0.25
pos_hint: {"x": 0.3, "top": 0.4}
on_press:
root.button("start") if start.state == "down" else root.button("stop")
Button:
id: plus
text: "+"
size_hint: 0.3, 0.25
pos_hint: {"x": 0.7, "top": 0.4}
on_release:
root.button("+")
Label:
id: tempo
text: root.labelTempo
From the kivy docs:
The default clock suffers from the quantization problem, as frames occur only on intervals and any scheduled timeouts will not be able to occur during an interval.
So to get around that, use the "free clock" by
Selecting a clock mode using the config:
# set config options before other imports
from kivy.config import Config
Config.set('kivy', 'kivy_clock', 'free_all')
Where there are four options:
When kivy_clock is default, the normal clock, ClockBase, which limits
callbacks to the maxfps quantization - is used.
When kivy_clock is interrupt, a interruptible clock,
ClockBaseInterrupt, which doesn’t limit any callbacks to the maxfps -
is used. Callbacks will be executed at any time.
When kivy_clock is free_all, a interruptible clock,
ClockBaseFreeInterruptAll, which doesn’t limit any callbacks to the
maxfps in the presence of free events, but in their absence it limits
events to the fps quantization interval - is used.
When kivy_clock is free_only, a interruptible clock,
ClockBaseFreeInterruptAll, which treats free and normal events
independently; normal events are fps limited while free events are not
is used.
call the _free method version:
Clock.schedule_interval_free(self.job, self.bpm)
I built an app which is taking photo and goes to another empty screen and runs my main code on the background. I want to show a text input box in the empty screen while my main code is in its 1st if condition; and hide the box while the code is in 2nd if condition. My code is in below. I wrote "blablabla"s for unnecessary long things for my question.
class CheckScreen(Screen):
def deneme(self):
#MY MAIN CODE
#...
if(BLABLABLA)
self.isShownMenu = BooleanProperty(True)
else
self.isShownMenu = BooleanProperty(False)
GUI = Builder.load_string("""
#BLABLABLA1
#...
<SingleLineTextInput#TextInput>:
pos_hint: {'center_x': .5, 'center_y': .4}
size_hint: 0.5, 0.05
multiline: False
<CheckScreen>:
#BLABLABLA2
#...
SingleLineTextInput:
opacity: 1 if root.isShownMenu else 0
""")
class TestCamera(App):
def build(self):
return GUI
TestCamera().run()
While I'm running this, app always shows a textinput even I'm changing True to False in conditions. Where is my problem?
Your BooleanProperty needs to be defined at the class level:
class CheckScreen(Screen):
isShownMenu = BooleanProperty(True)
Use True or False as desired. Then in your code just reference self.isShownMenu, like:
if(BLABLABLA)
self.isShownMenu = True
else
self.isShownMenu = False