How to add hyperlinks in the help of a wxpython app? - python

I'm implementing the help menu of an App done in wxPython. By now, I'm using a txt file opened in a frame. I would like to have hyperlinks in the help text in order to open other txt files in the same frame. However, I don't know how to do this. I don't even know if this is the most elegant way to implement a help menu. Any suggestion will be very useful.
Below you can find part of the code I'm using (you will need a txt file called "Help_Main_App.txt"):
import wx
class Help_Frame(wx.Frame):
title = "Help, I need somebody, help..."
def __init__(self):
wx.Frame.__init__(self, wx.GetApp().TopWindow, title=self.title, size=(450,500))
self.CreateStatusBar()
panel = wx.Panel(self, wx.ID_ANY)
panel.SetBackgroundColour('#ededed')
self.Centre()
vBox = wx.BoxSizer(wx.VERTICAL)
hBox = wx.BoxSizer(wx.HORIZONTAL)
self.textbox = wx.TextCtrl(panel, style=wx.TE_MULTILINE, size=(-1, 295))
hBox.Add(self.textbox, 1, flag=wx.EXPAND)
vBox.Add(hBox, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=10)
panel.SetSizer(hBox)
defaultdir, filename = './', 'Help_Main_App.txt'
self.filePath = '/'.join((defaultdir, filename))
self.textbox.LoadFile(self.filePath)
self.textbox.Disable()
class Main_Window(wx.Frame):
def __init__(self, parent, title):
#wx.Frame.__init__(self, parent, title = title, pos = (0, 0), size = wx.DisplaySize())
wx.Frame.__init__(self, parent, title=title, size=(1000,780))
self.Center()
# Setting up the menu.
filemenu = wx.Menu()
helpmenu = wx.Menu()
menuExit = filemenu.Append(wx.ID_EXIT,"&Exit"," Close window and exit program")
menuHelp = helpmenu.Append(wx.ID_HELP, "&Help"," Help of this program")
# Creating the menubar.
menuBar = wx.MenuBar()
menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar
menuBar.Append(helpmenu,"&Help") # Adding the "helpmenu" to the MenuBar
self.SetMenuBar(menuBar) # Adding the MenuBar to the Frame content.
# Set event handlers
self.Bind(wx.EVT_MENU, self.OnHelp, menuHelp)
self.Bind(wx.EVT_MENU, self.OnExit, menuExit)
def OnHelp(self,e):
Help_Frame().Show()
def OnExit(self,e):
self.Close(True) # Close the frame.
def main():
app = wx.App(False)
frame = Main_Window(None, "Main App")
frame.Show()
app.MainLoop()
if __name__ == "__main__" :
main()

I recommend using an HTMLWindow for something simple like that. It can handle only simple HTML, so don't try to make a website with it as HTMLWindow doesn't support CSS or javascript.
I wrote a simple About box using it. You can read about it here:
http://www.blog.pythonlibrary.org/2008/06/11/wxpython-creating-an-about-box/
The basic idea is to subclass HTMLWindow and override its OnLinkClicked method. Then you can use Python's webbrowser to open the user's default browser. Or you can try using subprocess, although that will be a lot less likely to work unless you always know what is installed on your target machines.

Further to Mikes answer if you are able to use wxPython 2.9.4 or above you can consider using the more advanced html2 webview which does support CSS and javascript. Using this you could make the help as a simple website that can be viewed in program.
http://wxpython.org/Phoenix/docs/html/html2.WebView.html
Its also worth mentioning that if (for some strange reason) you don't want to work with you could achieve a similar outcome with a StyledTxtCtrl.

Late to the party but just for the sake of completeness (seeing that the OP's code was using wx.TextCtrl to show the help text), here is an example on how to add and launch hyperlinks using wx.TextCtrl (I have attached any explanations on the code comments):
class HelpDialog(wx.Dialog):
"""Help Dialog."""
def __init__(self, parent, title, style):
"""Init."""
wx.Dialog.__init__(self, parent=parent, id=wx.ID_ANY,
title=title, pos=wx.DefaultPosition, size=wx.DefaultSize, style=style)
# We need the 'wx.TE_AUTO_URL' style set.
self.help = wx.TextCtrl(self, wx.ID_ANY, '', DPOS, DSIZE,
wx.TE_AUTO_URL|wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_RICH2|wx.TE_WORDWRAP)
# Events - this is the interesting part,
# we catch the mouse on hovering the hyperlink:
self.help.Bind(wx.EVT_TEXT_URL, self.openHlpUrl)
# Show dialog
self.ShowModal()
def openHlpUrl(self, event):
"""Open help URL."""
# We get the starting and ending points on
# the text stored in our ctrl from this event
# and we slice it:
url = self.help.GetValue()[event.GetURLStart():event.GetURLEnd()]
# We want to capture the left up mouse event
# when hovering on the hyperlink:
if event.MouseEvent.LeftDown():
# Let's be wxpythion native and launch the browser this way:
wx.LaunchDefaultBrowser(url)

Related

How to prevent user dragging window/frame

I need to create a simple app that cannot be resized, minimised, maximised and moved. I've been able to fulfill all this requirements but one thanks to this answer but I cant find how to prevent window dragging.
I've tried to adapt this C++ answer : when move event is trigerred, I just move back the window to its original position, but it's not very clean : it makes the windows shaky and sometimes minimize all other running apps. Also, the "Move" options is style available from system menu and I'd like to disable it.
So how can I disable dragging the window when clicking on title bar and disable Move option from system menu ?
I'm running Windows 10, Python 3.10 and wxpython 4.1.1.
def __init__(self):
# stuffs ...
self.Center()
self.Show()
x, y = self.GetPosition()
self.x = x
self.y = y
self.Bind(wx.EVT_MOVE, self.on_move)
def on_move(self, ev):
ev.Skip()
self.Move(self.x, self.y)
I would never recommend having a window that is not moveable but if that is a game changer, in your specific situation, then you could define a window that has no frame and thus is not moveable.
The downside is that you would have to include any caption and a close facilty within the window yourself.
I'd also recommend making it Always_on_top.
As you can see, you gain the restrictions but lose compatability with other windows on the desktop.
import wx
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, ("Moveable or Stuck"), size=(420, 210), \
style = wx.FRAME_NO_TASKBAR \
& ~(wx.MINIMIZE_BOX|wx.RESIZE_BORDER|wx.MAXIMIZE_BOX))
panel = wx.Panel(self)
caption = wx.StaticText(panel, -1, "Moveable or Stuck", pos=(10,5))
caption.SetBackgroundColour('lightgrey')
self.CloseButton = wx.BitmapButton(panel, bitmap=wx.ArtProvider.GetBitmap(wx.ART_CLOSE), \
pos=(380,5), size=(32,32))
self.Bind(wx.EVT_BUTTON, self.OnExit)
self.SetWindowStyle(wx.STAY_ON_TOP | wx.BORDER_NONE | wx.FRAME_NO_TASKBAR )
def OnExit(self, event):
self.Destroy()
app = wx.App()
frame = MyFrame(None)
frame.Show()
app.MainLoop()
I don't run windows OS so I can't test the wx.EVT_MOVE_END event, which should give you a better result.
The style gives you just a Close box and prevents the window being included in the taskbar.
Overriding the Move ability provided by the OS, is I believe, not possible, at an App level at least.
import wx
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, ("Moveable or Stuck"), size=(420, 210), \
style = wx.SYSTEM_MENU|wx.CLOSE_BOX|wx.FRAME_NO_TASKBAR \
& ~(wx.MINIMIZE_BOX|wx.RESIZE_BORDER|wx.MAXIMIZE_BOX))
self.Pos = self.GetPosition()
# other than Windows OS
self.Bind(wx.EVT_MOVE, self.OnMove)
# Windows Only
#self.Bind(wx.EVT_MOVE_END, self.OnMove)
def OnMove(self, event):
self.SetPosition(self.Pos)
app = wx.App()
frame = MyFrame(None)
frame.Show()
app.MainLoop()

Adding a text control window to a wxMessageDialog box/AboutDialogInfo in wxPython

I am working with python v2.7 and wxPython v3.0 on Windows 7 OS.
In my app I have a about menu. Upon clicking the about menu I want to display some information about my app. I am trying to create a dialog box/AboutBox exactly as shown in the image below.(This is the about dialog of notepad++. Click on ? in the menu bar of notepad++.)
The special thing about the dialog box of notepad++ is that I need a text control window too. One can copy the info.
I tried to do the same in wxPython, but unfortunately I failed. I tried two different hit and trial approaches.
1. I tried to add the text control window to the dialog box wxMessageDialog but it doesn't shows up at all.
2. I tried to use the AboutBox in wxPython, and tried to add the text control to it but it failed because the AboutDialogInfo is not a window and the parent of the text control should be of a window type.
Error:
aboutPanel = wx.TextCtrl(info, -1, style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
File "C:\Python27\lib\site-packages\wx-3.0-msw\wx\_controls.py", line 2019, in __init__
_controls_.TextCtrl_swiginit(self,_controls_.new_TextCtrl(*args, **kwargs))
TypeError: in method 'new_TextCtrl', expected argument 1 of type 'wxWindow *'
It would be great if someone could provide some idea on how to add a text control windows to a dialog box/AboutBox?
Code: Here is my code sample for playing around:
import wx
from wx.lib.wordwrap import wordwrap
class gui(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self,None, id, title, style=wx.DEFAULT_FRAME_STYLE)
panel1 = wx.Panel(self, -1)
panel1.SetBackgroundColour('#fffaaa')
menuBar = wx.MenuBar()
file = wx.Menu()
file.Append(101, '&About1', 'About1')
file.Append(102, '&About2', 'About2')
menuBar.Append(file, '&File')
self.SetMenuBar(menuBar)
wx.EVT_MENU(self, 101, self.onAbout)# Event for the About1 menu
wx.EVT_MENU(self, 102, self.onAboutDlg)# Event for the About2 menu
def onAbout(self, event):
message = 'This fantastic app was developed using wxPython.\nwxPython is c00l :)'
dlg = wx.MessageDialog(self, message, 'My APP', wx.OK|wx.ICON_INFORMATION)
aboutPanel = wx.TextCtrl(dlg, -1, style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
aboutPanel.WriteText('Experimentation is the part of our life.\n')
dlg.ShowModal()
dlg.Destroy()
def onAboutDlg(self, event):
self.panel = wx.Panel(self, -1)
info = wx.AboutDialogInfo()
info.Name = "My About Box"
info.Version = "0.1"
info.Copyright = "(C) 2014 xxx"
info.Description = wordwrap(
"This is an example application that shows the problem "
"that I am facing :)",
350, wx.ClientDC(self.panel))
info.WebSite = ("http://stackoverflow.com/users/2382792/pss", "My Home Page")
info.Developers = ["PSS"]
info.License = wordwrap("Driving license and a AK-47 too :P ", 500,wx.ClientDC(self.panel))
# Uncomment the following line to get the error!
#aboutPanel = wx.TextCtrl(info, -1, style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
#aboutPanel.WriteText('Experimentation is the part of our life.\n')
wx.AboutBox(info)
if __name__ == '__main__':
app = wx.App()
frame = gui(parent=None, id=-1, title="My-App")
frame.Show()
app.MainLoop()
Thank you for your time!
wxAboutBox() uses the standard about dialog for the current platform so it won't help you to achieve your goal. Similarly, wxMessageDialog is the native message box dialog which, again, can't have your custom text box. OTOH there is absolutely no problem with building any dialog you want using wxDialog and adding elements to it (and using sizers to lay them out).
IOW your mistake is to think that the class you need to use is wxMessageDialog: it isn't, you need wxDialog.

Sizing a wx.TextCtrl widget?

I'm in the process of learning to use wxWidgets and Python, but I'm having some trouble figuring out how to size widgets within a frame.
I am under the impression that I can set the size of various widgets by giving them a custom size=(x,y) value when calling the constructor. This code is copied and pasted out of the examples for wxPython and I have added the value="example",pos=(0,0) and size=(100,100) values to the wx.TextCtrl() constructor, but when I run this program, the text control takes up the entirety of the 500x500 frame. I'm not sure why, and I'd appreciate any help you could give me to get it to work.
import wx
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(500,500))
self.control = wx.TextCtrl(self,-1,value="example",pos=(0,0),size=(100,100))
self.CreateStatusBar() # A Statusbar in the bottom of the window
# Setting up the menu.
filemenu= wx.Menu()
# wx.ID_ABOUT and wx.ID_EXIT are standard IDs provided by wxWidgets.
filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
filemenu.AppendSeparator()
filemenu.Append(wx.ID_EXIT,"E&xit"," Terminate the program")
# Creating the menubar.
menuBar = wx.MenuBar()
menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar
self.SetMenuBar(menuBar) # Adding the MenuBar to the Frame content.
self.Show(True)
app = wx.App(False)
frame = MainWindow(None, "Sample editor")
app.MainLoop()
Please read the sizers overview in the manual to know about how to size widgets correctly.
As for your particular example, it's an exception due to the fact that wxFrame always resizes its only window to fill its entire client area -- just because this is what you almost always want. However typically this only window is a wxPanel which, in turn, contains other controls and uses sizers to position them.
TL;DR: You should never use absolute positioning, i.e. specifying positions in pixels.
For sure you need to read from this book: wxPython 2.8 Application Development Cookbook
Read -> Chapter 7: Window Layout and Design
In your code, add widgets holder: panel.
import wx
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(500,500))
panel = wx.Panel(self)
self.control = wx.TextCtrl(panel,-1,value="example",pos=(0,0),size=(100,100))
self.CreateStatusBar() # A Statusbar in the bottom of the window
# Setting up the menu.
filemenu= wx.Menu()
# wx.ID_ABOUT and wx.ID_EXIT are standard IDs provided by wxWidgets.
filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
filemenu.AppendSeparator()
filemenu.Append(wx.ID_EXIT,"E&xit"," Terminate the program")
# Creating the menubar.
menuBar = wx.MenuBar()
menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar
self.SetMenuBar(menuBar) # Adding the MenuBar to the Frame content.
self.Show(True)
app = wx.App(False)
frame = MainWindow(None, "Sample editor")
app.MainLoop()

wxpython, passing user datas to html2

i am trying to develop python GUI to access webpages. below example is working fine. but i need to pass user credentials(username/password) with in this code.
i dont want to click on that button. just i need to fill text boxes in login page
import wx
import wx.html2
class MyBrowser(wx.Dialog):
def __init__(self, *args, **kwds):
wx.Dialog.__init__(self, *args, **kwds)
sizer = wx.BoxSizer(wx.VERTICAL)
self.browser = wx.html2.WebView.New(self)
self.browser.LoadURL("http://wiki.python.org/moin/GuiProgramming?action=login")
sizer.Add(self.browser, 1, wx.EXPAND, 10)
self.SetSizer(sizer)
self.SetSize((700, 700))
if __name__ == '__main__':
app = wx.App()
dialog = MyBrowser(None, -1)
dialog.Show()
app.MainLoop()
The "Use javascript" answer is certainly helpful, but with recent versions of wxPython anyway, it won't run unless wx.html2.EVT_WEB_VIEW_LOADED is changed to wx.html2.EVT_WEBVIEW_LOADED ("WEB_VIEW changed to "WEBVIEW").
Use javascript. Simple sample code below.
import wx
import wx.html2
class MyBrowser(wx.Dialog):
def __init__(self, *args, **kwds):
wx.Dialog.__init__(self, *args, **kwds)
sizer = wx.BoxSizer(wx.VERTICAL)
self.browser = wx.html2.WebView.New(self)
self.browser.LoadURL("http://wiki.python.org/moin/GuiProgramming?action=login")
sizer.Add(self.browser, 1, wx.EXPAND, 10)
self.SetSizer(sizer)
self.SetSize((700, 700))
# We have to bind an event so the javascript is only run once the page
# is loaded.
self.Bind(wx.html2.EVT_WEB_VIEW_LOADED, self.OnPageLoaded,
self.browser)
def OnPageLoaded(self, evt):
self.browser.RunScript("""
// There are probably better ways to get the elements you
// want, but this works.
document.getElementsByName('name')[0].value="hist";
document.getElementsByName('password')[0].value="bar";
document.getElementById('openididentifier').value="ident";
// If you want to submit the form you can use something like
//document.getElementsByName('login')[1].click()
""")
# And you probably want to unbind the event here
self.Bind(wx.html2.EVT_WEB_VIEW_LOADED, None,
self.browser)
if __name__ == '__main__':
app = wx.App()
dialog = MyBrowser(None, -1)
dialog.Show()
app.MainLoop()
I would check out Selenium. It's an open source web navigating automation tool which has developed an awesome module for python. I've used it to automate logging into multiple different websites and you could easily slap a wx GUI on top of it.

Adding a widget with a button - wxPython

I'm trying to create something like the categories panel in Wordpress, with wxPython.
What I'm trying to figure out, is how to add a widget when the user clicks a button (like "Add New Category")
Here is my code:
import wx
class MainWindow(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, size=(300,200))
self.panel = wx.Panel(self, -1)
button = wx.Button(self.panel,-1,"Button")
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.vbox.Add(button)
add_btn = wx.Button(self.panel,-1,"Add")
add_btn.Bind(wx.EVT_BUTTON, self.add)
hbox = wx.BoxSizer(wx.HORIZONTAL)
hbox.Add(add_btn)
main_vbox = wx.BoxSizer(wx.VERTICAL)
main_vbox.Add(self.vbox)
main_vbox.Add(hbox)
self.panel.SetSizer(main_vbox)
self.Centre()
self.Show(True)
def add(self,event):
self.vbox.Add((wx.Button(self.panel,-1,"Button")))
if __name__ == "__main__":
app = wx.App()
MainWindow(None, -1, 'Add a Button')
app.MainLoop()
My problem is, the button gets added on top of the previous button. I'm rather mystified by this, because if I delete the event argument of the add() function, and then call it in the __init__ method, self.add(), it works fine. But that doesn't help me any because I need to add the widgets when the user clicks the button.
Any help is much appreciated.
Call self.panel.Layout() after adding the button. This function is called automatically when you resize a window with children (try it with your current code), but not when you add widgets to it.

Categories