I need to add more than one app.root.current in kivy - kivy

I am creating an app in kivy in which basically it gives you a poem once you enter a date and password. For this I have created several windows for each poem. My problem is that i made it so that on_release of the button there is a selective-like structure to show the right poem. The problem is that this only works for the last sentence and not all of them at the same time as supposed to.
Button:
font_name: "Georgiai"
font_size: 20
text: "Enter"
size_hint: 0.5, 0.5
background_color: "#00FFCE"
on_release:
app.root.current = "fifth" if date.text == "01.02.2023" and password.text == "Si" else "fourth"
app.root.current = "seis" if date.text == "02.02.2023" and password.text == "te" else "fourth"
I have tried using multiple on_release: app.root.current but it also does not work. This is the only thing left for it to function so I hope someone can help me. I am quite new in programming

You should move the program logic into Python and not include logic in your kivy file. Arguments can be passed like this:
Button:
on_release: app.give_poem(_date.text, _password.text, self)
# or
# on_release: root.give_poem(_date.text, _password.text, self)
I changed your variables by adding an underscore to avoid confusing your variables with other common objects. I am assuming date and password were ids of other kivy objects.
in your python app or top level widget you can employ your logic. Here is the basic signature that your method should have within your app or your top level widget object. This one just prints the arguments sent and you can decide what to do from there.
def give_poem(self, _date, _password, my_button):
print(f"{self}: {_date}: {_password}: {my_button}")

Related

kivy: have tcp event open popup

I'm new to kivy, so apologies if I've missed something really basic here but I am struggling to understand both the behavior I'm getting as well as my inability to get the outcome I'm after.
The goal
The kivy app should be listening for TCP events from an external server, with certain triggers causing various stuffs in the kivy app. In this case, I want the external server to be able to open a popup in the app.
Illustrative Code
I have adopted the following (working) demo code from another SO answer that I can no longer seem to find, so apologies for the plagiarism but it is not intentional.
server.py
import socket
serversocket = socket.socket()
host = 'localhost'
port = 54545
serversocket.bind(('', port))
serversocket.listen(1)
clientsocket,addr = serversocket.accept()
print("got a connection from %s" % str(addr))
while True:
msg = input("> ")
clientsocket.send(msg.encode('utf-8'))
client.py
import socket
class MySocket:
def __init__(self,host="localhost",port=54545):
self.sock = socket.socket()
self.sock.connect((host, port))
def get_data(self):
return self.sock.recv(1024)
main.py
import kivy
from kivy.app import App
from kivy.event import EventDispatcher
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from threading import Thread
from client import *
kivy.require('1.9.1')
class MyEventDispatcher(EventDispatcher):
def __init__(self, **kwargs):
self.register_event_type('on_test')
super(MyEventDispatcher, self).__init__(**kwargs)
def do_something(self, value):
self.dispatch('on_test', value)
def on_test(self, *args):
print('I am dispatched', args)
CustomPopup().open()
class CustomPopup(Popup):
pass
class MainScreen(Label):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
self.sock = MySocket()
Thread(target=self.get_data).start()
def get_data(self):
while True:
self.text = self.sock.get_data().decode('utf-8').strip()
if self.text == 'click':
MyEventDispatcher().do_something(self.text)
class MyApp(App):
def build(self):
return MainScreen()
if __name__ == "__main__":
MyApp().run()
my.kv
<MainScreen>:
Button:
text: 'I do nothing'
<CustomPopup>:
title: 'Popup window'
size_hint: .5, .5
auto_dismiss: False
GridLayout:
cols: 1
Label:
size_hint: .9, .9
halign: 'center'
valign: 'middle'
text: 'message text goes here'
text_size: self.width, None
Button:
text: 'A: Close'
on_release: root.dismiss()
Button:
text: 'B: Close'
on_release: root.dismiss()
This all works in that I can send the trigger word click from the server and the popup will open, it has multiple placeholder buttons that work. Hooray.
The problem, however, is that I seem to require the dummy button on the main app screen, which I do not want. If I simply eliminate the <MainScreen> bit from the .kv file, sending the click keyword leads to:
I am dispatched ('click',)
Segmentation fault (core dumped)
but I can send any other string and it will display on screen as expected.
So, the basic questions are:
How do I get this to work as desired and
Why does placing a non-functional button on the screen make the popup work?
Thanks!

Updating Button's background_color not reflected on the UI sometimes

Well, I'm creating a Kivy app in which you can select only one of many statuses. Every status has his own Button and the selected status has a different background_color from the others.
The problem is that sometimes (apparently random) after clicking a button, two of them stays with his background changed at the same time. The strange thing is that I'm checking the background_color of those elements and it doesn't match with the result that I'm seeing on the screen.
So, the background_color property has one color but another one is being rendered on the screen.
Relevant kv file section:
<StatusButtonsContainer>:
cols: 2
spacing: 8
padding: 0,16,0,0
<StatusButton>:
selected: False
text: self.status_name
on_release: app.on_change_status_click(self.status_name)
font_size: '16'
background_color: self.back_color if self.selected else (0.259, 0.259, 0.259,1)
background_normal: ''
background_down: ''
background_disabled_normal: ''
This is how I'm creating the Button widgets dinamically:
class StatusButtonsContainer(GridLayout):
def __init__(self, **kwargs):
super(StatusButtonsContainer, self).__init__(**kwargs)
for name, color in config.statuses.items():
button = StatusButton(status_name=name, back_color=color)
self.add_widget(button)
class StatusButton(Button):
status_name = StringProperty()
back_color = ListProperty()
And this the function that is executed when the button is pressed:
class ControlsScreen(Screen):
def change_selected_status(self, status):
for button in self.ids.buttons_container.children:
if button.status_name == status:
button.selected = True
button.disabled = True
print('Status ' + button.status_name + ' was selected.')
print('background_color:' + str(button.background_color))
else:
button.selected = False
button.disabled = status in ['printing', 'preparing', 'paused']
print('Status ' + button.status_name + ' was NOT selected.')
print('background_color:' + str(button.background_color))
It's even more weird that this is happening on a Raspberry Pi 3 with Raspbian, but I'm not able to reproduce it on a Windows machine... I double checked that the [input] section in the config is correct and the buttons are being pressed only once.
Versions
Python: 3.6.0
OS: Raspbian GNU/Linux 9 (stretch)
Kivy: 1.10.1
Finally, the problem was that I was updating the UI in a thread different from the main thread.
In my application, the button status can be changed either from the UI directly or from a web socket message. So, when the button's background_color was changed through web socket, the UI update was called by another thread and, for some reason, that was causing the issue.
I've solved it with the #mainthread decorator:
#mainthread
def change_status_ui(self, status):
self.get_screen('Controls').change_selected_status(status)
self.get_screen('Status').change_status(status)
Now, it doesn't matter which thread call this method, it will be executed on the main thread.

WoW Addon - Dynamic tooltip while key is pressed

I've got a tooltip and I want it to show more detailed information if the SHIFT key is pressed/held. The following code works so far:
function myAddonFrame_OnEnter(self)
myAddon_GenerateTooltip(self)
end
function myAddonFrame_OnLeave(self)
GameTooltip:Hide()
end
function myAddon_GenerateTooltip(self)
GameTooltip:SetOwner(self or UIParent, "ANCHOR_LEFT")
GameTooltip:SetText(“myAddon”, 0.7, 0, 1)
GameTooltip:AddLine(" ", 1, 1, 1)
if IsShiftKeyDown() then
-- Show some more details, if the SHIFT key is pressed.
GameTooltip:AddLine(“Some detailed stuff about god and the world.”, 1, 1, 1)
GameTooltip:AddLine(“Even more great stuff to read.”, 0.9, 0.8, 0.1, 1)
else
-- Basic information to be displayed when no button is pressed.
GameTooltip:AddLine(“Some basic information. Yaaay!“, 1, 1, 1)
GameTooltip:AddLine(" ", 1, 1, 1)
GameTooltip:AddLine(“Hold SHIFT for more information…”, 0.5, 0.5, 0.5)
end
GameTooltip:Show()
end
But this only works if the SHIFT key is held before the mouse hovers the myAddonFrame. It also will display the detailed information even after releasing SHIFT as long as the mouse cursor remains on the myAddonFrame. But I want it dynamic!
My question here is:
How can I make the tooltip to refresh itself according to the SHIFT key when the mouse cursor stays on the frame?
I have in mind something like when I hover over an item in WoW and only as long as I hold the SHIFT key, that item will be compared with what I currently wear. I want exactly this effect, just within the same tooltip.
I hope somebody can push me in the right direction. I have tried using MODIFIER_STATE_CHANGED but I didn't get it working.
Oh, and while we are talking about toolips...here's a bonus question:
Is it possible to format them a bit? Bold, italics, fontsize, colorchanges for a single word in a line, etc. ? I didn't find anything particular here (wowprogramming.com), but maybe I overlooked it.
--- SOLVED ---
For the ones interested in the solution:
I used the code snippet of Nathanyel and adjusted it a bit. Because I use one XML file for all the frames and one corresponding LUA file for just the functions, I didn't wanted to create a new frame there. But I figgured out a way to use the existing myAddonFrame for the dynamic tooltip as well.
My code from above works perfectly and I just had to add the following:
-- [ALL THE CODE ABOVE!]
function myAddonFrame_OnLoad(frame)
frame:RegisterEvent("MODIFIER_STATE_CHANGED") -- Needed for the dynamic tool tip.
end
function myAddonFrame_OnEvent(frame, event, ...)
-- Fired, when any keyboard key is pressed.
local key, state = select(1, ...)
if (event == "MODIFIER_STATE_CHANGED") then
-- Switch the dynamic tooltip when the SHIFT key is held.
if myAddonFrame:IsMouseOver() and ((key == "LSHIFT") or (key == "RSHIFT")) then
myAddon_GenerateTooltip(frame)
end
end
end
That's it! Thanks to Nathanyel for the food for thought. :)
That event is indeed the key, but as you need to register it to a frame, preferably the one affected by the code, you should use a new frame for your tooltip:
local myFrame = CreateFrame("GameTooltip","myFrame",UIParent,"GameTooltipTemplate")
myFrame:SetScript("OnEvent",function(self, event, arg, ...)
if myFrame:IsShown()
and event == "MODIFIER_STATE_CHANGED"
and (arg = "LSHIFT" or arg = "RSHIFT") then
myAddon_GenerateTooltip() -- might need a parameter
end
end
myFrame:RegisterEvent("MODIFIER_STATE_CHANGED")
This simply re-populates the tooltip when either Shift key is pressed, and your function can react to the new state of the key.
Napkin code, slash some copy&paste from an addon where I used this method, so it might not be perfect, but enough to convey the concept.

Kivy_Text_INPUT_Multiline_app (frames)

I am developing some app in kivy,I need to display mulitple line of string in a text input.How to do it?
If any examples please do post it.
An example is right here together with description. You didn't provide any code or basically anything I could use, so I'll do it this way:
<Box>:
TextInput:
id: mytextinput
multiline: True ## defaults to True, but so you could see how it works
text: 'something'
Button:
on_release: root.update_text('new value')
this will be your kv file/string for a TextInput no matter where you'll put it and id is identificator of how to access that widget, then in python
class Box(BoxLayout):
def update_text(self, value):
self.ids.mytextinput.text = value
It means that whatever widget you have and TextInput is its child in kv file/string, you will access it through ids dictionary and change its variable text to your desired value with calling custom update_text(<string>) in your class.

Call method when user changes focus from on textfield to another (RubyMotion)

I'm using RubyMotion (had been writing Obj C in Xcode previously but now I'm collaborating with someone that wants to use RubyMotion due to bkgd in Ruby)
I have a ViewController with 2 textfields. I need to have it so that whenever user switches from being in textfield1 to textfield2 if a condition of textfield1 isn't met, Error Label shows (I use self.ErrorLabel.show for this). I know how to write my condition statements but I really do not know how to know when user switches to next textfield.
I thought I could use:
if ([textField1 isFirstResponder] && (textField1 != touch.view))
log( 'left textfield1' )
end
if ([textField2 isFirstResponder] && (textField2 != touch.view))
log( 'left textfield2' )
end
from this question Detect UITextField Lost Focus but no such luck. I know I'm using RubyMotion so there are some differences.
How do I get this to work? I don't think I'm searching with the right keywords because it seems like something developers use all the time yet I'm not finding any results.
Thank you for any help available.
Update: I am thinking I will be using this method:
- (void)textFieldDidEndEditing:(UITextField *)textField
In RubyMotion:
def textFieldDidEndEditing( textField )
I used log() to determine that this does indeed let me know that the user has changed from one textfield to the next. I just need to do a couple of tweaks so that I can specify which textfield was left.
You need to set the delegate of each of your text fields to the view controller class, then implement the textFieldDidEndEditing method. Here's a working example:
class MyViewController < UIViewController
attr_accessor :textField1, :textField2
def viewDidLoad
#textField1 = UITextField.alloc.initWithFrame([[10, 100], [300, 40]])
# Customize your text field...
#textField2 = UITextField.alloc.initWithFrame([[10, 150], [300, 40]])
# Customize your text field...
view.addSubview(#textField1)
view.addSubview(#textField2)
#textField1.delegate = self
#textField2.delegate = self
end
def textFieldDidEndEditing(textField)
if textField == #textField1
puts 'left textField1'
elsif textField == #textField2
puts 'left textField2'
end
end
end

Resources