I have a cell that produces a button that invokes a function when clicked:
stop_button = widgets.Button(description='Stop Motor')
stop_button.on_click(stop_motor)
When another cell is executing, however, the button's callback is not fired (even though the button appears to respond to the click).
Can I make the button always work, even when another cell is executing?
I'm not sure this is the best answer, but I made my longer-running task asynchronous:
%gui asyncio
import asyncio
async def work():
while True:
await asyncio.sleep(0.1)
task = asyncio.create_task(work())
However you would need to repeat this pattern for every cell that might block you from clicking your button.
Related
The question is not how to get it working, it works. I am just interested but can't find out what goes on in the background to make it work.
Prime example is the standard Blazor server webapp template, which has a "counter" and a "fetchdata" component.
In the fetchdata component, there is protected override async Task OnInitializedAsync() that gets called, and the component renders itself. If the OnInitializedAsync() does not return in time, the render still happens, in the example just writing out loading... Then when the OnInitializedAsync() actually finishes, the component is magically re-rendered, but what caused the re-render? how is it wired together?
Does the framework peek at the class' memory every so often to figure out if a rerender is needed? Is it just that there is a call for the page render before the OnInitializedAsync() call and one after awaiting it?
This is all based on async programming...
protected override async Task OnInitializedAsync()
{
forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
}
The OnInitializedAsync method you've mentioned has two parts: synchronous and asynchronous. When the OnInitializedAsync method starts executing, a call to the awaitable GetForecastAsync method is made, control is yielded to the calling code, Blazor runtime, that starts rendering the UI without delay. This is the first time your component is rendered. This is also why you must check in code that the forecasts variable is not null. When GetForecastAsync returns and the forecasts variable is populated, more code (if exists) is executed to the end of the method, after which your component re-renders again, this time forecasts is not null, and thus the table is rendered. Of course, this was a simplistic explanation. You can inspect the process, step by step, by viewing the code that does this: It all starts here. It shouldn't be difficult to follow. No magic
Hope this helps...
I have a WKInterfaceController with a WKInterfaceTable that lists some events a user has recorded in my app.
A user can tap a row of the table to see more details about that event. To accomplish this, I've overridden contextForSegue(withIdentifier:in:rowIndex:) on the WKInterfaceController that contains the table, so tapping a row modally presents a detail view of that row in a new WKInterfaceController called EventDetailController.
The modal presentation is defined on the Storyboard. I can't use push presentation because the WKInterfaceController with the WKInterfaceTable is a page among multiple instances of WKInterfaceController at the top level of my app.
Here's the main issue:
Within the EventDetailController, there's a Delete button to destroy the record that the table row represents.
When a user taps the Delete button, I present an alert that allows the user to confirm or cancel the delete action.
Once the user confirms the record deletion, I want to dismiss the EventDetailController since it's no longer relevant, because it represents a deleted record.
Here's the IBAction defined on EventDetailController that gets called when the Delete button is tapped:
#IBAction func deleteButtonTapped(_ sender: WKInterfaceButton) {
let deleteAction = WKAlertAction(title: "Delete", style: .destructive) {
// delete the record
// as long as the delete was successful, dismiss the detail view
self.dismiss()
}
let cancelAction = WKAlertAction(title: "Cancel", style: .cancel) {
// do nothing
}
presentAlert(withTitle: "Delete Event",
message: "Are you sure you want to delete this event?",
preferredStyle: .alert,
actions: [deleteAction, cancelAction])
}
The problem is that watchOS doesn't seem to allow this. When testing this code, the EventDetailController does not dismiss. Instead, an error message is logged in the console:
[WKInterfaceController dismissController]:434: calling dismissController from a WKAlertAction's handler is not valid. Called on <Watch_Extension.EventDetailController: 0x7d1cdb90>. Ignoring
I've tried some weird workarounds to try to trick the EventDetailController into dismissing, like firing a notification when the event is deleted and dismissing the EventDetailController from a function that's called from an observer of the notification, but that doesn't work either.
At this point I'm thinking there's some correct way I'm supposed to be able to dismiss a WKInterfaceController, or in other words reverse the contextForSegue(withIdentifier:in:rowIndex:) call, but I don't know what it is.
When I call dismiss() directly in the IBAction, instead of in a WKAlertAction handler, it works fine, but I don't like this implementation since it doesn't allow the user to confirm the action first.
I feel like an idiot, but I figured out the solution.
The answer was in Apple's WKInterfaceController.dismiss() documentation the whole time (emphasis added):
Call this method when you want to dismiss an interface controller that you presented modally. Always call this method from your WatchKit extension’s main thread.
All I had to do differently was call self.dismiss() on the main thread.
Here's my updated code for the delete action, which now works as expected:
let deleteAction = WKAlertAction(title: "Delete", style: .destructive) {
// delete the record
// as long as the delete was successful, dismiss the detail view
DispatchQueue.main.async {
self.dismiss()
}
}
Hopefully this will save someone else some troubleshooting time!
As soon as I press "Enter" after I wrote an asynchronous function into a cell, the async function is correctly called, and Excel raises the event xleventCalculationEnded when the calculation is finished.
However, if I press another cell just after I clicked "Enter" , the event xleventCalculationCanceled is raised, and then the async function is called another time ! Is this behavior normal ? Should I return a result via the Excel12(xlAsyncReturn,...) for the first async call , for the second async call or for both ?
In other word, does the xleventCalculationCanceled event implies that I'm not forced to return a result to Excel ? (using the appropriate asyncHandle)
I'm using async functions to delegate intensive computation in another thread and to not block excel during computation. However if the async function is called automatically two times (as it is the case when the user click another cell without waiting for the first call to finish) then the intensive computation are computed two times for the same input (because the first call -cancelled by excel- still live in the delegate thread...) How do you deal with this problem ?
Two calls for the same function - with the same input - is it a bug ?
Many thanks
What you describe is the normal behaviour. Excel cancels and then restarts the async calculations when there is user interaction (and can do so multiple times).
The documentation suggest that:
xleventCalculationEnded will fire directly after xleventCalculationCanceled, and
You can release any resources allocated during the calculation when xleventCalculationEnded fires. I understand that to include any asyncHandle you might have, and thus that you need not return any result based on the handle.
If your long-running function allows cancellation while in flight, you can cancel the work you do. Otherwise, you might do some internal bookkeeping on what function calls are in flight, and prevent doing the work twice yourself that way.
I'm wanting to customize the look of a QProgressBar with a stylesheet. I'd like to give it that animated, 'indeterminate' look. So I made an animated GIF to stick in the background of the QProgressBar::chunk.
It shows up fine, but the image is static, with no animation. Any suggestions or workarounds?
I'm running PyQt 4.9.4 on OS X 10.8.
So, this is a slightly more complex problem that you're facing.
The QProgressBar relies on information from a loop or some determined amount to represent the percentage complete. When you set the value through your code, Qt will set the value of the progress bar, redraw it and thus show the updated value. This actually takes a few milliseconds of processing within your loop. If you didn't do this, then Qt's optimization wouldn't ever redraw. This is sychronous.
The "indeterminate" look that you are going for (like an AJAX gif spinner or bar) is used on the web because the process actually moved off the client-side and is being processed on a server and is waiting for the response. The client's process is not blocked at all, so it is free to update the interface with the movie.
You can achieve the look you're going for by using QMovie in a QLabel:
movie = QMovie()
movie.setFileName('/path/to/ajax_loader.gif')
movie.start()
label = QLabel(parent)
label.setMovie(movie)
This will make your indeterminate spinner. However, it will only play as long as the event loop is processing events. Once you start your actual worker process, it blocks the event loop so your movie will start playing.
You'll need to actually "pump" the events to the player in your loop to get it to update. You can do this by:
app = QApplication.instance()
# show my label
label.show()
for obj in objs:
# process stuff on my obj
app.processEvents()
# hide the label
label.hide()
Of course, depending on how long each individual action you're doing in your loop takes, your spinner/loader movie will be "stuck" until the events are processed again.
Really, the best way to achieve the look you are going for is with a threaded application. You can use QThread to perform all of your actions and display the loader image to the user while it is processing. This is more similar to the way ajax works - the main Qt Event Loop will continue running as your worker thread processes everything - in an unknown amount of time. This is asynchronous.
Something like:
class MyThread(QThread):
def run( self ):
# perform some actions
...
class MyDialog(QDialog):
def __init__( self, parent ):
# initialize the dialog
...
self._thread = MyThread(self)
self._thread.finished.connect(self.refreshResults)
self.refresh()
def refresh( self ):
# show the ajax spinner
self._ajaxLabel.show()
# start the thread and wait for the results
self._thread.start()
def refreshResults( self ):
# hide the ajax spinner
self._ajaxLabel.hide()
# process the thread results
...
I have a loader widget that I use whenever I do stuff like this in my GUI library. If you want to see the code for it/use it, its at http://dev.projexsoftware.com/projects/projexui and the class is XLoaderWidget (projexui.widgets.xloaderwidget)
The setup is same as above, but would just be:
from projexui.widgets.xloaderwidget import XLoaderWidget
class MyThread(QThread):
def run( self ):
# perform some actions
...
class MyDialog(QDialog):
def __init__( self, parent ):
# initialize the dialog
...
self._thread = MyThread(self)
self._thread.finished.connect(self.refreshResults)
self.refresh()
def refresh( self ):
# show the ajax spinner
XLoaderWidget.start(self)
# start the thread and wait for the results
self._thread.start()
def refreshResults( self ):
# hide the ajax spinner
XLoaderWidget.stop(self)
# process the thread results
...
I need to call FileReference.save() after a web service call has completed, but this method has a restriction: "In Flash Player, you can only call this method successfully in response to a user event (for example, in an event handler for a mouse click or keypress event). Otherwise, calling this method results in Flash Player throwing an Error exception." (from the documentation here)
This restriction is a bit vague. Does it mean that I can only call the FileReference.save() method from within an event handler function that is registered as a listener for certain types of user events? If so then exactly which user events are valid? (Perhaps there's an event that will never be dispatched by user interaction with my application and I could register an event handler function for that event type and make the save() call from within that function?)
My difficulty is that I can't safely call the FileReference.save() method until my web service returns with the data that will be used as the argument of the FileReference.save() method call, so the event that triggers the FileReference.save() call is actually a ResultEvent rather than a user event, and I'm leery of dispatching a new (faux) user event type in order to be able to trigger the FileReference.save() call unless it's definitely a user event that would never be dispatched as a result of actual user interaction with my application.
In a nutshell what I'm doing now is this: I have a function that is registered as a handler for a button click. In this function I make my web service call to fetch data from the server. I also have a result handler function which gets invoked when the web service call completes, and it's in here that I want to call the FileReference.save() method since it's at this point that I know that the data is ready to be saved to a file. But the aforementioned restriction is blocking me from doing this -- I get an error:
Error #2176: Certain actions, such as those that display a pop-up window,
may only be invoked upon user interaction, for example by a mouse click
or button press.
I've tried many things to get around this such as creating a second mouse click event handler function with the FileReference.save() call within and calling it after a timeout interval (to give the web service time to complete), but I keep running into the same error -- maybe that approach doesn't work since the second function isn't registered as an event listener for the event type used as its argument.
I'm new to Flex development so perhaps I'm just not thinking about this in the right way. If anyone can suggest another approach I'd really appreciate it. Thanks in advance for your comments or suggestions.
--James
Adobe does this as a sort of security measure to ensure users are the ones messing with files rather than potentially harmful code. My understanding is that they enforce this by only allowing handlers of (click?) events that originate from UI components to execute the FileReference methods, so generating your own events programmatically will not work, although I have not tried to verify this. Unfortunately the best resolution I've found is to re-work the UI a bit to conform to this constraint. In your particular situation, you could make this a two click process with a button that says something like "Prepare Download", which changes to "Download File" after the web service is complete. This is less than ideal from a user perspective, but I don't think there's much else that can be done unless you can somehow complete your web service call prior to displaying the button that triggers the FileReference.save() call.
After struggling for that for well, a couple hours I found a workaround: you can use both mouseDown AND mouseUp events instead of just click.
For instance:
s:Button
mouseDown="prepare_PDF()"
mouseUp="save_PDF()"
Works fine for me!
Happy coding!
--Thomas
As a workaround I used the ExternalInterface class. I created a javascript function with this code
function downloadFile (url) {
window.open(url);
}
An in AS3 I call
var url = 'www.example.com/downloadfile.php?file_id=xxx';
ExternalInterface.call('downloadAttachmentFile', url);
So with that I transfer the file handling to JS/HTML.
This is a comment on Thomas' answer (I don't have enough XP to comment yet): The mousedown and mouseup workaround works nicely. Just a note that if you make any changes in prepare_PDF() that need 'undoing' in save_PDF(), then its a good idea to call that code on the mouseout event as well, since there might be a case that the user mousedown's on the button, but then moves the mouse away from the button.
This was particularly relevant for my case, in which we increase the size of a watermark on an image when the user clicks the download button (that triggers the .save() call). I reduce the size of the watermark down to normal on the mousedown and mouseout events.
I had this same issue, I chose to use flash.net methods. Call flash.net.navigateToURL(url); from an actionscript or navigateToURL(url); from mxml.
What i do to solve this is to show an alert message with an anonymous function so i don't have to create a button.
Alert.show("Do you wish to download the file?", "Confirm", Alert.OK | Alert.CANCEL, this, function (eventObj:CloseEvent):void {
if (eventObj.detail == Alert.OK) {
fileReference.save(zipOut.byteArray, dateFormater_titulo.format(new Date ()) + ".zip");
}//if
}/*function*/, null, Alert.OK);