I wish to have a list of text items in a PySimpleGUI that I can update later. That is, I want to have a key for the list. This might be vertical or horizontal, and I do not know how many items there will be.
I end up with different use cases, but the current one is to make a single line of text items with different colors. Other times, I need to write and update a customized table, just different enough that the table widget does not work.
Conceptually, I want to do something like this:
layout = [ [sg.T('Titles and Things')], sg.ListThing(key='-data-', [[]]) ]
so that I can:
window['-data-'].update(values=[ [sg.T(v, color=c)] for (v,c) in my_data ])
Another, invalid syntax, way of saying what I want is to use [key="-data-", sg.T('Item1'), sg.T('Item2')].
Is this possible?
You can update individual layout elements but you cannot dynamically change the layout itself.
It is possible to create 2 or more elements, whereby only one of them is visible, and switch them later as needed. Or you can close and re-create the window with another layout. Or combine both approaches.
An example of switching layouts:
def change_layout():
left_col_1 = sg.Column([[sg.Text(f'Text {i}') for i in range(4)]], visible=True, key='col_1')
left_col_2 = sg.Column([[sg.Text(f'Text {i}')] for i in range(6)], visible=False, key='col_2')
visible_1 = True
layout = [[sg.Column([[left_col_1, left_col_2]]), sg.Button('Change layout', key='change')]]
window = sg.Window('window', layout=layout, finalize=True)
while True:
event, values = window.read()
print(event)
print(values)
print(visible_1)
if event in ('Exit', sg.WIN_CLOSED):
break
if event == 'change':
window['col_1'].update(visible=not visible_1)
window['col_2'].update(visible=visible_1)
visible_1 = not visible_1
Please notice that the alternative layouts for the left part (left_col_1, left_col_2) need to be enclosed in a container (column, frame) to keep their position in the window in the moment they are invisible.
Related
I have a view-based NSOutlineView with a dataSource/delegate model instead of binding to a tree controller (I want control over the insert/update animations).
I'm replacing an item in my model and would like to update that specific row in the outline view without having to call reloadData().
I cannot get this to work. Either the item does not update at all or the item's expanded state doesn't update. There seems to be some caching being done inside of NSOutlineView according to this, but even with these suggestions, I could not get it to work.
What I have:
(1) The outline view represents a folder structure
(2) At first, there is a singe file:
(3) The file is then replaced with a folder item:
// Model update
let oldFileItem = rootItem.children.first!
rootItem.children.remove(at: 0)
rootItem.children.append(Item(title: "Folder", children:[], isExpandable:true))
Expected result:
Actual result (reloadItem):
outlineView.reloadItem(oldFileItem) // I kept a reference
Icon and title reloaded, but note that the expansion triangle is missing.
I can somewhat understand that reloadItem() might not work in this case, because the old item is not part of the data model anymore. Strangely enough, the item's title and icon update, but not the expansion state.
Actual result (reloadData(forRowIndexes:columnIndexes:):
outlineView.reloadData(forRowIndexes: IndexSet(integer:0), columnIndexes: IndexSet(integer:0))
No effect whatsoever. This is the one that I would have expected to work.
Actual result (remove/insert):
outlineView.removeItems(at: IndexSet(integer:0), inParent: rootItem, withAnimation: [])
outlineView.insertItems(at: IndexSet(integer:0), inParent: rootItem, withAnimation: [])
No effect whatsoever.
The docs say about removeItems(): "The method does nothing if parent is not expanded." and isExpanded does indeed return false for the root node, although its children are visible. Is this special behavior for items that are direct children of the root node? What am I missing here?
For reference, my data model:
class Item:NSObject {
var title:String
var children:[Item]
var isExpandable:Bool
init(title:String, children:[Item], isExpandable:Bool) {
self.title = title
self.children = children
self.isExpandable = isExpandable
}
}
For reference:
It turned out to be an issue with how I used the API. NSOutlineView.removeItems/insertItems expect nil for the inParent parameter for the root item. I was handing in the actual root item. Using nil instead of the root item solved the problem.
I am writing an UI test case, in which I need to perform an action, and then on the current page, scroll the only UITableView to the bottom to check if specific text shows up inside the last cell in the UITableView.
Right now the only way I can think of is to scroll it using app.tables.cells.element(boundBy: 0).swipeUp(), but if there are too many cells, it doesn't scroll all the way to the bottom. And the number of cells in the UITableView is not always the same, I cannot swipe up more than once because there might be only one cell in the table.
One way you could go about this is by getting the last cell from the tableView. Then, run a while loop that scrolls and checks to see if the cell isHittable between each scroll. Once it's determined that isHittable == true, the element can then be asserted against.
https://developer.apple.com/documentation/xctest/xcuielement/1500561-ishittable
It would look something like this (Swift answer):
In your XCTestCase file, write a query to identify the table. Then, a subsequent query to identify the last cell.
let tableView = app.descendants(matching: .table).firstMatch
guard let lastCell = tableView.cells.allElementsBoundByIndex.last else { return }
Use a while loop to determine whether or not the cell isHittable/is on screen. Note: isHittable relies on the cell's userInteractionEnabled property being set to true
//Add in a count, so that the loop can escape if it's scrolled too many times
let MAX_SCROLLS = 10
var count = 0
while lastCell.isHittable == false && count < MAX_SCROLLS {
apps.swipeUp()
count += 1
}
Check the cell's text using the label property, and compare it against the expected text.
//If there is only one label within the cell
let textInLastCell = lastCell.descendants(matching: .staticText).firstMatch
XCTAssertTrue(textInLastCell.label == "Expected Text" && textInLastCell.isHittable)
Blaines answer lead me to dig a little bit more into this topic and I found a different solution that worked for me:
func testTheTest() {
let app = XCUIApplication()
app.launch()
// Opens a menu in my app which contains the table view
app.buttons["openMenu"].tap()
// Get a handle for the tableView
let listpagetableviewTable = app.tables["myTableView"]
// Get a handle for the not yet existing cell by its content text
let cell = listpagetableviewTable.staticTexts["This text is from the cell"]
// Swipe down until it is visible
while !cell.exists {
app.swipeUp()
}
// Interact with it when visible
cell.tap()
}
One thing I had to do for this in order to work is set isAccessibilityElement to true and also assign accessibilityLabel as a String to the table view so it can be queried by it within the test code.
This might not be best practice but for what I could see in my test it works very well. I don't know how it would work when the cell has no text, one might be able to reference the cell(which is not really directly referenced here) by an image view or something else. It's obviously missing the counter from Blaines answer but I left it out for simplicity reasons.
I have a dc.rowchart that has 5 different "categories". Initially, all are selected. When I click on one, then only that one is highlighted. When I click on a second one... both the first and second one I clicked are highlighted.
How can I make it/configure the rowchart such that only a single category is highlighted every time a bar is clicked?
dc.filter("category1");
dc.filter("category2");
Both of these in sequence appear to "append" filters than replace.
Ethan's answer is almost there. It's likely unintentional, but dc.js doesn't appear to use the return value for addFilterHandler, so you'll have to modify the filters parameter for it to work, like so:
myChart.addFilterHandler(function (filters, filter) {
filters.length = 0; // empty the array
filters.push(filter);
return filters;
});
This works great as long as your chart doesn't use cap (e.g. a pie chart with limited slices), as this handler prevents the others group from working. I've yet to come up with a satisfactory solution for that case, unfortunately.
I assume you'll want addFilterHandler - https://github.com/dc-js/dc.js/blob/master/web/docs/api-latest.md#dc.baseMixin+addFilterHandler
You'd probably want to do the following, but it's untested.
myChart.addFilterHander(function (filters, filter) {
return [filter];
});
Is there any way i can hide HOT columns from javascript?
The requirement is such that the column to hide will come as a parameter in javascript and based on that the respective column will show hide accordingly.
The HOT has rowHeaders and colHeaders and the data with 20 columns.
Please advise.
OUTDATED SOLUTION
Ok I founnd a possible solution. I tested it out on my own system but it's actually quite simple.
You should be using a customRenderer in your columns option. Read up about this if you aren't already. The idea is that you're giving each cell its own renderer. In this custom function, you can do something like this:
var colsToHide = [3,4,6]; // hide the fourth, fifth, and seventh columns
function getCustomRenderer() {
return function(instance, td, row, col, prop, value, cellProperties) {
if (colsToHide.indexOf(col) > -1) {
td.hidden = true;
} else {
td.hidden = false;
}
}
}
What this renderer does is hide the cells that the var colsToHide specify. All you do now is add a DOM element that lets the user pick which and so every time the table gets rendered (which happens basically after any change, or manually triggered need be), the cells in the columns specified will be hidden, keeping the data array intact like you described. And when not in colsToHide they are re-rendered so make sure you get that working as well.
Here I implemented it with very basic functionality. Just enter the index of a column into the input fields and watch the magic happen.
http://jsfiddle.net/zekedroid/LkLkd405/2/
Better Solution: handsontable: hide some columns without changing data array/object
I have designed a group of three uitabpanels objects.
htab = uitabgroup('v0');
th1 = uitab('v0',htab,'title','Panel 1','ButtonDownFcn',...
#th1_ButtonDownFcn);
th2 = uitab('v0',htab,'title','Panel 2','ButtonDownFcn',...
#th2_ButtonDownFcn);
th3 = uitab('v0',htab,'title','Panel 3','ButtonDownFcn',...
#th3_ButtonDownFcn);
My intention is having a smooth transition between them when I change the selected uipanel through the mouse click. I pretend to achieve it changing the 'Visible' property of the elements contained inside them using the ButtonDownFcn function ( I got this idea based on the description section of this page).
set(handles.th2,'Visible','off');
set(handles.th3,'Visible','off');
...
function th1_ButtonDownFcn(hObject, eventdata)
handles = guidata(fh);
set(handles.th1,'Visible','on');
set(handles.th2,'Visible','off');
set(handles.th3,'Visible','off');
guidata(fh,handles);
end
function th2_ButtonDownFcn(hObject, eventdata)
handles = guidata(fh);
set(handles.th1,'Visible','off');
set(handles.th2,'Visible','on');
set(handles.th3,'Visible','off');
guidata(fh,handles);
end
function th3_ButtonDownFcn(hObject, eventdata)
handles = guidata(fh);
set(handles.th1,'Visible','off');
set(handles.th2,'Visible','off');
set(handles.th3,'Visible','on');
guidata(fh,handles);
end
where
fh: handle of the figure where they are contained the uitabpanels.
handles.th1, handles.th2, handles.th3: handles of the elements contained into each uitabpanel respectively.
However, it has not worked (I click on each one of uitabpanel's tabs and the visibility of them do not change) and I do not understand why.
In conclusion, the ButtonDownFcn and SelectionChangeFcn functions of an UITAB are already active when you click in the tabĀ“s label. So it is not possible to achieve the desired target (smooth optical transition) because the obtained result (modifying the mentioned functions) is the same that doing nothing.