Tkinter combobox loop again. Getting the results - list

I created a group of comboboxes from looping a list and
used dict key as item em combolist. Problems: I cannot get back results to a 'results' list. All comboboxes change at same time. I´ve studied other posts about the subject, but I cant understand the concept involved in the solution.
To sum: The code generate 3 combobox but they change togeter, and I cant append results.
Thanks for your help, in advance:
#-----------------------code
from tkinter import
from tkinter import ttk
win = Tk()
win.geometry('400x600')
win.title('combobox')
result =[] #--> the new list with results of comboboxes
opts = StringVar()
anyFruits =['any_grape', 'any_tomato', 'any_banana']
#--> list just to generate the loop
fruits = {'specialgrape':'specialgrape', 'specialtomato':'specialtomato','specialbanana':'specialbanana'}
#--> dictonary to generate drop down options menu (key of dict)
for index, fruit in enumerate(anyFruits):
mycombo = ttk.Combobox(win,
values= (list(fruits.keys())),textvariable=opts)
mycombo.bind('<<ComboboxSelected>>', lambda event, index=index: fruit_callBack(index, event))
mycombo.pack()
def fruit_callBack(index, event):
for opts in mycombo:
result.append(opts)
def print_():
print(result)
bt = Button(win,command= print_)
bt.pack()
win.mainloop()

The value all change together because all Combobox have same textvariable. Hence change in one, will force the others to keep the value same. Anyway, you are appending it wrong too, you do not need to pass in index, just pass the combobox itself and append the value inside it:
for index, fruit in enumerate(anyFruits):
mycombo = ttk.Combobox(win,values=list(fruits.keys()))
mycombo.bind('<<ComboboxSelected>>',lambda e,cmb=mycombo: fruit_callBack(e,cmb))
mycombo.pack()
def fruit_callBack(event,cmb):
result.append(cmb.get())

Related

Python Data Scraping (using Xpath) - Returning empty lists and stripping characters

--
I am attempting to scrape information from the website:
http://www.forexfactory.com/#tradesPositions
Now, I used to have one up and running which this forum helped me get going, but I think something has changed on the website and the script I had no longer works.
What do I need?
I would like to scrape the number of 'short' and 'long' positions for AUDUSD, EURUSD, GBPUSD, USDJPY, USDCAD, NZDUSD and USDCHF.
NOT the percentages, the actual number of traders.
What have I done?
This is for EURUSD
import lxml.html
from selenium import webdriver
driver = webdriver.Chrome("C:\Users\MY NAME\Downloads\Chrome Driver\chromedriver.exe")
url = ('http://www.forexfactory.com/#tradesPositions')
driver.get(url)
tree = lxml.html.fromstring(driver.page_source)
results_short = tree.xpath('//*[#id="flexBox_flex_trades/positions_tradesPositionsCopy1"]/div[1]/table/tbody/tr/td[2]/div[1]/ul[1]/li[2]/span/text()')
results_long = tree.xpath('//*[#id="flexBox_flex_trades/positions_tradesPositionsCopy1"]/div[1]/table/tbody/tr/td[2]/div[1]/ul[1]/li[1]/span/text()')
print "Forex Factory"
print "Traders Short EURUSD:",results_short
print "Traders Long EURUSD:",results_long
driver.quit()
This returns
Forex Factory
Traders Short EURUSD: ['337 Traders ', ' ']
Traders Long EURUSD: [' 259 Traders']
I would like to strip everything away from the result except for the numbers. I've tried .strip() and .replace() but neither work on a list. Which will come as no surprise to you guys I don't think!
Empty List
When I apply the same technique to AUDUSD I get an empty list.
import lxml.html
from selenium import webdriver
driver = webdriver.Chrome("C:\Users\Andrew G\Downloads\Chrome Driver\chromedriver.exe")
url = ('http://www.forexfactory.com/#tradesPositions')
driver.get(url)
tree = lxml.html.fromstring(driver.page_source)
results_short = tree.xpath('//*[#id="flexBox_flex_trades/positions_tradesPositionsCopy1"]/div[6]/table/tbody/tr/td[2]/div[1]/ul[1]/li[2]/span/text()')
results_long = tree.xpath('//*[#id="flexBox_flex_trades/positions_tradesPositionsCopy1"]/div[6]/table/tbody/tr/td[2]/div[1]/ul[1]/li[1]/span/text()')
s2 = results_short
l2 = results_long
print "Traders Short AUDUSD:",s2
print "Traders Long AUDUSD:",l2
This returns
Traders Short AUDUSD: []
Traders Long AUDUSD: []
What gives? Is the Xpath not working? Just use Chromes 'inspect element' feature and navigated to the desired number, and copied the path. Same method for EURUSD.
Ideally, It would be nice to set up a list of div numbers that can insert into the tree.xpath instead of repeating the lines of code for all the different currencies to make it neater. So, in the Xpath where it has:
/div[number]/
It would be nice to have a list, i.e [1,2,3,4,5,6] that can insert into that because the rest of the Xpath is the same for the currencies. Anyway, that's an optional bonus, priority is to get a return for all currencies listed.
THANKS
You can remove all the space inside your result as you mentioned with strip method, here is my sample code:
for index in range(len(results_short)):
results_short[index] = results_short[index].strip()
if results_short[index] == "":
del results_short[index]
for index in range(len(results_long)):
results_long[index] = results_long[index].strip()
if results_long[index] == "":
del results_long[index]
For the problem you cannot get the result of AUD because the values are not loaded to the page until you have clicked the "expand" button. But I have found you can get the result from the following page: http://www.forexfactory.com/trades.php
So you can change the value of url as:
url = ('http://www.forexfactory.com/trades.php')
For this page, since the name of CSS id has changed, you need to update your value to:
results_short = tree.xpath('//*[#id="flexBox_flex_trades/positions_tradesPositions"]/div[6]/table/tbody/tr/td[2]/div[1]/ul[1]/li[2]/span/text()')
results_long = tree.xpath('//*[#id="flexBox_flex_trades/positions_tradesPositions"]/div[6]/table/tbody/tr/td[2]/div[1]/ul[1]/li[1]/span/text()')
Then apply the strip function as mentioned above, you should be able to get the correct results.

When appending dictionaries to a list through for loop, I get only the last dictionary

I'm trying to scrape a career search website by going through all the different pages and I keep running into a problem when I try to append the dictionaries into a list using a for loop. When I execute the code below in Python 3.4, the code will pull all the relevant data from each page into a dictionary (I've checked with print()) and append into "FullJobDetails", but at the end of the for loop I get a list that is full of dictionaries from the last page only. The number of dictionaries is exactly the same as the number of pages in the list "ListofJobs". "ListofJobs" is a list of html links to each page that I am scrapping.
I just started learning code, so I know that the code below is not in any shape, way, or form the most efficient or best way of doing it. Any suggestions would be appreciated. Thanks in advance!
FullJobDetails = []
browser = webdriver.Chrome()
dictionary = {}
for jobs in ListofJobs:
browser.get(jobs)
dictionary["Web Page"] = jobs
try:
dictionary["Views"] = browser.find_element_by_class_name('job-viewed-item-count').text
except NoSuchElementException:
dictionary["Views"] = 0
try:
dictionary['Applicants'] = browser.find_element_by_class_name('job-applied-item-count').text
except NoSuchElementException:
dictionary["Applicants"] = 0
try:
dictionary["Last Application"] = browser.find_element_by_class_name('last-application-time-digit').text
except NoSuchElementException:
dictionary["Last Application"] = "N/A"
try:
dictionary["Job Title"] = browser.find_element_by_class_name('title').text
except NoSuchElementException:
dictionary["Job Title"] = "N/A"
try:
dictionary['Company'] = browser.find_element_by_xpath('/html/body/div[3]/article/section[2]/div/ul/li[4]/span/span').text
except NoSuchElementException:
dictionary['Company'] = "Not found"
try:
dictionary['Summary'] = browser.find_element_by_class_name('summary').text
except NoSuchElementException:
dictionary['Summary'] = "Not found"
FullJobDetails.append(dictionary)
The problem is that you create only a single dictionary - dicitonaries are mutable objects - the same ditionary is appended over and over to your list, and on each pass of the for loop you update its contents. Therefore, atthe end, you will have multple copies of the same dicitonry, all showing the information on the last page fectched.
Just create a new dictionary object for each run of the for loop. That new dictionary will be saved on the list, and the variable name dictionary can hold your new object with no conflicts.
for jobs in ListofJobs:
dictionary = {}
browser.get(jobs)
...

Change label in tkinter inside a class? StringVar()?

I'm trying to construct a game where you can play five-in-row and when someone wins I want the label to change from like "Game is going on" to "Player 1 won" and this is where I am stuck.
The way I have constructed it all is like this. I have one class for the graphics (a Frame) and I have one class for all the Buttons that you can click on to set it to "X" or "O". And then I have one last class that checks if the grid has five of some value in a row, and that one runs after each successful click.
The label is of course set in the Frame class and first I had it like this just to make sure that I could see something
def __init__(self, master = None, rows = 10, cols = 10):
Frame.__init__(self, master)
Some other selfs....
self.inforad = Label(master, text = "Game is going on")
and this prints out nicely, just like I want it. Now I have been trying for hours to make this change when someone wins and I just can't make it work! The last thing I tried is to use the StringVar() and I did it like this
self.info = StringVar()
self.info.set("Game is going on")
self.inforad = Label(master, text = self.info)
and for starter this doesn't work at all, it prints out PY_VAR0. My plan was to define a method to call this during the game, like this
def disp(self, string):
self.info.set(string)
return self.info
And this doesn't work at all. Can someone help me with this? I am really new to programming, did my first code for like 3 weeks ago.
(If self.inforad is placed within the Frame subclass instance, self, its master should be self, not the master of self.)
As for your specific question, change Label(master, text = self.info) to Label(self, textvariable = self.info).

Python - Changing Class Variable Value in Function

I'm building a python class to encapsulate a drop-down list and its buttons in one convenient widget and I ran across a problem.
class DropDownMenu(DropDown):
def __init__(self, **kwargs):
super(DropDownMenu, self).__init__(**kwargs)
self.The_Menu = DropDown()
self.The_Btns = []
self.Num_Btns = 0
def Set_Num_Btns(self):
self.Num_Btns = len(self.The_Btns)
def Create_Menu(self, Btn_Names):
# Populate List Size Property
if (self.Num_Btns == 0):
self.Set_Num_Btns()
# Add Buttons to the Drop-Down
for i in range(0, self.Num_Btns):
self.The_Btns.append(Button(text = Btn_Names[i], size_hint_y = None, height = 20))
self.The_Menu.add_widget(self.The_Btns[i])
It compiles fine and when I try to create a drop-down menu, I get what I want:
self.File_Menu = DropDownMenu()
self.File_Menu.Create_Menu(self.File_Menu_Names)
self.add_widget(self.File_Menu)
But, if I try to bind any of the buttons to anything, like so:
self.File_Menu.The_Btns[0].bind(on_release = self.Insert_File_Menu.open)
The compiler throws an exception saying the list was out-of-bounds. On further inspection, I realized that even though I'm calling the Create_Menu function, the value of The_Btns is not being changed from an empty list. So, my question is: how do I fix this problem?
Any help would be appreciated. Thanks!
First of all, python doesn't "compile" in the sense that you refer to, and doesn't have a compiler. Also, have a look at PEP8.
To answer your question, You are iterating over a range, 0 to Num_Btns. However, in Set_Num_Btns, you set the variable to len(self.The_Btns), which is an empty list, i.e. you are iterating over range(0, 0). I suspect you mean to do soemthing like this:
for name in Btn_Names:
self.The_Btns.append(Button(text=name, ...))
....

how can I add another textlabel and buttons to a cell in a tableView in rubymotion?

I use the following methods to create a cell and populate data in it(adapted from the tweets app example)
Now I want to add a new label displaying the selected event date and a button that will perform another action.
Here comes the 2 methods:
def self.cellForEvent(event, inTableView:tableView)
cell = tableView.dequeueReusableCellWithIdentifier(EventCell::CellId) || EventCell.alloc.initWithStyle(UITableViewCellStyleDefault, reuseIdentifier:CellId)
cell.fillWithEvent(event, inTableView:tableView)
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton
cell
end
Populate data into the cell
def fillWithEvent(event, inTableView:tableView)
self.textLabel.text = event.name
puts event.image
unless event.image
self.imageView.image = nil
Dispatch::Queue.concurrent.async do
event_image_data = NSData.alloc.initWithContentsOfURL(NSURL.URLWithString(event.image_url))
if event_image_data
event.image = UIImage.alloc.initWithData(event_image_data)
Dispatch::Queue.main.sync do
self.imageView.image = event.image
tableView.delegate.reloadRowForEvent(event)
end
end
end
else
self.imageView.image = event.image
end
end
railsdog's recommendation is not without merit. You could make a #reference to the cell you want to edit, and make changes to it later. But this is kind of dangerous - lots of pitfalls: What happens it the cell is moved off screen? Reused elsewhere? Tricky.
Instead, I would recommend adding some of the fillWithEvent:inTableView code into the cellForEvent method, that way you can call tableView.reloadRowsAtIndexPaths:withRowAnimation: and that method will be called. This moves the complications I mentioned above onto the back of the Cocoa framework, and that's a good thing :-)
The downside is that you'll need to keep the indexPath handy (or calculatable), and always keep in mind that the event associated with the cell is transient, because the cells are reused. Your code above doesn't seem to keep a reference to event, which is a good thing!
# in fetchImageForEvent:tableView:
# ...
event.image = UIImage.alloc.initWithData(event_image_data)
Dispatch::Queue.main.sync do
# instead of this:
# self.imageView.image = event.image
# tell the tableView to reload. unfortunately, we don't have the index
# path. not sure how you're calculating it, but if you've got a list of
# events, this should be easy.
# I'm just assuming section 0, row 1 here.
path = NSIndexPath.indexPathWithIndex(0).indexPathByAddingIndex(1)
tableView.reloadRowsAtIndexPaths([path], withRowAnimation:UITableViewRowAnimationAutomatic)
end