wxPython change mouse cursor to notify a long running operation - python

I am building a Python program that searches things on a remote website.
Sometimes the operation takes many seconds and I believe that the user will not notice the status bar message "Search Operation in progress".
Therefore, I would like to change the mouse cursor to highlight that the program is still waiting for a result.
This is the method I am using:
def OnButtonSearchClick( self, event ):
"""
If there is text in the search text, launch a SearchOperation.
"""
searched_value = self.m_search_text.GetValue()
if not searched_value:
return
# clean eventual previous results
self.EnableButtons(False)
self.CleanSearchResults()
operations.SearchOperation(self.m_frame, searched_value)
I tried two different approaches, both before the last line:
wx.BeginBusyCursor()
self.m_frame.SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
None of them are working.
I am using KDE under GNU/Linux. This does not work under Gnome, too
Any hints? Thank you

I asked Robin Dunn, the maker of wxPython about this, and it looks like this should work, but doesn't. However, if you call the panel's SetCursor(), it DOES work or so I'm told. Here's an example you can try:
import wx
########################################################################
class MyForm(wx.Frame):
#----------------------------------------------------------------------
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Tutorial")
# Add a self.panel so it looks the correct on all platforms
self.panel = wx.Panel(self, wx.ID_ANY)
btn = wx.Button(self.panel, label="Change Cursor")
btn.Bind(wx.EVT_BUTTON, self.changeCursor)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(btn)
self.panel.SetSizer(sizer)
#----------------------------------------------------------------------
def changeCursor(self, event):
""""""
myCursor= wx.StockCursor(wx.CURSOR_WAIT)
self.panel.SetCursor(myCursor)
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = MyForm().Show()
app.MainLoop()

Related

Disable windows border with wxpython and cefpython3

I am following this example https://github.com/cztomczak/cefpython/blob/master/examples/wxpython.py for cefpython3 and wxpython. However I am looking to disable the windows border so the program can no longer be adjusted size wise and their is no title bar. But I would like to keep it so the program can be moved around the screen. I have done some research but not come across a way to successfully do this.
The following appears to do the job on Linux.
Your mileage may vary.
import wx
class MyFrame(wx.Frame):
def __init__(self, parent, id=wx.ID_ANY, title="", size=(360,100)):
super(MyFrame, self).__init__(parent, id, title, size, style = wx.DEFAULT_FRAME_STYLE & ~(wx.RESIZE_BORDER | wx.MAXIMIZE_BOX))
self.panel = wx.Panel(self)
self.panel.SetBackgroundColour('palegreen')
self.Show()
if __name__ == "__main__":
app = wx.App()
frame = MyFrame(None,title="Non-Resizeable Frame")
app.MainLoop()

How could I successfully run onButton during startup?

import wx
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Test")
panel = wx.Panel(self, wx.ID_ANY)
#Button is created; binded to onButton
button = wx.Button(panel, id=wx.ID_ANY, label="Press Me")
button.Bind(wx.EVT_BUTTON, self.onButton)
def onButton(self,EVT_BUTTON):
print("Hello world!")
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
#Runs Button command on startup
MyForm.onButton()
I want onButton() to run at startup, and have it be able to run when the
wx.Button is pressed. Unfortunetly, it comes up with this error:
>TypeError: onButton() missing 2 required positional arguments: 'self' and 'EVT_BUTTON'
It is slightly more difficult. I am guessing you are a beginner to programming. If so, I suggest, you learn some more basics. Doing GUI applications is a bit more advanced topic.
So, firstly, for your wxPython program to run, you must start an event loop, so your program should have something like this:
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()
You defined your function onButton with 2 parameters. Therefore you must supply them when calling the function. The first one is instead of the self, and that is the frame. The second is named EVT_BUTTON (and giving the variable this name suggests that you actually do not understand these concepts, and that's the reason why I suggested that you start with studying basics).
So you could call
frame.OnButton(None)
before calling app.MainLoop() and the code will run. But that's probably not enough.

Dialog is not modal when ProgressDialog and Dialog are opened in sequence

I'm running wxPython 4.0.1 msw (phoenix) with Python 3.6.5 on a Windows7 machine, as well as wxPython 2.9.4 with Python 2.7.
I'm observing an issue with a modal dialog, which doesn't block the access to its parent window behind. This only occurs if I run a progress dialog followed by a modal dialog. This behavior is somehow related to custom dialogs. Integrated dialogs like wx.MessageDialog doesn't seem to have this issue.
To isolate the issue, I've written an example. The first two buttons open either the progress or the modal dialog and work properly. The third button opens both dialogs in sequence. In this case the modal functionality of the custom dialog doesn't work and I'm able to access and close the mainframe. Which causes multiple issues.
Dialog is not modal, the main window can be accessed and closed
import wx
class SomeDialog(wx.Dialog):
def __init__(self, parent):
wx.Dialog.__init__(self, parent, title='SomeDialog',
style=wx.DEFAULT_DIALOG_STYLE)
self.button_ok = wx.Button(self, wx.ID_OK, size=(120,-1))
hsizer = wx.BoxSizer(wx.HORIZONTAL)
hsizer.Add(self.button_ok, 0, wx.ALL|wx.ALIGN_CENTER, 10)
self.SetSizer(hsizer)
self.SetSize(self.BestSize)
self.Layout()
class TestFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, size=(400, 400))
self.button_progress = wx.Button(self, -1, 'Show Progress')
self.button_modal = wx.Button(self, -1, 'Show Modal')
self.button_both = wx.Button(self, -1, 'Show Both')
self.Bind(wx.EVT_BUTTON, self.on_button, self.button_progress)
self.Bind(wx.EVT_BUTTON, self.on_button, self.button_modal)
self.Bind(wx.EVT_BUTTON, self.on_button, self.button_both)
sizer = wx.BoxSizer()
sizer.Add(self.button_progress)
sizer.Add(self.button_modal)
sizer.Add(self.button_both)
self.SetSizer(sizer)
def on_button(self, event):
if event.EventObject is self.button_progress:
self._show_progress_dialog()
elif event.EventObject is self.button_modal:
self._show_modal_dialog()
else:
self._show_progress_dialog()
self._show_modal_dialog()
def _show_progress_dialog(self):
max = 10
dlg = wx.ProgressDialog('Progress dialog example', 'Some message',
maximum=max, parent=self,
style=wx.PD_APP_MODAL|wx.PD_AUTO_HIDE)
keepGoing = True
count = 0
while keepGoing and count < max:
count += 1
wx.MilliSleep(250)
wx.Yield()
(keepGoing, skip) = dlg.Update(count)
dlg.Destroy()
def _show_modal_dialog(self):
with SomeDialog(self) as dlg:
dlg.CenterOnParent()
dlg.ShowModal()
if __name__ == '__main__':
app = wx.App()
frame = TestFrame()
frame.Show()
app.MainLoop()
In case this is an issue in the wxpython framework and not an issue with my implementation, it would be great if someone could provide me a workaround to show such dialogs in sequence.
This looks like a bug to me. I'm not sure why its happening, but one workaround would be to use wx.CallLater
changing _show_modal_dialog to:
def _show_modal_dialog(self):
def _make_dialog():
with SomeDialog(self) as dlg:
dlg.CenterOnParent()
dlg.ShowModal()
wx.CallLater(50, _make_dialog) # 50 mils is arbitrary
Seems to resolve the issue of the dialog not acting modalish. The problem with this workaround is that it will be non-blocking, meaning that any code that has to wait for the dialog to return has to be moved into the dialog class or passed to the dialog as a callback.
In the meantime I found a workaround myself which I like to share.
I added a handler catching the windows close event.
class TestFrame(wx.Frame):
def __init__(self):
#...
self.Bind(wx.EVT_CLOSE, self.on_close)
This event function checks if some child dialog is open and modal and performs a veto.
def on_close(self, event):
# In case any modal dialog is open, prevent the frame from closing.
for children in (c for c in self.Children if isinstance(c, wx.Dialog)):
if children.IsModal():
event.Veto()
return
event.Skip()
This is also only a workaround, but I seems to work for my use cases.

wxPython event starts up at startup

So I am a complete beginner at python and usually code in C/C++ or Java. In the process of developing a GUI my events keep getting called at the start of the program running instead of when I click the button. (i know my indents are wrong because I needed to put it into a code block). How do I make my events only get called when I left click the button?
def __init__(self, parent, title):
super(QuadDash, self).__init__(parent, title=title, size=(1024, 780))
self.SetBackgroundColour("white")
pnl = wx.Panel(self)
cbtn = wx.Button(pnl, label='Start Quad', pos=(20,30))
self.Bind(wx.EVT_LEFT_DOWN, self.start_quad_event(), cbtn)
self.Show(True)
def start_quad_event(self):
dlg = wx.MessageDialog(self, "Test", "ABC", wx.YES_NO | wx.ICON_QUESTION)
dlg.ShowModal()
if __name__ == '__main__':
app = wx.App()
qd = QuadDash(None, title='QuadCopter Dashboard')
app.MainLoop()
The offending line is:
self.Bind(wx.EVT_LEFT_DOWN, self.start_quad_event(), cbtn)
You actually call start_quad_event() and pass the result to bind().
The correct line is:
self.Bind(wx.EVT_LEFT_DOWN, self.start_quad_event, cbtn)
Note: No parentheses. Here you actually pass the function to bind().

wx.Notebook tab only showing in cropped in top left corner rather than taking up the whole frame?

I'm trying to test out the wx.Notebook tabs in one of my guis. However, I've got a problem where only a tiny portion of the tab shows in the top left corner.
As you can see, only a very small part of it is showing. I can't figure out what wrong.
My code is as follows:
import wx
class MyFrame(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, "test", size=(640,480))
panel = wx.Panel(self)
notebook = wx.Notebook(panel, style=wx.BK_DEFAULT, size=(640,480))
tabOne = wx.Panel(notebook)
notebook.AddPage(tabOne, "Tab One")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(notebook, flag=wx.EXPAND)
self.SetSizer(sizer)
if __name__ == '__main__':
app = wx.PySimpleApp(redirect=False)
frame = MyFrame(parent=None, id=-1)
frame.Show()
app.MainLoop()
Anyone know what would cause such a thing?
You have to fix one line:
notebook = wx.Notebook(self, style=wx.BK_DEFAULT, size=(640,480))
Formerly you created the notebook as child of panel which hasn't any sizer set. Hence the small size. panel is now obsolete.

Categories

Resources