Reputation: 47
I have been trying to simulate a command line design which would allow me to:
- Type in a command
- Execute it
- Output it in the same window below the command text that I have typed in.
So what I need is this:
command_one
command one has been processed and this is the output
I have partially completed this but what happens is that the output text overwrites the input instead of 'adding up'. Another problem that I have encountered is that I have to click on the TextInput window every time I need to type something in instead of just being able to keep typing in the commands without ever using my mouse.
Is there any workaround that will help me solve this?
Here is my code: (mainapp.py)(changed)
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
class MainWindow(BoxLayout):
# We create a dictionary of all our possible methods to call, along with keys
def __init__(self, **kwargs):
super(MainWindow, self).__init__(**kwargs) #This makes sure the kivy super classes from which MainWindow descends get initialized correctly.
self.command_dict = {
'one': self.command_one,
'two': self.command_two,
'three': self.command_three,
}
def process_command(self):
# We grab the text from the user text input as a key
command_key = self.ids.fetch_key_and_process_command.text
old_text = command_key.strip()
# We then use that key in the command's built in 'get_method' because it is a dict
# then we store it into a variable for later use
called_command = self.command_dict().get[old_text, 'default']
try:
# The variable is a method, so by adding we can call it by simple adding your typical () to the end of it.
called_command()
except TypeError:
# However we use an exception clause to catch in case people enter a key that doesn't exist
self.ids.fetch_key_and_process_command.text = 'Sorry, there is no command key: ' + command_key
# These are the three commands we call from our command dict.
def command_one(self):
self.ids.fetch_key_and_process_command.text = "{}\n{}\n".format(old_text, "Command One has Been Processed")
def command_two(self):
self.ids.fetch_key_and_process_command.text = 'Command Two has Been Processed'
def command_three(self):
self.ids.fetch_key_and_process_command.text = 'Command Three has been Processed'
class MainApp(App):
def build(self):
return MainWindow()
if __name__ == '__main__':
MainApp().run()
(mainapp.kv)
<MainWindow>:
Label:
text: 'This is a label'
TextInput:
id: fetch_key_and_process_command
multiline: True
Button:
id: process_command_button
text: "Process Command"
on_release: root.process_command()
The error that comes up says:
line 21, in process_command called_command = self.command_dict().get[command_key, 'default'] TypeError: 'dict' object is not callable
Upvotes: 1
Views: 1848
Reputation: 48599
See this example:
class Dog:
def __init__(self):
self.command_dict = {
'a': 1,
'b': 2,
}
def bark(self):
val = self.command_dict['a']
print(val) #=> 1
val = self.command_dict.get('c', "hello")
print(val) #=> hello
self.command_dict['c'] = 3
val = self.command_dict.get('c', "goodbye")
print(val) #=> 3
def run(self):
print(self.command_dict['c']) #=>3
print(self.I_cant_spell_command_dict_correctly['a']) #=> error
d = Dog()
d.bark()
d.run()
Output:
1
hello
3
3
Traceback (most recent call last):
File "1.py", line 27, in <module>
d.run()
File "1.py", line 21, in run
print(self.I_cant_spell_command_dict_correctly['a'])
AttributeError: 'Dog' object has no attribute 'I_cant_spell_command_dict_correctly'
If I needed to write self.command_dict more than once in a method, then I would do this:
cmd_dict = self.command_dict
and I would use cmd_dict thereafter.
Just so you know, kivy is for intermediate to advanced python programmers. Trying to figure out the fundamentals of python in a complex setting like gui programming is going to be very frustrating.
Response to comment:
I thought we agreed:
Yes, you can't write some_dict() because a dictionary is not a function.
After you attach the dictionary to self inside __init__()
, how do you retrieve the dictionary in some other method?
Answer: self.command_dict
Because self seems to confuse you, when you need to retrieve the dict just do this in your code:
my_command_dict = self.command_dict
Next, how do you retrieve the values in the dictionary my_command_dict? Do you write:
val = my_command_dict()
or
val = my_command_dict['some key']
??
And if you want to get a default value back if the key doesn't exist--instead of causing an error--and therefore you decide to use the get()
method for dictionaries, do you write:
my_command_dict.get['some key', 'default']
or
my_command_dict.get('some key', 'default')
??
Take a look a close look at my example code and your code:
you: called_command = self.command_dict().get[old_text, 'default']
me: val = self.command_dict.get('c', "hello")
Can you discern any difference in your syntax and my syntax? Start at the equals sign and move to the right comparing each line character by character.
Upvotes: 2
Reputation: 48599
I have partially completed this but what happens is that the output text overwrites the input instead of 'adding up
You need to do something like this:
text_input = self.ids.fetch_key_and_process_command
old_text = text_input.text.strip()
new_text = "{}\n{}\n".format(old_text, "Command One has Been Processed")
text_input.text = new_text
Note that when you need several dots to retrieve some object, e.g. self.ids.fetch_key_and_process_command don't keep doing that over and over again--it's inefficient. Instead, retrieve the object once and assign the object to a variable, then use the variable from then on, with the added benefit that it's easier to type!
Another problem that I have encountered is that I have to click on the TextInput window every time I need to type something in instead of just being able to keep typing in the commands without ever using my mouse
Try this:
...
...
text_input.text = new_text
text_input.focus = True
I do realise that I have to remove the multiline:False property from the kv file, but then I will not be able to use the on_text command so I do not really know where to start.
Create a button that the user can click when they are done typing--just like the Post Your Question
button that you clicked when you were done typing your question here. Then the button can call process_command() instead of the TextInput:
<MainWindow>:
Label:
text: 'This is a label'
TextInput:
id: fetch_key_and_process_command
multiline: True
Button:
id: process_command_button
text: "Process Command"
on_release: root.process_command()
Response to comment:
Here are a couple of things to consider:
1) The problem with adding text to the TextInput is that when you retrieve the text for the second command, you will get all the text in the TextInput, which will contain the text of the first command as well as the output for the first command, right? So that long string containing the first command and the output of the first command and the second command is not going to be a key in your command_dict, right? So, you are going to have to somehow process all that text and extract the second command.
You can certainly do that, say splitting the text on newlines, but it would be easier to have one TextInput for the input and another TextInput or Label for the output, then you can clear the input TextInput after every command.
2) Instead of recreating the dict every time when you need to access it, like you are doing here:
def command_dict(self):
return {
'one': self.command_one,
'two': self.command_two,
'three': self.command_three,
}
just create the dict once and attach it to self:
def __init__(self, **kwargs):
super(MainWindow, self).__init__(**kwargs) #This makes sure the kivy super classes from which MainWindow descends get initialized correctly.
self.command_dict = {
'one': self.command_one,
'two': self.command_two,
'three': self.command_three,
}
Then inside any method in the class, you can retrieve the dict with self.command_dict
, and write things like:
self.command_dict['one']
You can also take the opportunity in __init__()
to set the focus on the TextInput.
3) You should always strip()
the text that is input by a user. For instance, what if the user inputs 'one', then hits return, then realizes they need to click the button, and they click the button. In that case, the text will be "one\n", which will not be found in your dict.
Upvotes: 0