I am using wxPython and want my HtmlWindow to scroll down automatically after adding new content. I am using it as a log window inside my app. Unfortunately, I am struggling to get it working. Here is my sample with lacks the functionality:
import wx
import wx.html
class GUI(wx.Frame):
def __init__(self, parent):
super().__init__(parent)
self.html = wx.html.HtmlWindow(self, -1, pos=(0, 0), size=(50, 50))
msg = '<pre>FOO</pre>'
for i in range(10):
self.html.AppendToPage(msg)
if __name__ == '__main__':
app = wx.App()
frame = GUI(parent=None)
frame.Show()
app.MainLoop()
I want the scrollbar showing the stack of "Foos" to be at the bottom instead of staying on top so that the latest logging content is shown to the user.
Arguably wx.html.HtmlWindow is the wrong tool to use.
You'd have to insert Anchors and then leap to each Anchor.
For a log, it's better to use a wx.TextCtrl e.g.
import wx
import time
class GUI(wx.Frame):
def __init__(self, parent):
super().__init__(parent)
self.log = wx.TextCtrl(self, wx.ID_ANY, size=(600, 480),
style=wx.TE_MULTILINE| wx.TE_READONLY| wx.VSCROLL)
# set initial position at the start
self.log.SetInsertionPoint(0)
self.Show()
msg = 'FOO\n'
for i in range(50):
self.log.write(msg)
wx.GetApp().Yield()
time.sleep(0.5)
if __name__ == '__main__':
app = wx.App()
frame = GUI(parent=None)
app.MainLoop()
Related
I'm trying to create an App which allows the user to switch the information show using buttons. The basic idea of the code is that the user sees buttons on the left side of the screen and when the user presses "button 1", the code shows Panel1. I've made 2 buttons so far and the code for 2 panels is written as well but i can't figure out how to update my MainFrame so it show a different panel when one of the buttons is pressed.
Code:
import wx
TabNumber = 1
class ButtonPanel(wx.Panel):
def __init__(self, parent):
global TabNumber
super(ButtonPanel, self).__init__(parent, -1)
self.Tab1Button = wx.Button(self, label="TAB 1")
self.Tab1Button.Bind(wx.EVT_BUTTON, self.SwitchTab(1))
self.Tab2Button = wx.Button(self, label="TAB 2")
self.Tab2Button.Bind(wx.EVT_BUTTON, self.SwitchTab(2))
self.Sizer = wx.BoxSizer(wx.VERTICAL)
self.Sizer.Add(self.Tab1Button, wx.CENTER,0)
self.Sizer.Add(self.Tab2Button, wx.CENTER, 0)
self.SetSizer(self.Sizer)
def SwitchTab(self, tab):
def OnClick(event):
print(f"Switch to tab {tab} started")
TabNumber = tab
print(TabNumber)
return OnClick
class Panel1(wx.Panel):
def __init__(self, parent):
super(Panel1, self).__init__(parent, -1)
self.panel = wx.Panel(self)
self.text = wx.StaticText(self.panel, label="1")
class Panel2(wx.Panel):
def __init__(self, parent):
super(Panel2, self).__init__(parent, -1)
self.panel = wx.Panel(self)
self.text = wx.StaticText(self.panel, label="2")
class MainFrame(wx.Frame):
def __init__(self):
super(MainFrame, self).__init__(None, -1, "Test Application")
self.Panels = {
"Panel1": Panel1(self),
"Panel2": Panel2(self)
}
self.MySizer = wx.BoxSizer(wx.HORIZONTAL)
self.tabpanel = ButtonPanel(self)
self.MySizer.Add(self.tabpanel,wx.CENTER,0)
self.InfoPanel = self.Panels["Panel"+str(TabNumber)]
self.MySizer.Add(self.InfoPanel, wx.CENTER,0)
self.SetSizer(self.MySizer)
if __name__ == "__main__":
app = wx.App(False)
frame = MainFrame()
frame.Show()
app.MainLoop()
I was also wondering how I can adjust the ratio for the space that is given to my ButtonPanel and my InfoPanel.
As far as I can see, you are trying to do something that works like a Wizard... On the one hand, you can use wx.adv.Wizard. On the other hand, you can look at this tutorial that does something very similar and adapt it to what you need:
WXPython: How to create a generic wizard
Good luck!
I have a big doubt because my script is not performing as it should be. So I have a simple main file with a button that opens a blank grid (codes below). The problem with this code is that it opens reportWindow the first time it executes, but if I close the report and I try to open it again I receive the error :
NameError: name 'TestFrame' is not defined
I've also removed if __name__ == '__main__' from the last lines of reportWindow.py because the script wasn't working with it. I tried if __name__ == 'main' as it's imported from main.py but it didn't work either.
Please, can someone show me how it should have been done the correct way?
Thank you
main.py
import wx
class Test(wx.Frame):
def __init__(self,parent,id):
wx.Frame.__init__(self,parent,id, "Frame aka Window", size=(300, 200))
panel = wx.Panel(self)
button = wx.Button(panel, label = "Exit", pos=(80, 80), size = (120,30))
self.Bind(wx.EVT_BUTTON, self.closebutton, button)
def closebutton(self,event):
from reportWindow import SimpleGrid
SimpleGrid(TestFrame, -1)
if __name__ == '__main__':
app = wx.App()
frame = Test(parent=None, id=1)
frame.Show()
app.MainLoop()
reportWindow.py
import wx
import wx.grid as gridlib
class SimpleGrid(gridlib.Grid): ##, mixins.GridAutoEditMixin):
def __init__(self, parent, log):
gridlib.Grid.__init__(self, parent, -1)
#[...Some code...]
class TestFrame(wx.Frame):
def __init__(self, parent, log):
wx.Frame.__init__(self, parent, 0, "Title", size=(1400,800))
self.grid = SimpleGrid(self, log)
#[...Some code...]
#if __name__ == '__main__':
import sys
from wx.lib.mixins.inspection import InspectableApp
app = InspectableApp(False)
frame = TestFrame(None, sys.stdout)
frame.Show(True)
#import wx.lib.inspection
#wx.lib.inspection.InspectionTool().Show()
app.MainLoop()
Your code has a few of issues.
1) The NameError is occurring because you're not importing TestFrame from reportWindow.
2) Without if __name__ == "__main__" in reportWindow.py your program will create another wx.App and start another MainLoop which will block the first loop from receiving events etc... You should only create 1 App/MainLoop. It will also create the TestFrame the first time you import reportWindow.
3) It looks like you want SimpleGrid to be a child of TestFrame, but then you try to create it by itself in closebutton.
4) When creating the SimpleGrid you pass the TestFrame class instead of an instance of the TestFrame class.
Here is the modified code
# main.py
import wx, sys
class Test(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, "Frame aka Window", size=(300, 200))
panel = wx.Panel(self)
button = wx.Button(panel, label="Exit", pos=(80, 80), size=(120, 30))
self.Bind(wx.EVT_BUTTON, self.closebutton, button)
def closebutton(self, event):
from reportWindow import TestFrame
frame = TestFrame(self, sys.stdout)
frame.Show()
if __name__ == '__main__':
app = wx.App()
frame = Test(parent=None, id=1)
frame.Show()
app.MainLoop()
# reportWindow.py
import wx
import wx.grid as gridlib
class SimpleGrid(gridlib.Grid): ##, mixins.GridAutoEditMixin):
def __init__(self, parent, log):
gridlib.Grid.__init__(self, parent, -1)
# [...Some code...]
class TestFrame(wx.Frame):
def __init__(self, parent, log):
wx.Frame.__init__(self, parent, 0, "Title", size=(1400, 800))
self.grid = SimpleGrid(self, log)
# [...Some code...]
if __name__ == '__main__':
import sys
from wx.lib.mixins.inspection import InspectableApp
app = InspectableApp(False)
frame = TestFrame(None, sys.stdout)
frame.Show(True)
import wx.lib.inspection
wx.lib.inspection.InspectionTool().Show()
app.MainLoop()
I'am a new bie in python,I have to call a frame "Frame2" when I clic on a button from Frame1,I have this error:
this I my code:
global Frame2 fr
def OnButton4Button(self, event):
fr.Show()
even.Skip()
NB:I work with wxpython,and boa constructor
thanks fro help
You have several typos in your code. Here's a corrected example:
from Frame2 import fr
def OnButton4Button(self, event):
fr.Show()
event.Skip() # you need to spell event correctly
This assumes that Frame2 is a module. Most of the time, you don't need to use globals in Python.
To make this a bit easier to follow, I wrote an example that has a MainFrame class and a Frame2 class in the same module so you don't have to import anything or use globals:
import wx
########################################################################
class Frame2(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Frame2")
panel = wx.Panel(self)
########################################################################
class MainFrame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Main Frame")
panel = wx.Panel(self)
button = wx.Button(panel, label="Open Frame2")
button.Bind(wx.EVT_BUTTON, self.onButton)
self.Show()
#----------------------------------------------------------------------
def onButton(self, event):
""""""
frame = Frame2()
frame.Show()
event.Skip()
if __name__ == "__main__":
app = wx.App(False)
frame = MainFrame()
app.MainLoop()
In your short code you have an indentation on the second line, this is an error, you must write it like:
from Frame2 import fr
def OnButton4Button(self, event):
fr.Show()
event.Skip()
You may respect the indentation in Python like following example:
global var
def function():
#indented block
#should be always on the same column
condition = 1
if condition:
#new indented block
#is also aligned on a column
print "something"
#this is out of the IF block
#call the function
function()
In the PEP8 recommendations you will find the rules to avoid indenting errors.
i have two py files that each have its own frame made using wxPython, MainWindow and RecWindow. MainWindow has the RecWindow python file included using the keyword "recovery".
MainWindow code:
class MainWindow(wx.Frame):
def __init__(self,parent,id,title):
wx.Frame.__init__(self, parent, wx.ID_ANY,title,pos=(500,200), size = (650,500), style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
self.Bind(wx.EVT_CLOSE,self.OnExit)
self.SetIcon(wx.Icon('etc\icons\download.ico', wx.BITMAP_TYPE_ICO))
panel = wx.Panel(self)
RecWindow code:
class RecWindow(wx.Frame):
def __init__(self,parent,id,title):
wx.Frame.__init__(self, parent, wx.ID_ANY,title,pos=(400,200), size = (700,600), style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
self.SetIcon(wx.Icon('etc\icons\download.ico', wx.BITMAP_TYPE_ICO))
self.count = 0
when i click on a button in MainWindow , it will hide the MainWindow create an instance of RecWindow, shown below;
def OpenRec(self,event):#this will be used to open the next frame
OR = recovery(None,-1,"RAVE")
OR.Show(True)
MainWindow.Hide()
now, what i am unsure of is how i can return to the MainWindow once i close the RecWindow. RecWindow has a cancel and finish button which both map to a self.close() function. How would i then get MainWindow to show itself again?
Use pubsub to send a message to the main window telling it to Show itself again. I actually have an example of how to do that here:
http://www.blog.pythonlibrary.org/2010/06/27/wxpython-and-pubsub-a-simple-tutorial/
Note that this tutorial is using slightly older API that was available in wxPython 2.8. If you're using wxPython 2.9, then you'll have to use the slightly different API that I detail in this article:
http://www.blog.pythonlibrary.org/2013/09/05/wxpython-2-9-and-the-newer-pubsub-api-a-simple-tutorial/
When you create an instance of RecWindow keep a reference to it on main_window and bind to its close event.
In the main_window's close handler check if the window closed was the RecWindow.
If it was, clear the reference to it and show the main_window.
Elif the closed window was main_window carry out any required code.
Finally call event.Skip() so the windows get destroyed.
import wx
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, -1, title, (500, 200), (650, 500),
wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
panel = wx.Panel(self)
button = wx.Button(panel, wx.ID_OPEN)
panel.sizer = wx.BoxSizer(wx.VERTICAL)
panel.sizer.Add(button, 0, wx.ALL, 7)
panel.SetSizer(panel.sizer)
button.Bind(wx.EVT_BUTTON, self.on_button)
self.Bind(wx.EVT_CLOSE, self.on_close)
self.rec_window = None
def on_button(self, event):
rec_window = RecWindow(self, 'Rec window')
rec_window.Show()
self.Hide()
rec_window.Bind(wx.EVT_CLOSE, self.on_close)
self.rec_window = rec_window
def on_close(self, event):
closed_window = event.EventObject
if closed_window == self.rec_window:
self.rec_window = None
self.Show()
elif closed_window == self:
print 'Carry out your code for when Main window closes'
event.Skip()
class RecWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, -1, title, (400, 200), (700, 600),
wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
app = wx.App(False)
main_window = MainWindow(None, 'Main window')
main_window.Show()
app.MainLoop()
I'm creating table with static text with a while loop, after that I want to set labels. I'm having problem with it, because it only works with the last one. Here is my code:
import wx
class Mainframe(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
self.panel = wx.Panel(self)
def test(self,n):
while n <=5:
a = wx.StaticText(self.panel, label='bad', id=n, pos=(20,30*n))
n = n+1
return a
test(self,0)
if test(self,0).GetId()==1:
test(self,0).SetLabel('good')
if test(self,0).GetId()==5:
test(self,0).SetLabel('exelent')
if __name__=='__main__':
app = wx.App(False)
frame = Mainframe(None)
frame.Show()
app.MainLoop()
When you are returning a it is only the last control created because each time you loop it is overwritten.
Append the controls to a list and then you can access them all.
Also note that you are calling test 5 times, so you will have 5 lots of your set of statictexts created on top of each other.
import wx
class Mainframe(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
self.panel = wx.Panel(self)
ctrls = []
for n in range(6):
ctrls.append(wx.StaticText(self.panel, label='bad',
pos=(20, 30 * n)))
ctrls[1].SetLabel('good')
ctrls[5].SetLabel('excellent')
if __name__ == '__main__':
app = wx.App(False)
frame = Mainframe(None)
frame.Show()
app.MainLoop()