I've created a GUI that ask the user an User/Password. The class that creates the GUI calls another class that creates a web browser and try to login in a website using a method of the same class. If the login is successful, a variable of the GUI's object becomes 'True'
My main file is the next one :
from AskUserPassword import AskGUI
from MainInterfaceGUI import MainGUI
Ask = AskGUI()
Ask = Ask.show()
MainInterface = MainGUI()
if Ask.LoginSuccesful == True:
Ask.close()
MainInterface.show()
If the login is successful, I would like to hide the User/Password GUI and show the main GUI. The code above clearly doesn't work.
How can I make Python to wait until some condition of this type is met ?
Instead of constantly checking for the condition to be met, why not supply what you want upon login to do as a callback to your AskGUI object, and then have the AskGUI object call the callback when login is attempted? Something like:
def on_login(ask_gui):
if ask_gui.LoginSuccesful:
ask_gui.close()
MainInterface.show()
Ask = AskGUI()
Ask.login_callback = on_login
Then, in AskGUI, when the login button is clicked and you check the credentials, you do:
def on_login_click(self):
...
# Check login credentials.
self.LoginSuccessful = True
# Try to get self.login_callback, return None if it doesn't exist.
callback_function = getattr(self, 'login_callback', None)
if callback_function is not None:
callback_function(self)
Re.
I prefer to have all the structure in the main file. This is a reduced example but If I start to trigger from a method inside a class that is also inside another class... it's going to be hard to understand
I recommend this way because all the code to handle something that happens upon login is included in the class that needs to do the logging in. The code to handle which UI elements to display (on_login()) is included in the class that handles that.
You don't need anything in the background that keeps checking to see if Ask.LoginSuccessful has changed.
When you use a decent IDE, it's pretty easy to keep track of where each function is defined.
Related
I am developing a telegram bot, and I have many handlers for responses from the user.
Since there are many handlers and many different dialogs are also possible. I moved some of the handlers into different classes (Dialogs)
import telebot
bot = telebot.TeleBot(tg_api_key)
#bot.message_handler(commands=['buy_something'])
def buy(message):
from dialogs.buy_something
import Buy_Dialog
w = Buy_Dialog(bot, message)
and:
#bot.message_handler(commands=['sell_something'])
def sell(message):
from dialogs.sell_something
import Sell_Dialog
w = Sell_Dialog(bot, message)
Inside the dialog classes I can send questions to the user and get answers from them by using:
self.m = self.bot.send_message(message.chat.id, "Some question")
self.bot.register_next_step_handler(self.m, self.enter_your_name)
But now I need to get from user callback from button click:
#bot.callback_query_handler(func=lambda call: True)
def button_click_yes_or_no(self, call):
So I can catch them only from main.py, not inside the dialog.
How to redesign code to get clear logic and code with the ability to catch button_callback?
maybe the function can't see your variable try to make your variable global in the beginning of the code :
global your variable, another variable
You cannot catch callbacks from inside the class instance.
But you can follow the same logic as for commands: create a decorated general function for catch all calls and pass each one to proper class instance based on call's content (call.data or call.message.chat.id).
I am relatively new to coding and to python. I am trying to make a telegram bot using telebot.
I have a flow in which I have to go. eg: after /start, I should get a text. So I created a running_work_list in which my workflow is arranged in order and after completion, I delete the index 0 elements. 'start' is the first element in the array.
So I tried to implement something like this so that I can comfortably code each step rather than looking at input and deciding.
But despite putting inside an if statement, '#bot.message_handler()' is running even if the condition fails.
if running_work_list[0]=='start':
print('inside if')
#bot.message_handler(commands=['start','new test'])
def start(message):
print('user sent start')
bot.send_message(message.chat.id,welcome_message)
running_work_list.pop(0)
print(f'work flow deciding list {running_work_list}')
#bot.message_handler(content_types=['text','photo','poll'])
def bot_intro(message):
print('here')
print(f'user sent {message.text}')
bot.send_message(message.chat.id,BOT_INTRO_MSG)
Below are the outputs I got. The user had sent /start first and then some other random text.
inside if
user sent start
work flow deciding list ['test_name', 'test_description', 'test_time', 'test_image', 'test_poll']
here
user sent fdf
Is it like the '#bot.message_handler()' will run even if we put it inside an if statement?
Executing #bot.message_handler decorator marks the function to be called when the message matches the filters that you've passed as arguments (content_types or commands). They are called by the telebot library directly and do not execute the if statement after they have been registered.
The proper way to do this is to invert the logic: define the message handler functions at the top level of the program and decide what you should do in the if within the function.
I am using Robot Framework to do test automation and would like to know if there is a way for me to extend the Selenium Webdriver to handle a loading element universally. For reference when I say loading element I am referring to a custom Angular element.
I understand that I can do a Wait Until Element Not Visible, but what I really want to do is create a constant listener on the Selenium Webdriver to halt whenever this loading element is visible. I would like to not constantly use a robot keyword to check if the element is visible, and be forced to implement arbitrary-ish sleeps.
If my question is still not clear, below is some pseudocode / psuedoflow I was thinking of. I'm assuming Selenium is single threaded, otherwise this idea would be fruitless.
Selenium Webdriver:
AddListener(new LoadingListener)
LoadingListener
While ( LoadingElement is Visible)
Sleep 1s
Please respond with any feedback. If this isn't possible let me know, If you have any ideas also let me know.
I haven't ever done this, but you might be able to use the listener interface to call a function before or after every keyword. For example, the listener might look something like this (untested):
from robot.libraries.BuiltIn import BuiltIn
xpath = //*[#id='loading_element']
class CustomListener(object):
ROBOT_LISTENER_API_VERSION = 2
def start_keyword(self, name, attrs):
selib = BuiltIn().get_library_instance("SeleniumLibrary")
selib.wait_until_element_is_not_visible(xpath)
This may throw an exception if the element doesn't exist on the current page, and you also need to add code to handle the case where a keyword runs before the browser is open, but hopefully it gives the general idea.
For more information about the listener interface see the section titled Listener Interface in the Robot Framework User Guide.
Another direction you could take is to use the SeleniumLibrary keyword Execute Async Javascript.
The document states:
Executes asynchronous JavaScript code.
Similar to Execute Javascript except that scripts executed with this
keyword must explicitly signal they are finished by invoking the
provided callback. This callback is always injected into the executed
function as the last argument.
Scripts must complete within the script timeout or this keyword will
fail
With the following code example from the docs:
${result} = Execute Async JavaScript
... var callback = arguments[arguments.length - 1];
... function answer(){callback("text");};
... window.setTimeout(answer, 2000);
Should Be Equal ${result} text
Below is the solution I ended up using :)
Worked well, thank you for all comments and help!
from robot.libraries.BuiltIn import BuiltIn
from datetime import datetime
class LoaderListener(object):
ROBOT_LISTENER_API_VERSION = 2
ROBOT_LIBRARY_SCOPE = "GLOBAL"
def __init__(self):
self.ROBOT_LIBRARY_LISTENER = self
self.selib = BuiltIn().get_library_instance("SeleniumLibrary")
def _start_suite(self, name, attrs):
BuiltIn().log_to_console("Loader Listener Has Been Attached.")
def start_keyword(self, name, attrs):
try:
# If this doesn't fail we have the loader
self.selib.find_element("id=loader")
# Take time stamp for when we begin waiting
BuiltIn().log_to_console("The Page Is Loading.")
start_time = datetime.now()
# Wait Until Element Not Visible
# Sometimes this will fail, because by the time we get here the loader is gone.
# In that case, just continue as normal
try:
self.selib.wait_until_element_is_not_visible("id=loader")
except:
pass
# Take time stamp for when we have stopped waiting
end_time = datetime.now()
# Calculate time elapsed
elapsed_time = (end_time-start_time)
# Create output message and log to conosle
output_message = "The Page Has Finished Loading in {0}".format(elapsed_time)
BuiltIn().log_to_console(output_message)
except:
# We don't have the element...
pass
So I have a small python program that is spread out across a few classes. In my main class, I tell my title screen class to display and then wait for input. If the input it gets is 'q' it calls back to my main class telling it to set it's stop flag to true. Otherwise, it just loops.
This is the callback I give to my title screen:
def quit():
stopped = True
stopped is set to False outside of the callback. The callback is registered fine, and goes off no problem, but it seems to set stopped to true locally in titlescreen, and not in main. I can fix this by creating a class stopFlag and doing the exact same thing, except in the object.
My question is why do I need to make a new class to do this? Is there a way I can set a global flag in main which is just a boolean without making an object out of it? How can I have the callback reference that boolean?
Edit:
I declare stopped like this:
stopped = False
Here is the quit callback register call:
titleScreen.registerCallbackQuit(quit)
Which looks like:
def registerCallbackQuit(self, callback):
self.callbackQuit = callback
And it calls quit if it gets a in the user input.
global stopped would work (probably). People use classes to avoid globals (among other things). If 'stopped' is spread out over many files, you would need to import it.
I am trying to create a chat server where multiple clients can connect to a server. I want to create a GUI in one module (say clientgui.py) which calls another module (say client.py) to connect to the server.
The problem is I have to call client.py when a certain button is clicked. But while doing so, the main loop never gets executed. For example,
app = Tk()
...
sendbutton = Button(text,height...., command = Client().senddata())
#This will call function of a different module.
....
app.mainloop()
Is it possible to call another module inside a "GUI loop"?
You are not assigning the function to command, but the result of senddata. Try this instead:
sendbutton = Button(text, height, ..., command = Client().senddata)
That should solve your problem. Notice how the senddata method has no parenthesis on it? That is because you don't want to call it right there, you want to talk about it. Since functions and methods are objects, it is ok to assign it to a parameter (e.g. command).
What your code was doing, was assign the result of calling Client().senddata() to the buttons command. I assume this doesn't return a function/method/callable object (but it could) and that instead you assign None, in effect making the button not do anything at all when clicked.