Close window A by clicking a button on window B - python-2.7

I have window A with some graphs and a button "NEXT". When the user presses the button, window B opens with some questions about the graphs on window A. It also has a button "Done". Once "Done" is pressed, window A, and B close.
Problem: I don't get window A to close and no error messages in the console.
Window A:
class Window_A(QDialog):
def __init__(self, parent = None):
super(Window_A, self).__init__(parent,flags=Qt.WindowMinimizeButtonHint|Qt.WindowCloseButtonHint)
self.setWindowTitle("Window A")
self.initUI()
def initUI(self):
# Load the images
[...]
#"NEXT button"
self.b_next = QPushButton("NEXT")
self.b_next.clicked.connect(self.open_next)
#Layout
[...]
def close_win(self):
self.close()
def open_next(self):
self.next= Window_B()
self.next.show()
Window B:
class Window_B(QDialog):
def __init__(self):
super(Window_B, self).__init__()
self.setWindowTitle("Window_B")
self.initUI()
def initUI(self):
#define text labels
[...]
#done button
self.d_button = QPushButton("Done")
self.d_button.clicked.connect(self.on_click)
self.d_button.clicked.connect(lambda:self.close())
#layout for text (questions)
[...]
def on_click(self):
Window_A().close_win()

In your window_B code, you do not know the reference to the correct window_A object.
So your line:
def on_click(self):
Window_A().close_win()
creates a new Window_A object, calls its close function immediately, and this objects is garbage collected asap.
You will need to pass a reference to A in the B object.
For instance:
in window_A.py
def open_next(self):
self.next= Window_B()
self.next.show()
self.next.ref_to_close_father = self
in window_B.py
def on_click(self):
self.ref_to_close_father.close_win()
This should be better (but your example is not reproducible, so there might be another problem).
By the way,
self.d_button.clicked.connect(lambda:self.close())
could simply be
self.d_button.clicked.connect(self.close)

Related

How to disable OnClick() event when user clicks on winform created by Ironpython?

I'm writing an Ironpython 2.7.5 code to display Winform and take inputs from the user. Whenever i accidentally clicks on form, it throws error "OnClick() takes exactly 3 arguments (2 given)". How shall i disable this event?
I've used Notepad++ to write my code and running it in ANSYS tool. Below is the structure of my code:
def init(context):
#Something here
def OpenForm1(analysis_obj):
form = SimpleTextBoxForm()
Application.Run(form)
class SimpleTextBoxForm(Form):
def __init__(self):
self.Text = 'Material Tool V1.0'
self.Size = Size(Width, Height)
self.MaximizeBox = False
self.MinimizeBox = False
self.FormBorderStyle = FormBorderStyle.FixedDialog
#Some other controls
def OnChanged(self, sender, event): #Triggered on change
#Some Code
def OnClick(self, sender, event): #Triggered on button clicks
try:
#Something here
except:
#Something here
I want to stop this event from triggering wherever i click on form. But i am unable to.
I figured it out. I was using conflicting name in the click event
def OnClick()
which i changed to some other name and it worked fine.
If anyone runs out in similar problems, check the user defined method names which may be same as inbuilt ones.

Weird tab traversal behavior in wxPython custom control

If you make a custom control as a subclass of wx.PyControl, the tab traversal will behave oddly. For example, with the code below, pressing tab a few times will get you stuck inside MyControl. Once you tab to a "Child of MyControl" textbox, you can only tab between the 2 "Child of MyControl" textboxes, and will never tab back to "Child of Panel".
class MyFrame(wx.Frame):
def __init__(self):
super(MyFrame, self).__init__(None)
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(wx.TextCtrl(panel, value="Child of Panel"), flag=wx.EXPAND)
sizer.AddSpacer(30)
sizer.Add(MyControl(panel), flag=wx.EXPAND)
panel.SetSizer(sizer)
class MyControl(wx.PyControl):
def __init__(self, parent):
super(MyControl, self).__init__(parent, style=wx.BORDER_NONE|wx.TAB_TRAVERSAL)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(wx.TextCtrl(self, value="Child of MyControl"), flag=wx.EXPAND)
sizer.AddSpacer(10)
sizer.Add(wx.TextCtrl(self, value="Child of MyControl"), flag=wx.EXPAND)
self.SetSizer(sizer)
Fixing the tab traversal to behave in a standard way is surprisingly simple. All you have to do is make your custom control subclass wx.Panel instead of wx.PyControl.
class MyControl(wx.Panel):
def __init__(self, parent):
super(MyControl, self).__init__(parent)
...

Multiprocessing in wxpython

I have a GUI with buttons 'OK' and 'Cancel' clicking on which creates a child threads which pops up a dialog box saying 'OK/Cancel button is pressed'. Now when i click on OK button, i want child thread calling it must wait for another process(may be another dialogbox saying wait) and then must pop up message 'OK button is pressed'.
I used wx.timer for childthread to wait but could not get it worked.
How to make a child thread pause and continue(like an interrupt) when the process is done?
Below find my trials!
import wx, time
from threading import Thread
ID_RUN = 101
ID_RUN2 = 102
class ChildThread_OK(Thread):
def __init__(self, myframe):
"""Init Worker Thread Class."""
Thread.__init__(self)
self.myframe = myframe
self._want_abort = True
def run(self):
if self._want_abort is True:
self.waitevent()
wx.CallAfter(self.myframe.AfterRun, 'Ok button pressed')
def waitevent(self):
wx.CallAfter(self.myframe.message,"Oops!!there is another process running wait for it to finish. Closing this dialog box in 2s...")
def closeit(self, event):
self.dialogBox.Destroy()
class ChildThread_Cancel(Thread):
def __init__(self, myframe):
Thread.__init__(self)
self.myframe = myframe
def run(self):
wx.CallAfter(self.myframe.AfterRun, "Cancel button pressed")
class MyFrame(wx.Frame):
def __init__(self, parent, ID, title):
wx.Frame.__init__(self, parent, ID, title)
panel = wx.Panel(self, -1)
mainSizer = wx.BoxSizer(wx.HORIZONTAL)
mainSizer.Add(wx.Button(panel, ID_RUN, "OK"))
mainSizer.Add(wx.Button(panel, ID_RUN2, "Cancel"))
panel.SetSizer(mainSizer)
mainSizer.Fit(self)
wx.EVT_BUTTON(self, ID_RUN, self.onRun)
wx.EVT_BUTTON(self, ID_RUN2, self.onRun2)
def onRun(self, event):
self.child = ChildThread_OK(myframe=self)
self.child.daemon = True
self.child.start()
def onRun2(self, event):
self.child2 = ChildThread_Cancel(myframe = self)
self.child2.daemon = True
self.child2.start()
def AfterRun(self, msg):
dlg = wx.MessageDialog(self, msg, "Message", wx.OK | wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
def message(self,msg):
dlg = wx.BusyInfo(msg)
time.sleep(5)
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, -1, "My GUI")
frame.Show(True)
frame.Centre()
return True
app = MyApp(0)
app.MainLoop()
First: You cannot use timer or message box in a child thread, you can only use them in main thread. So the methods below are not meaningful.
def waitevent(self):
self.timer = wx.Timer(self, 1)
self.Bind(wx.EVT_TIMER, self.closeit, self.timer)
self.timer.Start(2000, wx.TIMER_ONE_SHOT)
wx.CallAfter(self.AfterRun, 'Ok button pressed')
print 'Oops!!there is another process running wait for it to finish. Closing this dialog box in 2s...'
def closeit(self, event):
self.dialogBox.Destroy()
Second: If you use message boxes to display something, main thread is suspended and your code after message box is run after message box is closed. You can use wx.StaticText or wx.TextCtrl on main panel to show the messages or you can create new frames mimicing message boxes and they dont suspend the program and can be easily destroyed OR you can make a custom message dialog waiting for a variable to change (e.g. True/False). There is a custom message dialog example in wxPython demo.
Third: You should learn the events and queues in python's multithreading module to help you control the child threads. Child thread can sleep until an event is set or can wait until there is something in a queue.

Exit button doesn´t work - wxpython

I developed a GUI with wxGlade and its still working. But to start the GUI - I coded a script with some choices.
So everything works but when I push the red button with the "x" to close the window - the application doesn´t stop.
I made a method, called by a separate exit button, which calls an exit-function in my script. But normally the users are using the close button (red button with X) so my method is not being used to close the window and the window doesn't get closed ultimately.
This is the exit-function.
def stopExport(self, event): # wxGlade: MyFrame.<event_handler>
self.Close() # close the Frame
from ExportManager import Exportmanager # import the exit function
Exportmanager().exit() # call it
How can I use this function with the red button with the "x"?
As per my understanding of your question, your application is not closing when you click on the close button (The red button with X on the right top corner.)
By default when you click the close button your application should close. In your case it seems to me that you have bind the EVT_CLOSE to some method, which has no code in it to close the app window.
For eg. consider the code snippet below, I have intentionally bind the EVT_CLOSE event to a method named as closeWindow(). This method does nothing that is why I have the pass keyword there. Now if you execute the code snippet below you can see that the app window won't close.
Code:
import wx
class GUI(wx.Frame):
def __init__(self, parent, id, title):
screenWidth = 500
screenHeight = 400
screenSize = (screenWidth,screenHeight)
wx.Frame.__init__(self, None, id, title, size=screenSize)
self.Bind(wx.EVT_CLOSE, self.closeWindow) #Bind the EVT_CLOSE event to closeWindow()
def closeWindow(self, event):
pass #This won't let the app to close
if __name__=='__main__':
app = wx.App(False)
frame = GUI(parent=None, id=-1, title="Problem Demo-PSS")
frame.Show()
app.MainLoop()
So, in order to close the app window you need to change the closeWindow(). For eg: Following code snippet will use the Destroy() close the app window when you click on the close button.
import wx
class GUI(wx.Frame):
def __init__(self, parent, id, title):
screenWidth = 500
screenHeight = 400
screenSize = (screenWidth,screenHeight)
wx.Frame.__init__(self, None, id, title, size=screenSize)
self.Bind(wx.EVT_CLOSE, self.closeWindow) #Bind the EVT_CLOSE event to closeWindow()
def closeWindow(self, event):
self.Destroy() #This will close the app window.
if __name__=='__main__':
app = wx.App(False)
frame = GUI(parent=None, id=-1, title="Problem Demo-PSS")
frame.Show()
app.MainLoop()
I hope it was useful.

How to make a wxFrame behave like a modal wxDialog object

Is is possible to make a wxFrame object behave like a modal dialog box in that the window creating the wxFrame object stops execution until the wxFrame object exits?
I'm working on a small game and have run into the following problem. I have a main program window that hosts the main application (strategic portion). Occasionally, I need to transfer control to a second window for resolution of part of the game (tactical portion). While in the second window, I want the processing in the first window to stop and wait for completion of the work being done in the second window.
Normally a modal dialog would do the trick but I want the new window to have some functionality that I can't seem to get with a wxDialog, namely a status bar at the bottom and the ability to resize/maximize/minimize the window (this should be possible but doesn't work, see this question How to get the minimize and maximize buttons to appear on a wxDialog object).
As an addition note, I want the second window's functionality needs to stay completely decoupled from the primary window as it will be spun off into a separate program eventually.
Has anyone done this or have any suggestions?
I was also looking from similar solution and have comeup with this solution, create a frame, disable other windows by doing frame.MakeModal() and to stop execution start and event loop after showing frame, and when frame is closed exit the event loop e.g. I here is sample using wxpython but it should be similar in wxwidgets.
import wx
class ModalFrame(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, style=wx.DEFAULT_FRAME_STYLE|wx.STAY_ON_TOP)
btn = wx.Button(self, label="Close me")
btn.Bind(wx.EVT_BUTTON, self.onClose)
self.Bind(wx.EVT_CLOSE, self.onClose) # (Allows main window close to work)
def onClose(self, event):
self.MakeModal(False) # (Re-enables parent window)
self.eventLoop.Exit()
self.Destroy() # (Closes window without recursion errors)
def ShowModal(self):
self.MakeModal(True) # (Explicit call to MakeModal)
self.Show()
# now to stop execution start a event loop
self.eventLoop = wx.EventLoop()
self.eventLoop.Run()
app = wx.PySimpleApp()
frame = wx.Frame(None, title="Test Modal Frame")
btn = wx.Button(frame, label="Open modal frame")
def onclick(event):
modalFrame = ModalFrame(frame, "Modal Frame")
modalFrame.ShowModal()
print "i will get printed after modal close"
btn.Bind(wx.EVT_BUTTON, onclick)
frame.Show()
app.SetTopWindow(frame)
app.MainLoop()
It does not really make sense to "stop execution" of a window, as the window only handles events that are sent to it, like for example mouse, keyboard or paint events, and ignoring them would make the program appear hung. What you should do is disable all controls in your frame, this will gray them out and make the user aware of the fact that they can not be interacted with at this moment.
You can also disable the parent frame completely, instead of disabling all controls on it. Look into the wxWindowDisabler class, the constructor has a parameter to indicate a window that can be interacted with, and all other windows of the application will be disabled.
If you later on want to execute a secondary program, then you could use the wxExecute() function to do it.
This took me an annoying amount of time to figure out but here is a working example that grew out of Anurag's example:
import wx
class ChildFrame(wx.Frame):
''' ChildFrame launched from MainFrame '''
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, -1,
title=self.__class__.__name__,
size=(300,150))
panel = wx.Panel(self, -1)
closeButton = wx.Button(panel, label="Close Me")
self.Bind(wx.EVT_BUTTON, self.__onClose, id=closeButton.GetId())
self.Bind(wx.EVT_CLOSE, self.__onClose) # (Allows frame's title-bar close to work)
self.CenterOnParent()
self.GetParent().Enable(False)
self.Show(True)
self.__eventLoop = wx.EventLoop()
self.__eventLoop.Run()
def __onClose(self, event):
self.GetParent().Enable(True)
self.__eventLoop.Exit()
self.Destroy()
class MainFrame(wx.Frame):
''' Launches ChildFrame when button is clicked. '''
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id,
title=self.__class__.__name__,
size=(400, 300))
panel = wx.Panel(self, -1)
launchButton = wx.Button(panel, label="launch modal window")
self.Bind(wx.EVT_BUTTON, self.__onClick, id=launchButton.GetId())
self.Centre()
self.Show(True)
def __onClick(self, event):
dialog = ChildFrame(self, -1)
print "I am printed by MainFrame and get printed after ChildFrame is closed"
if __name__ == '__main__':
app = wx.App()
frame = MainFrame(None, -1)
frame.Show()
app.MainLoop()
Not sure this is a great answer but it worked.
bool WinApp1::OnInit()
{
if (!wxApp::OnInit())
return false;
SettingsDialog dialog(m_settingsData);
dialog.ShowModal();
return false;
}
SettingsDialog::SettingsDialog(SettingsData& settingsData)
: m_settingsData(settingsData)
{
SetExtraStyle(wxDIALOG_EX_CONTEXTHELP);
wxWindow* parent = nullptr;
Create(parent, wxID_ANY, "Preferences", wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
The WinApp1 window is never given a wxFrame and never paints.