PySimpleGui: How to add values from one listbox to another - listbox

So I've been looking for an answer to this for quite a while. Apologies if I'm just overlooking something here.
I want to create two listboxes where you can move the values from one listbox into another listbox, creating your own order for those values. This is because I'm trying to make a GUI for an API, and the order of the values will ultimately determine the order of dimension/metric headers, which is extremely important.
This was the approach I tried to take to achieve this, however, it appears the TEST object just overwrites its list everytime the "Add" button is pressed, whereas I want it to append:
import PySimpleGUI as sg
list1 = ["a", "b", "c"]
layout = [[sg.Text("Demo")],
[sg.Listbox(values=list1, size=(30,6), enable_events=True, key="-LIST-"), sg.Button("Add", enable_events=True, key="-BUTTON-"), sg.Listbox(values=[], size=(30,6), key="-LIST2-")],
[sg.Cancel("Exit")]]
window = sg.Window("Beta", layout=layout, background_color="#272533", size=(650, 450))
while True:
event, values = window.read()
if event == "Exit" or event == sg.WIN_CLOSED:
break
if event == "-BUTTON-":
TEST = []
TEST.append(values["-LIST-"])
window["-LIST2-"].update(TEST)
print(TEST)
window.close()

Thank you Jason, I managed to set up two listboxes such that you can move items from one box to another and thereby create whatever order of items you want by using get_indexes().
Basically I started with an empty list, then used get_indexes() to find the index of the value a user determines in the initial list. I then append that value onto the empty list using the pop function so it is deleted from the initial list. Then I had to update both windows so the indexes would update for when the user moves the next value.
Here's the code I used to get it to work. It's probably not the most beautiful or efficient (I'm quite new to python) but it does work! If there are any suggestions for how to make it more succinct they would be appreciated!
import PySimpleGUI as sg
list1 = ['a', 'b', 'c']
list2 = []
layout = [[sg.Text("Demo")],
[sg.Listbox(values=list1, size=(30,6), enable_events=True, key="-LIST-"), sg.Button("Add", enable_events=True, key="-BUTTON-"), sg.Button("Remove", enable_events=True, key="-BUTTON2-"), sg.Listbox(values=list2, size=(30,6), key="-LIST2-")],
[sg.Cancel("Exit")]]
window = sg.Window("Beta", layout=layout, background_color="#272533", size=(650, 450))
while True:
event, values = window.read()
if event == "Exit" or event == sg.WIN_CLOSED:
break
if event == "-BUTTON-":
INDEX = int(''.join(map(str, window["-LIST-"].get_indexes())))
list2.append(list1.pop(INDEX))
window["-LIST2-"].update(list2)
window["-LIST-"].update(list1)
if event == "-BUTTON2-":
INDEX = int(''.join(map(str, window["-LIST2-"].get_indexes())))
list1.append(list2.pop(INDEX))
window["-LIST2-"].update(list2)
window["-LIST-"].update(list1)
window.close()

Related

How to make only one piece of data affected by UserDefaults?

So I just did a bit of research on UserDefaults last night and this morning and I wanna know how I can use dictionaries in a certain way. My issue is that when a user presses a certain button and the UserDefaults dictionary data gets set, every piece of data in the app is affected as well. My goal is to just have that piece of data affected only and the rest stay the same as they were before.
let eventDict = ["eventName": "\(selectedEventName!)", "purchased": true] as [String : Any]
This is the dictionary I set, pretty simple. And when the button is pressed I run this line of code.
self.defaults.set(eventDict, forKey: "eventDict")
These work perfect and I check the .plist file and everything is correct, it shows the event name and the purchased as 1 (true).
Now I tried to add some logic, in my viewDidLoad() of the page I purchase the event on and it works but when I check every other event the page has the same outcome, which is not what I want.
let checkEventStatus = defaults.dictionary(forKey: "eventDict")
if checkEventStatus?.isEmpty == false {
viewPurchaseButton.isHidden = false
cancelPurchaseButton.isHidden = false
purchaseTicketButton.isHidden = true
creditCard.isHidden = true
} else {
viewPurchaseButton.isHidden = true
cancelPurchaseButton.isHidden = true
}
I couldn't figure out how to retrieve an exact value from a key in a dictionary, so I just used isEmpty() instead which I thought would make sense because if the event wasn't purchased, it wouldn't have a dictionary with it's name in it.
These are the buttons I want to show up when the purchase button is pressed. The purchase button is hidden right now because it was already pressed, so the UserDefault data is set, and that effects the button visibility. Now when I check every other event, the screen is the same. I just want each screen to be a certain way depending on if the event is purchased or not using UserDefaults.
I think you have a fundamental problem - you're not actually saving any purchase status liked to an event in the first place, so there's no way you'll be able to determine the status for an event and set the button status accordingly. You need a data structure that associates the event name with the event status.
I think you'll need more thought into your data and workflow, but as a trival example that might get you started, you could create a Event struct to hold both name and status. You could also build in the purchase date and cost...
struct Event {
name: String
purchased: Bool
purchaseDate: Date?
cost: Double?
}
and then you could carry on using a dictionary in the form of [String: Event] where the key is some sort of string for event ID, but it'd probably be easier for now to work with an array [Event]
You could then iterate through the array showing all the events (or filter to get specific ones) and then set the screen up accordingly, for example if event is an item from the array
eventNameField.text = event.name
if purchased, let date = event.purchaseDate, let cost = event.cost {
purchasedDateField.text = "\(date)" //would really be a dateFormatted date
costFiled.text = "\(cost)"
}
purchaseButton.isHidden = event.purchased
cancelButton.isHidden = !event.purchased

JavaFX TableView Selection Binding doesn't always fire - is there a better work around?

I have the need to disable a button in my application provided nothing is selected or multiple rows are selected.
I created the following binding (extra code to debug it since it wasn't working)..
BooleanBinding singleDocIsntSelected =
Bindings.createBooleanBinding(() -> {
boolean result = documentTable.getSelectionModel().getSelectedItems().size() != 1;
return result;
}, documentTable.getSelectionModel().selectedItemProperty());
What happens to me is that when I select the first row, it fires, then if I CTRL + Click the same row, it fires again. So far all is well in the world. Then I Click the same row - nothing, or when I CTRL Click other rows nothing.
My button will stay in the wrong state.
However, if I also add a listener to the property:
ChangeListener<MyDTO> selectedItemsListener = (obs, ov, nv) -> {
boolean result = table.getSelectionModel().getSelectedItems().size() != 1;
System.err.println(result);
};
Then everything works properly.
Is there no other way to handle this?
You need the binding to be invalidated when the list of selected items changes, not when the selectedItem property changes. If one item is selected, that becomes the selected item: if you then select additional items, the selectedItemProperty will not change (it is still the first one selected out of all those selected).
Using your current idiom, just bind to the selected items list:
BooleanBinding singleDocIsntSelected =
Bindings.createBooleanBinding(() -> {
boolean result = documentTable.getSelectionModel().getSelectedItems().size() != 1;
return result;
}, documentTable.getSelectionModel().getSelectedItems());
or, more simply,
BooleanBinding singleDocIsntSelected =
Bindings.createBooleanBinding(
() -> documentTable.getSelectionModel().getSelectedItems().size() != 1,
documentTable.getSelectionModel().getSelectedItems());
Though it's probably cleaner to use Bindings.size() and IntegerBinding.isNotEqualTo():
BooleanBinding singleDocIsntSelected = Bindings
.size(documentTable.getSelectionModel().getSelectedItems())
.isNotEqualTo(1);

How to get index of XCUIElement in XCUIElementQuery?

This is my simple UITest (customizing order of tabs in tabbarcontroller):
func testIsOrderOfTabsSaved() {
let app = XCUIApplication()
let tabBarsQuery = app.tabBars
tabBarsQuery.buttons["More"].tap()
app.navigationBars["More"].buttons["Edit"].tap()
tabBarsQuery.buttons["Takeaway"].swipeLeft()
tabBarsQuery.buttons["In Restaurant"].swipeLeft()
//here, how to get position of moved button with text "In Restaurant"?
NOTE:
It is possible to get XCUIElement from XCUIElementQuery by index. Can I do this fro the other way?
It seems that the queries automatically return in order based on position on screen.
for i in 0...tabsQuery.buttons.count {
let ele = tabsQuery.buttons.elementBoundByIndex(i)
}
Where the index i represents the position of the button in the tab bar, 0 being the leftmost tab, i == tabsQuery.buttons.count being the rightmost.
You have various ways to create a position test. The simplest way is to get buttons at indices 0 and 1, then get two buttons by name and compare the arrays are equal: (written without testing)
let buttonOrder = [tabBarsQuery.buttons.elementAtIndex(0), tabBarsQuery.buttons.elementAtIndex(1)]
let takeawayButton = buttons["Takeaway"];
let restaurantButton = buttons["In Restaurant"];
XCTAssert(buttonOrder == [takeawayButton, restaurantButton])
Another option is to directly get the frame of each button and assert that one X coordinate is lower than the other.
To answer your specific question about getting the index of an XCUIElement in a XCUIElementQuery, that's absolutely possible. Just go through all the elements in the query and return the index of the first one equal to the element.
An XCUIElement alone isn't able to tell you its position in within the XCUIElementQuery. You can search the XCUIElementQuery to discover its offset if you know something about the XCUIElement. In the below let's imagine that "More" is the identifier (change 'identifier' to 'label' if that is what you're working with).
If you want to find the offset of the "More" button (as posted in the original question) then:
var offsetWithinQuery : Int? = nil // optional since it's possible the button isn't found.
for i in 0...tapBarsQuery.buttons.count{
if tapBarsQuery.elementAtIndex(i).indentifier == "More" {
offSetWithinQuery = i
}
After the above loop exits, you'll either have the offset or it'll be nil if "More" isn't found.

Unhighlight first highlighted item from AutoComplete

Here I am using p:autoComplete tag in PrimeFaces 5.1, but I can't remove first highlighted/selected item from the suggestions list. How I can remove this ?
Since your "problem" comes from this particular line,
firstItem.addClass('ui-state-highlight');
What happens here is when the suggestions are ready to show up the script highlights the first item of the list, so in your case you would just "unhighlight" that item.
I have created a small function that would do that on every AutoComplete you have, you can call it in your document.ready or where ever you see suitable:
function removeHighlightedFirstItem() {
var oldAutoCompleteShowSuggestions = PrimeFaces.widget.AutoComplete.prototype.showSuggestions;
PrimeFaces.widget.AutoComplete.prototype.showSuggestions = function(query) {
//calling original ShowSuggestions
oldAutoCompleteShowSuggestions.apply(this, [query]);
//after ShowSuggestions
//remove first item highlight
var firstItem = this.items.eq(0);
firstItem.removeClass('ui-state-highlight');
}
}
The result would look like this:
Note: The feature you are requesting is available for 5.1.5, 5.0.14 and 5.2 by adding the autoHighlight = "false" attribute to the component.

Add an event to HTML elements with a specific class

I'm working on a modal window, and I want to make the function as reusable as possible. Said that, I want to set a few anchor tags with a class equals to "modal", and when a particular anchor tag is clicked, get its Id and pass it to a function that will execute another function based on the Id that was passed.
This is what I have so far:
// this gets an array with all the elements that have a class equals to "modal"
var anchorTrigger = document.getElementsByClassName('modal');
Then I tried to set the addEventListener for each item in the array by doing this:
var anchorTotal = anchorTrigger.length;
for(var i = 0; i < anchorTotal ; i++){
anchorTrigger.addEventListener('click', fireModal, false);
}
and then run the last function "fireModal" that will open the modal, like so:
function fireModal(){
//some more code here ...
}
My problem is that in the "for" loop, I get an error saying that anchorTrigger.addEvent ... is not a function.
I can tell that the error might be related to the fact that I'm trying to set up the "addEventListener" to an array as oppose to individual elements, but I don't know what I'm supposed to do.
Any help would be greatly appreciated.
anchorTrigger[i].addEventListener...

Resources