I'm trying to make a searchable combo box. Right now it searches it, but will only give me 1 result and I have to type in the whole item in the list for it to edit the list to only include the resulted item. I'm trying to get it where if I have multiple results in the editable but not insertable combo box, that it will give me every result in the combo box, even if I only type in a partial match. For example if I type in "200" it will give me all the items that contain anywhere in it "200" and then afterwards if I type in "100" it will give me a whole different list of items to choose from but the list now contains "100" anywhere in the items instead of the previous "200".
Sample of my list
self.Part = ["200019359", "300000272", "300000275", "200018792", "10024769", "10015919", "102000765"]
My function
def PartNumber(self):
if self.PartNum.currentText() != "" :
results = [s for s in self.Part if self.PartNum.currentText() in s]
self.PartNum.clear()
self.PartNum.addItems(results)
else:
self.PartNum.clear()
self.PartNum.addItems(self.Part)
Connecting it to the combo box
self.PartNum.activated.connect(self.PartNumber)
Related
I have a working Tkinter.Listbox object, but I want to set it up so that its elements can have carriage returns without having to somehow set up multiple linked items.
For instance, if I want to generate a selection pane with items that look like this..
# Here are four elements for the selector Listbox..
lb_items = ('mama', 'luigi', 'my birds', \
'this is a single element\n spanning two lines!')
# This generates and displays the selector window..
tk_selector = SingleSelect(lb_items, "TEST SELECTOR")
tk_selector.run_selector()
..it would be great if I could get the output to look like this mockup..
..instead of what it actually generates, which is this..
Listboxes seem to ignore '\n' and triple-quote strings with line-returns entirely; if \n is used, neither the characters nor the line break appears.
Is it possible to have individual, selectable Listbox elements that appear with line breaks?
I would also be satisfied with a word-wrap option, but after some looking, I couldn't find any such option in Listbox or Tk in general.
I could probably fake the effect by making multiline strings into multiple elements then setting it up to return the whole line if any of them are called, but it feels like an ordeal for something that could have a simple solution.
Like Bryan Oakley said, there is no native support in Listbox for carriage returns, so I tried building the 'fake' version I mentioned in the question, and it turns out that it isn't really that hard.
My solution is to parse each 'raw' string before inserting them into the Listbox, breaking strings into individual lines using splitlines, recording the number of lines and which indices in the Listbox's element roll correspond to which unbroken input string, and then selecting all the parts whenever the Listbox's selection changes using Listbox.bind('<<ListboxSelect>>', self._reselection_fxn).
There's an abridged and annotated sample below, or you can see my complete, working, even-more-heavily annotated code here.
class Multiline_Single_Selector(object):
## Go ahead and choose a better class name than this, too. :/
def __init__(self, itemlist, ..):
# ..
lb_splitlines = self._parse_strings(itemlist)
# ^ splits the raw strings and records their indices.
# returns the split strings as a list of Listbox elements.
self.my_Listbox.insert(0, *lb_splitlines)
# ^ put the converted strings into the Listbox..
self.my_Listbox.bind('<<ListboxSelect>>', self._reselect)
# ^ Whenever the Listbox selection is modifed, it triggers the
# <<ListboxSelect>> event. Bind _reselect to it to determine
# which lines ought to be highlighted when the selection updates.
# ..
def _parse_strings(self, string_list):
'''Accepts a list of strings and breaks each string into a series of lines,
logs the sets, and stores them in the item_roster and string_register attributes.
Returns the split strings to be inserted into a Listbox.'''
self.index_sets = index_sets = []
# ^ Each element in this list is a tuple containing the first and last
# Listbox element indices for a set of lines.
self.string_register = register = {}
# ^ A dict with a whole string element keyed to the index of the its
# first element in the Listbox.
all_lines = []
# ^ A list of every Listbox element. When a string is broken into lines,
# the lines go in here.
line_number = 0
for item in string_list:
lines = item.splitlines()
all_lines.extend(lines) # add the divided string to the string stack
register[line_number] = item
# ^ Saves this item keyed to the first Listbox element it's associated
# with. If the item is selected when the Listbox closes, the original
# (whole) string is found and returned based on this index number.
qty = len(lines)
if qty == 1: # single line item..
index_sets.append((line_number, line_number))
else: # multiple lines in this item..
element_range = line_number, line_number + qty - 1
# ^ the range of Listbox indices..
index_sets.extend([element_range] * qty)
# ^ ..one for each element in the Listbox.
line_number += qty # increment the line number.
return all_lines
def _reselect(self, event=None):
"Called whenever the Listbox's selection changes."
selection = self.my_Listbox.curselection() # Get the new selection data.
if not selection: # if there is nothing selected, do nothing.
return
lines_st, lines_ed = self.index_sets[selection[0]]
# ^ Get the string block associated with the current selection.
self.my_Listbox.selection_set(lines_st, lines_ed)
# ^ select all lines associated with the original string.
def _recall(self, event=None):
"Get the complete string for the currently selected item."
selection = self.my_Listbox.curselection()
if selection: # an item is selected!
return self.string_register[selection[0]]
return None # no item is selected.
If you adjust this code to match your own and drop it into an existing Listbox setup, it ought to let you simulate carriage returns. It's not the same as a native line-wrap or \n-parsing function directly in Listbox, but it does basically the same thing.
To get the whole string corresponding to the current selection, just bind _recall to whatever input or event you want to have return it. In this case, it returns None when no item is selected.
It's also kind of a lot of work considering the sophistication of the desired effect and it may not be appropriate to every situation. But at least you can do it.
It is not possible for a listbox item to be spread across more than one line or row.
This solution seems to me much easier:
Instead of trying:
lb_items = ('mama', 'luigi', 'my birds', \
'this is a single element\n spanning two lines!')
make two elements:
lb_items = ('mama', 'luigi', 'my birds', \
'this is a single element', 'spanning two lines!')
I have two separate data-sets that I am working with:
The primary database, containing retail data (POS), has an item-code column, e.g (12831903, E03JD920).
(I imported this as a DF AS importing as tibble threw parsing error)
The secondary database has the item-codes and the corresponding item description. I put this into an R list:
itemlist <- setNames(as.list(itemcodes$l1desc),itemcodes$itemcode)
The main dataset is stored as main. I am attempting to write a function / loop that checks every single row of the column itemcode in main and see if there is a match in itemlist. If there is a match, the function/loop pastes the corresponding value from the matching key into a new vector. This process loops until every single row has been found a match for.
How can I write this loop?
The image below shows what the list looks like
The second image is a snippet of what the main dataframe looks like
So to summarise again, I am looking to write a function that parses through the list to match values with the itemcodes in the main df. My eventual goal is to take the new vector with the matching item descriptions, and then merge that with the main-dataframe once again.
for(i in 1:nrow(transactions)) {
if (transactions[i,4] == itemlist[i]) {
#if item code in pos log is equal to item code in list, paste corresponding
## value from key-value pair
mapped_items[i] <- paste0(names(itemlist))
}}
View(mapped_items)
This is what my pseudo-code looks like, I tried running a few initial variants of this, but kept getting thrown an error that NAs were being parsed through, but this may have been because of the tibble. Any ideas as to how I can elegantly implement this function/loop?
I am also filling to try this with dict in python if necessary
My python pseudocode is as follows:
def search(itemcode, code):
match "" " "
for i in itemcode:
if (code == i):
match = code
return match
def mapitems(transactions,itemcode):
mappedlist = {}
i = 0
for code in transactions:
mappedlist.append(search(itemcode,code))
return mappedlist
Actually, you should be using neither R nor Python to do what would be a very basic operation on your actual SQL database:
SELECT *
FROM item_codes ic
INNER JOIN retail_data rd
ON rd.item_code = ic.item_code
Just execute the above query, and then access the result set as some kind of collection in your R or Python script.
I'm trying to iterate over a number of elements returned by matching class names, which I have stored in an array users. The print(len(users)) outputs as 12, which is accurately correct as to how many returned there should be. This is my code:
def follow():
time.sleep(2)
# iterate here
users = []
users = browser.find_elements_by_class_name('wo9IH')
print(len(users))
for user in users:
user_button = browser.find_element_by_css_selector('li.wo9IH div.Pkbci').click()
#user_button = browser.find_element_by_xpath('//div[#class="Pkbci"]/button').click()
However currently, only index [0] is being .click()'d and the program is terminating after this first click. What would be the problem as to why the index being iterated isn't incrementing?
resource: image - red shows what's being iterated through and blue is each button being .click()'d
try this,
You can directly make array of buttons rather than li array,
Go click all buttons contains text as Follow,
simple,
browser.maximize_window()
users = []
users = browser.find_elements_by_xpath('*//button[text()='Follow']')
print(len(users)) # check it must be 12
for user in users:
browser.execute_script("arguments[0].click()", user)
# user.click() Go click all buttons
Find all your css_selector elements as a list and then iterate that list to perform .click()
yourList = browser.find_elements_by_css_selector('w0o9IH div.Pkbci')
users = browser.find_elements_by_class_name('wo9IH') returns a list of selenium.webdriver.remote.webelement.WebElement instances that can also be transversed.
In your implementation of the iteration, the above fact about the items in the list is overlooked and the entire page is search by transversing the page source from the WebDriver instance (i.e. browser.find_element_by_css_selector).
Here is how to go about getting the button in the matched WebElements:
for user_web_element in users:
# The next line given that there is only a single <button>
# in the screenshot for the matched WebElements.
user_button = user_web_element.find_element_by_tag_name('button')
user_button.click()
I have this code
lst = ["Appearence","Logotype", "Catalog", "Product Groups", "Option Groups","Manufacturers","Suppliers",
"Delivery Statuses","Sold Out Statuses", "Quantity Units", "CSV Import/Export", "Countries","Currencies","Customers"]
for item in lst:
wd.find_element_by_link_text(item).click()
assert wd.title != None
I not want to write list by hand.
I want to receive the list - lst directly from the browser.
I use
m = wd.find_elements_by_css_selector('li[id=app-]')
print(m[0].text)
Appearence
I don't know how to transfer the list to a cycle
look this picture screen browser
Please help me to understand how to use the list and to transfer it to a cycle
In your example variable m will be a list of WebElements you get the length of it and iterate CSS pseudo selector :nth-child() with a range:
m = wd.get_elements_by_css_selector('li#app-')
for elem in range(1, len(m)+1):
wd.get_element_by_css_selector('li#app-:nth-child({})'.format(elem)).click()
assert wd.title is not None
In the for loop it will iterate over a range of integers starting with 1 and ending with the length of the element list (+1 because is not inclusive), the we will click the nth-child of the selector using the iterating number, .format(elem) will replace th {} appearance in the string for the elem variable, in this case the integer iteration.
Is there an easier way to change the order of items in a tkinter listbox than deleting the values for specific key, then re-entering new info?
For example, I want to be able to re-arrange items in a listbox. If I want to swap the position of two, this is what I've done. It works, but I just want to see if there's a quicker way to do this.
def moveup(self,selection):
value1 = int(selection[0]) - 1 #value to be moved down one position
value2 = selection #value to be moved up one position
nameAbove = self.fileListSorted.get(value1) #name to be moved down
nameBelow = self.fileListSorted.get(value2) #name to be moved up
self.fileListSorted.delete(value1,value1)
self.fileListSorted.insert(value1,nameBelow)
self.fileListSorted.delete(value2,value2)
self.fileListSorted.insert(value2,nameAbove)
Is there an easier way to change the order of items in a tkinter listbox than deleting the values for specific key, then re-entering new info?
No. Deleting and re-inserting is the only way. If you just want to move a single item up by one you can do it with only one delete and insert, though.
def move_up(self, pos):
""" Moves the item at position pos up by one """
if pos == 0:
return
text = self.fileListSorted.get(pos)
self.fileListSorted.delete(pos)
self.fileListSorted.insert(pos-1, text)
To expand on Tim's answer, it is possible to do this for multiple items as well if you use the currentselection() function of the tkinter.listbox.
l = self.lstListBox
posList = l.curselection()
# exit if the list is empty
if not posList:
return
for pos in posList:
# skip if item is at the top
if pos == 0:
continue
text = l.get(pos)
l.delete(pos)
l.insert(pos-1, text)
This would move all selected items up 1 position. It could also be easily adapted to move the items down. You would have to check if the item was at the end of the list instead of the top, and then add 1 to the index instead of subtract. You would also want to reverse the list for the loop so that the changing indexes wouldn't mess up future moves in the set.