I have been making an app which lets the users check-boxes. Depending on what boxes they check it will display different information. I decided to use PySimpleGUI for this project. I made 6 check-boxes and one text input which I want the user to be able to choose between the check-boxes and enter a title of a movie in the text input box. Depending on what check-boxes they select it will display different information based on the movie whose title was entered in the text input.
When I try to process the title value entered in the text input it process all values including the boolean values of the check-boxes. The information my code tries to process is: {0: ;'my input', 'Title': True, 'Year': False...}. I only need to process the my input/the movie title input and not the boolean values of the check-boxes.
Here is an example of my code (for reference I am also using the IMDBPY library to search for movies (which I have made work, the problem is that the id = search[0].movieID line is processing too many values.):
def run_code():
global name
while True:
event, value = window.read()
if event == 'SEARCH':
print(values)
name = str(values)[5:-2]
print('Please wait while your results load...')
search = ia.search_movie(name)
id = search[0].movieID
if values['Title'] == True:
print(movie_title)
I am trying to make my code search for the ID of the film title which would be typed by the user in an input field and than (at the bottom) and print the movie title depending if they have the title checkbox selected. At this point I just get an error saying id = search[0].movieID IndexError: list index out of range) To my understanding id = search[0].movieID is taking too many values (which it is, it is taking in all the values, input and check-boxes) I only want it to take in the text input value.
How should I spread out the values to deal with this issue?
Define keys in your sg elements. Then you can pick from values just the item that you need. For example:
def test():
layout = [[sg.Text('Movie title:'), sg.Input(key='input')],
[sg.Checkbox('Title', key='title'), sg.Checkbox('Reverse title', key='reverse')],
[sg.Button('Search', enable_events=True), sg.Cancel()]
]
window = sg.Window('My Window', layout, finalize=True)
while True:
event, values = window.read()
print(f'event = {event}, values = {values}')
if event in (sg.WINDOW_CLOSED, 'Cancel', 'Exit'):
break
if event == 'Search':
print('Searching now...')
if values['title']:
print(f'Title = {values["input"]}')
if values['reverse']:
print(f'Reverse title = {values["input"][::-1]}')
# any other action as needed
For example, when you check both the checkboxes and click on Search, you'll get
event = Search, values = {'input': 'abcde', 'title': True, 'reverse': True}
Searching now...
Title = abcde
Reverse title = edcba
Now you can be sure that you've got the right title as input by the user.
Except of this, you should check the value you get in search = ia.search_movie(name). For a title not found in the database, you'll probably get None and that's where id = search[0].movieID gets you the IndexError: list index out of range because there is no None[0]. So, you may want to add something like
if search is None:
print('Not found!')
continue
Related
When I want to get the index of a selected item in a listbox, and the listbox is empty i get a error.
window['Listbox'].get_indexes()[0]
------------------------------------
IndexError: tuple index out of range
The original list that I use in my program is not empty, but it's changing so it may be empty and in that case when I press on the listbox the program crashes.
Code:
import PySimpleGUI as sg
list1 = []
layout = [[sg.Listbox(list1, s=(13, 6), enable_events=True, key='Listbox')]]
window = sg.Window("listbox test 1", layout=layout, size=(100, 100))
while True:
event, values = window.read()
if event == "Exit" or event == sg.WIN_CLOSED:
break
if event == 'Listbox':
print(window['Listbox'].get_indexes()[0])
Is there maybe a simple fix for that?
If no, then I'd have to add a check if the listbox is empty or no.
May this work in your case?
if event == 'Listbox':
try:
print(window['Listbox'].get_indexes()[0])
except:
print("Empty list")
All that's required is an if statement.
if values['Listbox']: # if something was selected
first_entry = values['Listbox'][0]
window['Listbox'].get_indexes()[0] # if you want the index... it'll be safe because values says there are entries
I'm having some trouble with creating a program that automates a checkout process. I'm using python 3 along with Selenium. The program parses through a range of dates, which are outputted on the page as available four available slots. If none are available on the current page, it will click the 'next' button and search through the next four dates. If it gets to the end of the available date ranges and finds nothing, it'll wait thirty seconds and reset and do it all over again.
I've got the majority of this done, except for two issues:
1) I'm trying to add an argument that, when included, will go beyond the base functionality (which is to simply notify the user via text using Twilio), and complete the full checkout process.
This is the python code I'm using:
def find_available(args):
dates_available = True
spaces_free = False
free_spaces = ""
while not spaces_free:
while dates_available:
time.sleep(1.5)
spots = driver.find_elements_by_css_selector('.ss-carousel-item')
for spot_index, spot in zip(range(date_range), spots):
if spot.value_of_css_property('display') != 'none':
spot.click()
available_dates = driver.find_elements_by_css_selector('.Date-slot-container')
for available_date in available_dates:
if available_date.value_of_css_property('display') != 'none':
selected_spot = available_date.find_element_by_css_selector('#slot-container-UNATTENDED')
if 'No doorstep delivery' not in selected_spot.text:
free_spaces = selected_spot.text.replace('Select a time', '').strip()
spaces_free = True
else:
print(selected_spot.text.replace('Select a time', '').strip())
if spaces_free:
print('Slots Available!')
if args.checkout:
client.messages.create(to=to_mobilenumber,
from_=from_mobilenumber,
body=free_spaces)
driver.find_element_by_xpath("//*[contains(text(), 'Soonest available')]").click()
time.sleep(1.5)
driver.find_element_by_xpath("//input[#type='submit' and #value='Continue']").click()
print('Your order has been placed!')
else:
client.messages.create(to=to_mobilenumber,
from_=from_mobilenumber,
body=free_spaces)
print('Your order time will be held for the next hour. Check your date and confirm!')
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="auto-checkout")
parser.add_argument('--checkout', '-c', action='store_true',
help="Select first available slot and checkout")
args = parser.parse_args()
find_available(args)
Expected Behavior
If the program is launched using the '--checkout' or '-c' argument, then, once 'spaces-free' is set to true, it should send a text with the text within the 'free_spaces' element. It should then move on to the next phase, which would be a selecting of a radio button that contains the text 'Soonest available' (as in select the first available radio button that contains an available time slot), and then click the continue button.
Actual Behavior
The program will run, find an available time slot, then simply move on to the next days, never attempting to select a radio button and move forward in the checkout process.
What am I doing wrong?
Any help would be appreciated.
it seems to me that you never set the dates_available to False inside your while loop:
while dates_available:
time.sleep(1.5)
spots = driver.find_elements_by_css_selector('.ss-carousel-item')
for spot_index, spot in zip(range(date_range), spots):
if spot.value_of_css_property('display') != 'none':
spot.click()
available_dates = driver.find_elements_by_css_selector('.Date-slot-container')
for available_date in available_dates:
if available_date.value_of_css_property('display') != 'none':
selected_spot = available_date.find_element_by_css_selector('#slot-container-UNATTENDED')
if 'No doorstep delivery' not in selected_spot.text:
free_spaces = selected_spot.text.replace('Select a time', '').strip()
spaces_free = True
else:
print(selected_spot.text.replace('Select a time', '').strip())
So you'll never exit the while loop. If you don't want to rewrite the whole logic, you could set dates_available = False right after you set spaces_free = True. That would allow exiting the while loop, but you might need a break or two to exit the for loops too.
If you want a failsafe behavior, you should refactor your code for smaller functions and if you want only the first available something, you could just return from the function with the first available data.
Something like this maybe?
def find_available(args):
def get_a_date():
while True:
time.sleep(1.5)
spots = driver.find_elements_by_css_selector('.ss-carousel-item')
for spot_index, spot in zip(range(date_range), spots):
if spot.value_of_css_property('display') != 'none':
spot.click()
available_dates = driver.find_elements_by_css_selector('.Date-slot-container')
for available_date in available_dates:
if available_date.value_of_css_property('display') != 'none':
selected_spot = available_date.find_element_by_css_selector('#slot-container-UNATTENDED')
if 'No doorstep delivery' not in selected_spot.text:
return selected_spot.text.replace('Select a time', '').strip()
else:
print(selected_spot.text.replace('Select a time', '').strip())
free_spaces = get_a_date()
print('Slots Available!')
if args.checkout:
client.messages.create(to=to_mobilenumber,
from_=from_mobilenumber,
body=free_spaces)
driver.find_element_by_xpath("//*[contains(text(), 'Soonest available')]").click()
time.sleep(1.5)
driver.find_element_by_xpath("//input[#type='submit' and #value='Continue']").click()
print('Your order has been placed!')
else:
client.messages.create(to=to_mobilenumber,
from_=from_mobilenumber,
body=free_spaces)
print('Your order time will be held for the next hour. Check your date and confirm!')
I'm using ActionChains in a loop and I'm trying to figure out how to click on an item and perform some actions if certain conditions are met, in my case if the item isn't sold out and go back to the previous page to continue the loop. Is it possible to do so or should I be using a different method? The code I have below works fine until I click on the available item and go to a different url, which triggers a stale element error.
articles = driver.find_elements_by_tag_name('article')
for article in articles:
ActionChains(driver).move_to_element(article).perform()
if article.find_element_by_tag_name('a').text == "sold out":
print("sold out")
else:
print("available")
name = article.find_element_by_xpath("div/h1/a")
color = article.find_element_by_xpath("div/p/a")
name_text = name.text
color_text = color.text
print name_text, color_text
link = article.find_element_by_xpath('div/a').get_attribute('href')
print(link)
driver.find_element_by_xpath("""//*[#id="add-remove-
buttons"]/input""").click()
(...)
(...)
continue
new set-up although I'm getting a 'list index out of range' error now.
articles = driver.find_elements_by_tag_name('article')
for i in range(len(articles)):
article = driver.find_elements_by_tag_name('article')[i]
if article.find_element_by_tag_name('a').text == "sold out":
print("sold out")
else:
print("available")
name = article.find_element_by_xpath("div/h1/a")
color = article.find_element_by_xpath("div/p/a")
name_text = name.text
color_text = color.text
print name_text, color_text
link = article.find_element_by_xpath('div/a').get_attribute('href')
print(link)
driver.get(link)
(...)
(...)
continue
You get a StaleElementReferenceException whenever you hold a variable to an element on the page and something in the page changes or the page refreshes.
What you should do initiate a new variable inside the loop. In this way, every time you iterate through the loop and the page is (possibly) refreshed, there is a new variable and the exception doesn't occur.
Something like this to start your loop.
# look up the articles one time outside of the loop, so we can check the number of articles
articles = driver.find_elements_by_tag_name('article')
# loop through the articles, based on the number of articles (length of list)
for i in range(len(articles)):
# each time in the for loop, look up the list of articles again and then go for element number i
article = driver.find_elements_by_tag_name('article')[i]
I have a TreeCtrl where the user is able to edit the label of an item.
However, if the user presses enter when the input is empty, then label is should be set to its former state. I am able to accomplish this, but with a bug.
This is the "workflow"
Label shows 'item'
User starts to edit label
User inputs ''
User presses enter
Label shows ''
User starts to edit label
User changes nothing, inputs ''
User presses enter
Label shows 'item'
Code example
def on_rename(self, event):
treeItemId = self.tree.GetSelection()
self.tree.EditLabel(treeItemId)
def on_rename_end(self, event):
name = event.GetLabel()
treeItemId = self.tree.GetSelection()
pydata = self.tree.GetPyData(treeItemId)
if len(name) <= 0:
# pydata[1] == 'item'
self.tree.SetItemText(treeItemId, pydata[1])
else:
dostuff()
set_new_pydata()
I found out my own answer
if len(name) <= 0:
event.Veto()
I am using easygui multenterbox so as to give users the option to edit specific information out of data being copied. My only problem is that pressing 'backspace' results in '\b' being added to the string instead of erasing a character.
Has anybody figured out a way to add a <backspace> event to the field within the multenterbox?
I have used this event in the past on tkinter "Entry" and "Text".
sample code:
msg = "Enter name"
title = "New name"
fieldName = ['name']
newTitle= 'NewName'
fieldValue = [newName]
fieldValue = multenterbox(msg,title, fieldName,fieldValue)
# make sure that none of the fields were left blank
while 1: # do forever, until we find acceptable values and break out
if fieldValue == None:
break
errmsg = ""
# look for errors in the returned values
for i in range(len(fieldName)):
if fieldValue[i].strip() == "":
errmsg = errmsg + '"{}" is a required field.\n\n'.format(fieldName[i])
if errmsg == "":
# no problems found
print ("Reply was:", fieldValue)
break
else:
# show the box again, with the errmsg as the message
fieldValue = multenterbox(errmsg, title, fieldName, fieldValue)
The above code snippet is showing the functionality that I already have, and works. My program will open a multenterbox window with the value of NewTitle which could be edited by the user. How do I control the editable entry in the multenterbox so that it supports backspace?
Thanks,
MMH