wxPython, making a window modal - python-3.x

The following code makes a window to behave like a wx.Dialog but it works perfectly only in Windows. In macOS the buttons in the main window are not disabled and in Linux the main window can be maximized and minimized.
How can I have in macOS and Linux the same modal behavior I have in Windows? I know I could disable the buttons manually and I guess there should be a way to handle the minimize and maximize buttons, but perhaps there is an easier way.
import wx
class MyFrame(wx.Frame):
def __init__(self):
title='Multiple consecutive windows (ShowModal)'
super().__init__(None, title=title)
self.WinNum = 5
#### Widgets
self.panel = wx.Panel(self)
self.buttonShow = wx.Button(self.panel, pos=(50, 50), label='ShowModal')
self.buttonTest = wx.Button(self.panel, pos=(50, 100), label='Test')
#### Bind
self.buttonShow.Bind(wx.EVT_BUTTON, self.ShowWindows)
#### Position of the window
self.SetPosition(pt=(50, 50))
def ShowWindows(self, event):
i = 0
while i < self.WinNum:
a = WinModal(i)
a.ShowModal()
i += 1
class WinModal(wx.Frame):
def __init__(self, ThisWinNum):
title = 'This is window number: ' + str(ThisWinNum)
super().__init__(None, title=title)
#### Variables
self.ThisWinNum = ThisWinNum
#### Widgets
self.panel = wx.Panel(self)
self.buttonOk = wx.Button(self.panel, pos=(50, 50), label='Ok')
#### Positions
self.SetPosition(pt=(200, 200))
#### Bind
self.Bind(wx.EVT_CLOSE, self.OnClose)
self.Bind(wx.EVT_BUTTON, self.onOk)
def ShowModal(self):
"""
This function is the one giving the wx.FileDialog behavior
"""
self._disabler = wx.WindowDisabler(self)
self.Show()
self.eventLoop = wx.GUIEventLoop()
self.eventLoop.Run()
def OnClose(self, event):
"""
To handle closing the windows because you need to exit the eventLoop
of the modal window.
"""
del self._disabler
self.eventLoop.Exit()
self.Destroy()
def onOk(self, event):
print(self.ThisWinNum)
self.cancel = False
self.OnClose(event)
if __name__ == '__main__':
app = wx.App()
frame = MyFrame()
frame.Show()
app.MainLoop()
else:
pass

Probably my question was not correctly formulated. Now I realize that what I wanted to know was how to create a custom dialog window.
This can be done by deriving the custom class from wx.Dialog instead of wx.Frame. The class wx.Dialog gives an empty space that you can fill with widgets just like a normal wx.Frame and also has the ShowModal() method in it. Therefore, you can show your window as a modal window.
import wx
class MainWin(wx.Frame):
def __init__(self):
super().__init__(None, title='My modal window')
####---- Variables
self.frameFvar = None
####---- Widgets
self.panel = wx.Panel(self)
self.buttonF = wx.Button(self.panel, label='Show Normal')
self.buttonM = wx.Button(self.panel, label='Show Modal')
####---- Sizer
self.sizer = wx.BoxSizer(wx.HORIZONTAL)
self.sizer.Add(self.buttonF, border=20, flag=wx.ALIGN_CENTER|wx.ALL)
self.sizer.Add(self.buttonM, border=20, flag=wx.ALIGN_CENTER|wx.ALL)
self.sizerM = wx.BoxSizer(wx.HORIZONTAL)
self.sizerM.Add(self.sizer, border=20,
flag=wx.EXPAND|wx.ALIGN_CENTER|wx.ALL)
self.panel.SetSizer(self.sizerM)
self.sizerM.Fit(self)
####---- Position
self.SetPosition(pt=(50, 50))
####---- Bind
self.buttonF.Bind(wx.EVT_BUTTON, self.ShowAsNormal)
self.buttonM.Bind(wx.EVT_BUTTON, self.ShowAsModal)
#---
def ShowAsNormal(self, event):
if self.frameFvar == None:
self.frameF = AsFrame(self)
self.frameF.Show()
self.frameFvar = True
else:
self.frameF.Raise()
#---
def ShowAsModal(self, event):
self.frameM = AsDialog(self)
if self.frameM.ShowModal() == wx.ID_OK:
print("Exited by Ok button")
else:
print("Exited by X button")
self.frameM.Destroy()
#---
#---
class AsFrame(wx.Frame):
def __init__(self, parent):
super().__init__(parent=parent, title='Shown as a wx.Frame')
####---- Variables
self.parent = parent
####---- Widgets
self.a = MyPanel(self)
####---- Position
self.SetPosition(pt=(50, 200))
####---- Bind
self.Bind(wx.EVT_CLOSE, self.OnClose)
#---
def OnClose(self, event):
self.parent.frameFvar = None
self.Destroy()
#---
#---
class AsDialog(wx.Dialog):
def __init__(self, parent):
super().__init__(parent=parent, title='Shown as a wx.Dialog')
####---- Variables
self.SetEscapeId(12345)
####---- Widgets
self.a = MyPanel(self)
self.buttonOk = wx.Button(self, wx.ID_OK)
####---- Sizers
self.sizerB = wx.StdDialogButtonSizer()
self.sizerB.AddButton(self.buttonOk)
self.sizerB.Realize()
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.a, border=10, flag=wx.EXPAND|wx.ALIGN_LEFT|wx.ALL)
self.sizer.Add(self.sizerB, border=10,
flag=wx.EXPAND|wx.ALIGN_RIGHT|wx.ALL)
self.SetSizer(self.sizer)
####---- Position
self.SetPosition(pt=(550, 200))
#---
#---
class MyPanel(wx.Panel):
def __init__(self, parent):
super().__init__(parent=parent)
####---- Variables
self.parent = parent
####---- Widgets
label = ("The same window shown as a wx.Frame or a wx.Dialog")
self.text = wx.StaticText(self, label=label, pos=(10, 10))
#---
#---
if __name__ == '__main__':
app = wx.App()
frameM = MainWin()
frameM.Show()
app.MainLoop()

Related

wxPython: Why does modeless, non modal dialog stay on top of parent window?

I want to open a dialog modeless with Show(). After opening I can click on the parent window so the dialog opened successfully modeless.
The problem I have is that the modeless dialog always stays on top of the parent window. I want the parent to get on top when I click on it.
It doesn't matter wether I give the dialog a parent window or not. I test for the wx.STAY_ON_TOP style which is False for the dialog.
With simple code below (copied and modified from https://www.tutorialspoint.com/wxpython/wx_dialog_class.htm) I'm having the same problem.
How can I get a modeless dialog that can get behind the parent window?
import wx
# -----------------------------------------------------------------------------------
class MyDialog(wx.Dialog):
def __init__(self, parent, title):
super(MyDialog, self).__init__(parent, title=title, size=(250, 150))
panel = wx.Panel(self)
self.btn = wx.Button(panel, wx.ID_OK, label="ok", size=(50, 20), pos=(75, 50))
style = self.GetWindowStyle()
if style & wx.STAY_ON_TOP:
print('STAY_ON_TOP = True')
else:
print('STAY_ON_TOP = False')
# -----------------------------------------------------------------------------------
class Mywin(wx.Frame):
def __init__(self, parent, title):
super(Mywin, self).__init__(parent, title=title, size=(300, 300))
self.InitUI()
def InitUI(self):
panel = wx.Panel(self)
btn1 = wx.Button(panel, label="Modal Dialog", pos=(20, 10))
btn2 = wx.Button(panel, label="Modeless Dialog Parent", pos=(20, 40))
btn3 = wx.Button(panel, label="Modeless Dialog Parentless", pos=(20, 70))
btn1.Bind(wx.EVT_BUTTON, self.OnModal)
btn2.Bind(wx.EVT_BUTTON, self.OnModelessParent)
btn3.Bind(wx.EVT_BUTTON, self.OnModelessParentless)
self.Show(True)
def OnModal(self, event):
MyDialog(self, "Dialog").ShowModal()
def OnModelessParent(self, event):
dlg = MyDialog(self, "Dialog").Show()
def OnModelessParentless(self, event):
dlg = MyDialog(None, "Dialog").Show()
# -----------------------------------------------------------------------------------
ex = wx.App()
Mywin(None, 'Modal / Modeless')
ex.MainLoop()
Adding the style wx.DIALOG_NO_PARENT to your dialog's constructor allows the dialogs to float behind the frame. Ex:
import wx
# -----------------------------------------------------------------------------------
class MyDialog(wx.Dialog):
def __init__(self, parent, title):
super(MyDialog, self).__init__(parent, title=title, size=(250, 150),
style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT)
panel = wx.Panel(self)
self.btn = wx.Button(panel, wx.ID_OK, label="ok", size=(50, 20), pos=(75, 50))
style = self.GetWindowStyle()
if style & wx.STAY_ON_TOP:
print('STAY_ON_TOP = True')
else:
print('STAY_ON_TOP = False')
# -----------------------------------------------------------------------------------
class Mywin(wx.Frame):
def __init__(self, parent, title):
super(Mywin, self).__init__(parent, title=title, size=(300, 300))
self.InitUI()
def InitUI(self):
panel = wx.Panel(self)
btn1 = wx.Button(panel, label="Modal Dialog", pos=(20, 10))
btn2 = wx.Button(panel, label="Modeless Dialog Parent", pos=(20, 40))
btn3 = wx.Button(panel, label="Modeless Dialog Parentless", pos=(20, 70))
btn1.Bind(wx.EVT_BUTTON, self.OnModal)
btn2.Bind(wx.EVT_BUTTON, self.OnModelessParent)
btn3.Bind(wx.EVT_BUTTON, self.OnModelessParentless)
self.Show(True)
def OnModal(self, event):
MyDialog(self, "Dialog").ShowModal()
def OnModelessParent(self, event):
MyDialog(self, "Dialog").Show()
def OnModelessParentless(self, event):
MyDialog(None, "Dialog").Show()
# -----------------------------------------------------------------------------------
ex = wx.App()
Mywin(None, 'Modal / Modeless')
ex.MainLoop()
I removed the variable assignment dlg = on your 2 modeless event handlers because Show() returns True not the dialog instance.
It should also be noted that dialogs aren't automatically destroyed and you must call Destroy() manually to release their memory when you're done with them.

How to Switch Between Panels whitch Buttons

I can not navigate between the panels of my App through the buttons. I followed a lot of tutorials like this one but none solve my problem.
Thank for Help.
####################-----Principal Panel (first Panel)
import wx
from OtherPanel import OtherPanel
class MyPanel(wx.Panel):
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent = parent, size=(600, 500))
self.SetBackgroundColour(wx.Colour(250, 250, 250))
parent.Center()
self.histoPanel = OtherPanel(self)
self.home()
self.panel = wx.Panel(self)
def home(self):
self.panel = wx.Panel(self)
self.panel.SetBackgroundColour(wx.Colour(250, 250, 250))
bS =wx.Button(self, -1,"Go some where ",(200,30), size=(150,150))
self.Bind(wx.EVT_BUTTON, self.Sy, bS)
bOthe = wx.Button(self, -1,"Go to OtherPanel",(400,30),(150,150))
self.Bind(wx.EVT_BUTTON, self.onOpenFrame,bOthe)
def onOpenFrame(self, event):
"""
Opens secondary frame
"""
self.Hide()
self.histoPanel.Show()
def Sy(self, event):
"""
I have another panel (panel 3)
"""
self.Hide()
#self.panel3.Show()
--Second Panel (another Panel)
this panel is displayed when you click on the button of the first panel
import wx
class OtherPanel(wx.Panel):
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent = parent)
self.panel = wx.Panel(self)
self.SetBackgroundColour(wx.Colour(250, 250, 250))
self.Center()
self.homeHist()
self.Show()
def homeHist(self):
bBack = wx.Button(self, -1, "", pos=(20,30), size=(50,50))
self.Bind(wx.EVT_BUTTON, self.onClose, bBack)
def onClose(self, event):
self.Close()
------My Frame---.
here is my Frame/Windows. he must wear all the panels in turn
import wx
from MyPanel import MyPanel
from OtherPanel import OtherPanel
class MyFrame(wx.Frame):
""""""
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, size = (600,500))
self.myPanel = MyPanel(self)
self.otherpanel = OtherPanel(self)
self.otherpanel.Hide()
self.menu()
self.CreateStatusBar(style= wx.BORDER_NONE)
self.SetBackgroundColour("gray")
self.SetStatusText("\tthank")
def menu(self):
menubar = wx.MenuBar()
fileMenu = wx.Menu()
menubar.SetBackgroundColour(wx.Colour(1, 1, 6))
fileMenu.AppendSeparator()
live = wx.MenuItem(fileMenu, wx.ID_EXIT, '&Quit\tCtrl+Q', '\tlive my app')
fileMenu.Append(live)
self.Bind(wx.EVT_MENU, self.Onexit, live)
menubar.Append(fileMenu, '\t&Menu')
self.SetMenuBar(menubar)
def Onexit(self, event):
self.Close(True)
def Bouton(self, event):
if self.myPanel.IsShown():
self.SetTitle("Panel Two Showing")
self.myPanel.Hide()
self.otherpanel.Show()
else:
self.SetTitle("Panel One Showing")
self.myPanel.Show()
self.otherpanel.Hide()
self.Layout()
if __name__ == "__main__":
app = wx.App(False)
frame = MyFrame()
frame.Show()
app.MainLoop()
In your MyPanel.__init__ you are creating an instance of OtherPanel assigned to self.histoPanel as a child of the MyPanel, and then MyPanel's event handlers are hiding itself, which in turn hides its children including the self.histoPanel. You're also creating some panels assigned to self.panel which don't seem to be used for anything.
The way you are doing it in the MyFrame class is much better. It creates both panels and hides one. Then in MyFrame.Bouton it hides one and shows the other, depending on the current state. So what you need to do is get rid of all the extra panels in MyPanel (self.histoPanel and also the various self.panels that are never used for anything) and then you just need to have the button event ask the parent frame to swap the panels instead of trying to do it itself.
Here is an example of what Robin is suggesting in his answer:
import wx
class MyPanel(wx.Panel):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.panel = wx.Panel(self)
self.btn = wx.Button(self.panel, label="Panel 1", size=(250,75))
self.btn.Bind(wx.EVT_BUTTON, self.switch)
vbox1 = wx.BoxSizer(wx.VERTICAL)
vbox1.Add(self.btn)
self.panel.SetSizer(vbox1)
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(self.panel)
self.SetSizer(vbox)
self.Show()
def switch(self, event):
self.parent.Swap()
class MyOtherPanel(wx.Panel):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.panel = wx.Panel(self)
self.btn = wx.Button(self.panel, label="Panel 2", size=(175,250))
self.btn.Bind(wx.EVT_BUTTON, self.switch)
vbox1 = wx.BoxSizer(wx.VERTICAL)
vbox1.Add(self.btn)
self.panel.SetSizer(vbox1)
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(self.panel)
self.SetSizer(vbox)
self.Show()
self.panel.Hide()
def switch(self, event):
self.parent.Swap()
class PanelSwitcher(wx.Frame):
def __init__(self):
super().__init__(None)
vbox = wx.BoxSizer(wx.VERTICAL)
self.panel1 = MyPanel(self)
self.panel2 = MyOtherPanel(self)
vbox.Add(self.panel1)
vbox.Add(self.panel2)
self.SetSizer(vbox)
self.Show()
def Swap(self):
if self.panel1.panel.IsShown():
self.panel1.panel.Hide()
self.panel2.panel.Show()
else:
self.panel2.panel.Hide()
self.panel1.panel.Show()
self.Layout()
if __name__ == "__main__":
app = wx.App()
PanelSwitcher()
app.MainLoop()

Open another wx.python frame cause the main UI to be blocked

I am trying to open another wx frame from a main frame UI. I followed the example shown here: https://wiki.wxpython.org/CallAfter but my main UI is still blocked.
Here is the event listener on the main UI:
def testShowGUI(self):
# This process is a long one
# It uses the vtk to read point cloud file and reconstruct the surface
file = "cache/d9c5e8ef-7b7f-485e-8fc8-23098c32afcb.ply"
reader = vtk.vtkPLYReader()
reader.SetFileName(file)
reader.Update()
delaunay = vtk.vtkDelaunay2D()
delaunay.SetAlpha(0.1)
delaunay.SetTolerance(0.0001)
delaunay.SetOffset(1.25)
delaunay.BoundingTriangulationOff()
delaunay.SetInputData(reader.GetOutput())
delaunay.Update()
#Once finish reading and processing the point cloud, pass to the next function for rendering
wx.CallAfter(self.AfterProcess, delaunay)
def AfterProcess(self, data):
meshVisGui = MesVisGUI.MeshVisGui(data)
meshVisGui.Show()
def OnEnter(self, event):
#Event listener when user click on Enter button
my_thread = threading.Thread(target=self.testShowGUI)
my_thread.start()
The code for the separate frame is as below:
class MeshVisGui(wx.Frame):
SPACING = 4
def __init__(self, delaunay, parent=None):
self.delaunayData = delaunay
self.title = "Mesh Visualization"
wx.Frame.__init__(self, None, -1, self.title)
self.Initialize()
def Initialize(self):
self.panel = wx.Panel(self, -1, size=(600, 400), style=wx.BORDER_RAISED)
self.widget3d = wxVTKRenderWindowInteractor(self.panel, -1)
self.widget3d.Enable()
self.render = vtk.vtkRenderer()
self.render.SetBackground(params.BackgroundColor)
self.widget3d.GetRenderWindow().AddRenderer(self.render)
self.interactor = self.widget3d.GetRenderWindow().GetInteractor()
self.interactor.GetInteractorStyle().SetCurrentStyleToTrackballCamera()
self.axesWidget = utils.createAxes(self.interactor)
self.meshActor = utils.build_actor(self.delaunayData)
self.render.AddActor(self.meshActor)
self.render.ResetCamera()
box = wx.BoxSizer(wx.VERTICAL)
box.Add(self.widget3d, 1, wx.EXPAND, self.SPACING)
self.panel.SetSizer(box)
self.Layout()
However, on my main UI, it is still showing a spinning icon and block the UI while it is trying to process the point cloud data. Can someone help me spot what I have done wrong?
Wxpython version: 4.0.1
Python version: 3.6.5
Following on from Mike Driscoll's answer and comment, here is a sample of a threaded task running from another panel.
The thread reports back to the 2nd panel (it's parent) using an event. This allows a progress bar to be updated.
The 2nd panel includes a "Cancel" option for the threaded task, whilst the main frame has a button to test if it is not frozen.
The use of wx.GetApp().Yield() may be slightly old fashioned but I have always found it to be reliable.
import time
import wx
from threading import Thread
import wx.lib.newevent
progress_event, EVT_PROGRESS_EVENT = wx.lib.newevent.NewEvent()
class ThreadFrame(wx.Frame):
def __init__(self, title, parent=None):
wx.Frame.__init__(self, parent=parent, title=title)
panel = wx.Panel(self)
self.btn = wx.Button(panel,label='Stop Long running process', size=(200,30), pos=(10,10))
self.btn.Bind(wx.EVT_BUTTON, self.OnExit)
self.progress = wx.Gauge(panel,size=(300,10), pos=(10,50), range=300)
#Bind to the progress event issued by the thread
self.Bind(EVT_PROGRESS_EVENT, self.OnProgress)
#Bind to Exit on frame close
self.Bind(wx.EVT_CLOSE, self.OnExit)
self.Show()
self.mythread = TestThread(self)
#Enable the GUI to be responsive by briefly returning control to the main App
while self.mythread.isAlive():
time.sleep(0.1)
wx.GetApp().Yield()
continue
try:
self.OnExit(None)
except:
pass
def OnProgress(self, event):
self.progress.SetValue(event.count)
#or for indeterminate progress
#self.progress.Pulse()
def OnExit(self, event):
if self.mythread.isAlive():
self.mythread.terminate() # Shutdown the thread
self.mythread.join() # Wait for it to finish
self.Destroy()
class TestThread(Thread):
def __init__(self,parent_target):
Thread.__init__(self)
self.parent = parent_target
self.stopthread = False
self.time = time.time()
self.start() # start the thread
def run(self):
# A loop that will run for 5 minutes then terminate
while self.stopthread == False:
curr_loop = int(time.time() - self.time)
if curr_loop < 300:
time.sleep(1)
evt = progress_event(count=curr_loop)
#Send back current count for the progress bar
try:
wx.PostEvent(self.parent, evt)
except: # The parent frame has probably been destroyed
self.terminate()
else:
self.terminate()
def terminate(self):
self.stopthread = True
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.text_count = 0
self.parent=parent
btn = wx.Button(self, wx.ID_ANY, label='Start Long running process', size=(200,30), pos=(10,10))
btn.Bind(wx.EVT_BUTTON, self.Thread_Frame)
btn2 = wx.Button(self, wx.ID_ANY, label='Is the GUI still active?', size=(200,30), pos=(10,50))
btn2.Bind(wx.EVT_BUTTON, self.AddText)
self.txt = wx.TextCtrl(self, wx.ID_ANY, style= wx.TE_MULTILINE, pos=(10,90),size=(400,100))
def Thread_Frame(self, event):
frame = ThreadFrame(title='Threaded Task', parent=self.parent)
def AddText(self,event):
self.text_count += 1
txt = self.txt.GetValue()
txt += "More text " + str(self.text_count)+"\n"
self.txt.SetValue(txt)
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Main Frame', size=(600,400))
panel = MyPanel(self)
self.Show()
if __name__ == '__main__':
app = wx.App(False)
frame = MainFrame()
app.MainLoop()
You do not need threads to open new frames / windows in your wxPython application. You just need to create a sub-class of wx.Frame to hold the code of your other frame. Then from your main application's frame, you can instantiate the other frame and show it. You use the same concept when you create a wx.Dialog or a wx.MessageDialog.
Here is a simple example:
import wx
class OtherFrame(wx.Frame):
"""
Class used for creating frames other than the main one
"""
def __init__(self, title, parent=None):
wx.Frame.__init__(self, parent=parent, title=title)
self.Show()
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
btn = wx.Button(self, label='Create New Frame')
btn.Bind(wx.EVT_BUTTON, self.on_new_frame)
def on_new_frame(self, event):
frame = OtherFrame(title='SubFrame',
parent=wx.GetTopLevelParent(self))
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Main Frame')
panel = MyPanel(self)
self.Show()
if __name__ == '__main__':
app = wx.App(False)
frame = MainFrame()
app.MainLoop()
In this example, I set the other frame's parent to the MainFrame instance by using wx.GetTopLevelParent(self). The benefit of setting a parent for the sub-frames is that if I close the main frame, it will cause the other frames to also be closed.

How to refresh the main frame when the child dialog do someting

I want the main frame refresh to update the label When I click the "done" button on MyDialog, But now it doesn't work.
Does there anything wrong? Thanks.
This is the code:
MyDialog: the child dialog where there is a button on it to update the label of the main frame
MainFrame: the main Frame, there is a button on it to start my dialog
# -*- coding: utf-8 -*-
import wx
#Dialog
class MyDialog(wx.Dialog):
"""setting MyDialog."""
def __init__(self):
self.dlg_main = wx.Dialog.__init__(self, None, -1, title="setting", size=(300, 300))
self.btn_ok = wx.Button(self, label="done", pos=(30, 30), size=(50, 26))
self.Bind(wx.EVT_BUTTON, self.__OnButtonClick_save, self.btn_ok,)
def __OnButtonClick_save(self, event):
self.Destroy()
main = MainFrame()
**main.set_label_name('test')**
main.Destroy()
def start_dialog():
my_dialog = MyDialog()
my_dialog.ShowModal()
my_dialog.Destroy()
#Main Frame
class MainFrame(wx.Frame):
def __init__(self):
self.main_frame = wx.Frame.__init__(self, None, -1, title='simple', size=(400, 400))
self.Centre()
self.label_name = wx.StaticText(self, label="Hello,everyone", pos=(30, 30))
self.btn_set = wx.Button(self, label="set", pos=(30, 60))
self.Bind(wx.EVT_BUTTON, self.on_button_click, self.btn_set)
def set_label_name(self, str):
print(str)
self.label_name.SetLabel('hello, Boys')
def on_button_click(self, event):
start_dialog()
def show_main():
main = wx.App()
main_win = MainFrame()
main_win.Show()
main.MainLoop()
if __name__ == '__main__':
show_main()
I have got the method, thanks all!
The point is how to call the parent method, so , use self.parent.xxx to fix the problem.
The code like this:
# -*- coding: utf-8 -*-
import wx
#Dialog
class MyDialog(wx.Dialog):
"""setting MyDialog."""
def __init__(self, parent, title):
super(MyDialog, self).__init__(parent, title=title, size=(300, 300))
self.parent = parent
panel = wx.Panel(self)
btn_ok = wx.Button(panel, label="done", pos=(30, 30), size=(50, 26))
btn_ok.Bind(wx.EVT_BUTTON, self.__OnButtonClick_save)
def __OnButtonClick_save(self, event):
#THis is the different
self.parent.set_label_name('test')
self.Destroy()
def start_dialog():
my_dialog = MyDialog()
my_dialog.ShowModal()
my_dialog.Destroy()
#Main Frame
class MainFrame(wx.Frame):
def __init__(self):
self.main_frame = wx.Frame.__init__(self, None, -1, title='simple', size=(400, 400))
self.init_ui()
def init_ui(self):
self.label_name = wx.StaticText(self, label="Hello,everyone", pos=(30, 30))
btn_set = wx.Button(self, label="set", pos=(30, 60))
btn_set.Bind(wx.EVT_BUTTON, self.on_button_click)
self.Centre()
def set_label_name(self, str):
self.label_name.SetLabel('hello, Boys')
def on_button_click(self, event):
my_dialog = MyDialog(self, "setting")
my_dialog.ShowModal()
my_dialog.Destroy()
def show_main():
main = wx.App()
main_win = MainFrame()
main_win.Show()
main.MainLoop()
if __name__ == '__main__':
show_main()
Thanks for the idea in this answer Wxpython show dialog on main frame startup

PYQT how to draw line between two buttons

from PyQt4 import QtGui, QtCore
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.view = View(self)
self.button = QtGui.QPushButton('Clear View', self)
self.button.clicked.connect(self.handleClearView)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.view)
layout.addWidget(self.button)
def handleClearView(self):
self.view.scene().clear()
class DragButton(QtGui.QPushButton):
def mousePressEvent(self, event):
self.__mousePressPos = None
self.__mouseMovePos = None
if event.button() == QtCore.Qt.LeftButton:
self.__mousePressPos = event.globalPos()
self.__mouseMovePos = event.globalPos()
#super(DragButton, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if event.buttons() == QtCore.Qt.LeftButton:
# adjust offset from clicked point to origin of widget
currPos = self.mapToGlobal(self.pos())
globalPos = event.globalPos()
diff = globalPos - self.__mouseMovePos
newPos = self.mapFromGlobal(currPos + diff)
self.move(newPos)
self.__mouseMovePos = globalPos
#super(DragButton, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
if self.__mousePressPos is not None:
moved = event.globalPos() - self.__mousePressPos
if moved.manhattanLength() > 3:
event.ignore()
return
#super(DragButton, self).mouseReleaseEvent(event)
class View(QtGui.QGraphicsView):
def __init__(self, parent):
QtGui.QGraphicsView.__init__(self, parent)
self.setScene(QtGui.QGraphicsScene(self))
self.setSceneRect(QtCore.QRectF(self.viewport().rect()))
btn1=DragButton('Test1', self)
btn2=DragButton('Test2', self)
def mousePressEvent(self, event):
self._start = event.pos()
def mouseReleaseEvent(self, event):
start = QtCore.QPointF(self.mapToScene(self._start))
end = QtCore.QPointF(self.mapToScene(event.pos()))
self.scene().addItem(
QtGui.QGraphicsLineItem(QtCore.QLineF(start, end)))
for point in (start, end):
text = self.scene().addSimpleText(
'(%d, %d)' % (point.x(), point.y()))
text.setBrush(QtCore.Qt.red)
text.setPos(point)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.resize(640, 480)
window.show()
sys.exit(app.exec_())
Here is my code. There are two movable buttons on the QGraphicsView and I can draw line on the QGraphicsView with mouse dragging. But what I want to do is to draw line between two buttons. For detail, If I right click the btn1(Test1) and then right click the btn2(Test2) , the line would be created between two buttons. I'm struggling this problem for a month. Plz Help!
I am assuming that the line you need to draw between the buttons must also be movable. If not it is just simple you can just use :
lines = QtGui.QPainter()
lines.setPen(self)
lines.drawLine(x1,y1,x2,y2)
So, if the line needs to be movable along with the buttons then first you create a mini widget consisting of Two Buttons and the Line, so you can move the whole widget. This might help!, in that case.

Resources