scrollbar for statictext in wxpython? - python

is possible to add a scrollbar to a statictext in wxpython?
the thing is that i'm creating this statictext:
self.staticText1 = wx.StaticText(id=wxID_FRAME1STATICTEXT1,label=u'some text here',name='staticText1', parent=self.panel1, pos=wx.Point(16, 96),
size=wx.Size(408, 216),style=wx.ST_NO_AUTORESIZE | wx.THICK_FRAME | wx.ALIGN_CENTRE | wx.SUNKEN_BORDER)
self.staticText1.SetBackgroundColour(wx.Colour(255, 255, 255))
self.staticText1.SetBackgroundStyle(wx.BG_STYLE_SYSTEM)
self.staticText1.SetFont(wx.Font(9, wx.SWISS, wx.NORMAL, wx.BOLD, False,u'MS Shell Dlg 2'))
self.staticText1.SetAutoLayout(True)
self.staticText1.SetConstraints(LayoutAnchors(self.staticText1, False,True, True, False))
self.staticText1.SetHelpText(u'')
but later i use StaticText.SetLabel to change the label and the new text is too big to fit the window, so i need to add a scrollbar to the statictext..
i tried adding wx.VSCROLL to the style, and the scrollbar show up but cant scroll down to see the rest of the text..

wx.StaticText is designed to never respond to mouse events and never take user focus. Given that this is its role in life, it seems that a scrollbar would be inconsistent with its purpose.
There are two ways to get what you want: 1) You could use a regular TextCtrl with the style TE_READONLY (see here); or 2) you could make a scrolled window that contains your StaticText control.

Related

Why does the caret disappear from a TextCtrl widget after a focus event?

I'm writing a calculator application on MS Windows 8.1 using wxPython-Phoenix in which I'd like the calculation to be performed as soon as the user enters a value into one of the parameter fields. To achieve that I'm using wx.EVT_KILL_FOCUS generated by any parameter field to generate a command event which triggers the calculation method. Everything works fine but the appearance of the caret in the parameter fields (implemented by TextCtrl widgets).
After setting the focus either by the Tab key or the mouse on a certain field and moving it (again by the Tab key or the mouse) to a different field - the caret is gone from the TextCtrl widget never to return! when you call the GetCaret() method on that TextCtrl widget the return value is None.
See attached example. The widget still accepts input and displays it but without a caret.
How can I restore the caret in the right position? or not lose it in the first place?
I've tried setting a new caret in the TextCtrl but it does not follow the text input. Since the application is ment to be intensely interactive, I want the triggering event to be the lost focus and not a button (to minimize the number of clicks)
import wx
class MyFrame(wx.Frame):
def __init__(self, *args, **kwargs):
super(MyFrame, self).__init__(*args, **kwargs)
self.InitUI()
def InitUI(self):
# setting up the Input panel
panel = wx.Panel(self)
self.lbl1 = wx.StaticText(panel,wx.ID_ANY,'Some Text:')
self.txt1 = wx.TextCtrl(panel,wx.ID_ANY,style=wx.TE_RIGHT)
self.lbl2 = wx.StaticText(panel,wx.ID_ANY,'Some Other Text:')
self.txt2 = wx.TextCtrl(panel,wx.ID_ANY,style=wx.TE_RIGHT)
infgsz = wx.FlexGridSizer(2,2,15,15)
infgsz.AddMany([(self.lbl1,0,wx.ALIGN_LEFT),\
(self.txt1,0,wx.ALIGN_LEFT),\
(self.lbl2,0,wx.ALIGN_LEFT),\
(self.txt2,0,wx.ALIGN_LEFT)])
self.txt1.Bind(wx.EVT_KILL_FOCUS,self.OnInput)
self.txt2.Bind(wx.EVT_KILL_FOCUS,self.OnInput)
box = wx.BoxSizer(wx.HORIZONTAL)
box.Add(infgsz, flag= wx.EXPAND | wx.ALL, border=15)
panel.SetSizer(box)
self.SetSize((280, 140))
self.SetTitle('TextCtrl Demo')
self.Centre()
def OnInput(self, e):
if e.GetId() == self.txt1.GetId():
self.lbl2.SetForegroundColour(wx.ColourDatabase().Find('RED'))
self.lbl1.SetForegroundColour(wx.ColourDatabase().Find('BLACK'))
else:
self.lbl1.SetForegroundColour(wx.ColourDatabase().Find('BLUE'))
self.lbl2.SetForegroundColour(wx.ColourDatabase().Find('BLACK'))
self.Refresh()
def main():
app = wx.App()
frame = MyFrame(None)
frame.Show()
app.MainLoop()
if __name__ == '__main__':
main()
The above application displays 2 lines of text. In each line there is a label (wx.StaticText widget) on the left and a TextCtrl widget on the right. when you enter text into the TextCtrl widget of either line and move the focus to the other TextCtrl widget the corresponding label foreground color (the text color) changes to RED or BLUE and the other label changes to BLACK. However, without any seen reason, the caret disappears from the top TextCtrl widget and never returns to it! (On MS Windows 8.1 at least).
The documentation explicitly tells you
The focus event handlers should almost invariably call wxEvent::Skip() on their event argument to allow the default handling to take place.
and goes on to explain that not doing it may result in various problems -- such as the one you're observing.
Add e.Skip() to your event handler to fix it.

WxPython - How to hide the X and expand button on window

Im making a python program and in some functions it needs to hide the X and expand window buttons, how would i do it? Im using WxPython, how would I put this in?
The widgets in the window frame are defined as part of the window's style: CLOSE_BOX, MINIMIZE_BOX, and MAXIMIZE_BOX.
So, when you create the window, just leave those styles out.
If you're using a wx.Frame subclass, note that DEFAULT_FRAME_STYLE includes these values, so you will have to mask them out:
style = wx.DEFAULT_FRAME_STYLE & (~wx.CLOSE_BOX) & (~wx.MAXIMIZE_BOX)
super().__init__(whatever, args, you, use, style=style)
If you want to change them after creation, you use SetWindowStyle:
style = self.GetWindowStyle()
self.SetWindowStyle(style & (~wx.CLOSE_BOX) & (~wx.MAXIMIZE_BOX))
self.Refresh()
However, notice that the documentation of that function says:
Please note that some styles cannot be changed after the window creation and that Refresh() might need to be called after changing the others for the change to take place immediately.
And, from what I can tell, on Windows, if you create a window with a close box and then remove it later in this way, it doesn't actually go away. It does disable, which may be good enough. But if not, there's probably no way to do what you want without either reaching underneath wx to the native Windows API (which gets very tricky), or drawing the widgets on the frame manually (which gets even more tricky, especially if you care about looking right on different versions of Windows—not to mention porting to other platforms).
I wrote about Frame styles a while ago on my blog. To remove all the buttons, you could do this:
import wx
########################################################################
class NoSystemMenuFrame(wx.Frame):
"""
There is no system menu, which means the title bar is there, but
no buttons and no menu when clicking the top left hand corner
of the frame
"""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
no_sys_menu = wx.CAPTION
wx.Frame.__init__(self, None, title="No System Menu", style=no_sys_menu)
panel = wx.Panel(self)
self.Show()
#----------------------------------------------------------------------
if __name__ == "__main__":
app = wx.App(False)
frame = NoSystemMenuFrame()
app.MainLoop()
I tried setting the style to wx.DEFAULT_FRAME_STYLE & (~wx.CLOSE_BOX) & (~wx.MAXIMIZE_BOX) and to wx.DEFAULT_FRAME_STYLE^(wx.CLOSE_BOX|wx.MAXIMIZE_BOX), but both of those seem to only remove the Close box. For some reason, the Maximize button is still there on my Xubuntu machine.

Always open wxPython GUI with scroll bar selected

I have several GUIs which open progressively that are all experiencing the same issue. When I open them, the first object which is selected is a TextCtrl object. These GUIs are rather large and have scroll bars. Since a TextCtrl object is selected, scrolling with the mouse wheel does nothing and makes it appear as if the scroll bars are broken. To demonstrate this, I made the following code:
import wx
class Tester(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, "Window", size=(500, 500))
self.panel = wx.ScrolledWindow(self,wx.ID_ANY)
self.panel.SetScrollbars(30,30,600,400)
textBox = wx.TextCtrl(panel, -1, "", size=(200, 150), style=wx.TE_MULTILINE|wx.TE_LEFT)
textStuff = wx.StaticText(panel, -1, "A\nbunch\nof\nlines\nto\nmake\nthis\nlong\nenough\nto\nhave\nscroll\nbars\n\n\n\n\n\n\n\n\n\nIts lonely down here\n\n\n\n:(")
lonelyBtn = wx.Button(panel, -1, "So Lonely")
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(textBox, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, border=10)
vbox.Add(textStuff, flag=wx.LEFT|wx.RIGHT|wx.TOP, border=10)
vbox.Add(lonelyBtn, flag=wx.LEFT|wx.RIGHT|wx.TOP, border=10)
panel.SetSizer(vbox)
panel.Layout()
app = wx.PySimpleApp()
Tester().Show()
app.MainLoop()
When you run this and try to scroll down, you'll notice you cannot scroll with the mouse wheel.
So far, this all makes sense. Here's where it gets a little weird. If the selected object is a Button, using the mouse wheel engages the scroll bars. You can test this by pressing the button, then using the mouse wheel. Also, clicking on the panel, or even the scroll bar itself, doesn't allow the mouse wheel to work.
What I'm looking for is a way to make sure that if there are scroll bars, they can be used with the mouse wheel upon displaying the GUI (they are selected by default). I can accept that the mouse wheel will not function once a user clicks into the text control.
Additionally, if you have an explanation for why the mouse wheel works for buttons and not text controls, I'd love to hear it
EDIT: I know I can add a listener (thanks to Mr. Joran Beasley), but this means that the scroll bars within a multi-line text control can never be used with the mouse wheel. The ideal solution (which I'm not sure is possible), is to have clicking on anywhere outside the text control (panel or scroll bar) allows the mouse wheel to scroll the panel. Is this possible?
Additionally, I've switched over to using ScrolledWindow instead of ScrolledPanel
EDIT 2: The fix was to use the following:
self.panel.Bind(wx.EVT_MOTION, self.onMouseMove)
def onMouseMove(self, event):
self.panel.SetFocusIgnoringChildren()
EDIT 3: The actual fix was to do something a little tricky. Using the code below I bound only multiline text controls to EVT_ENTER_WINDOW and EVT_LEAVE_WINDOW as well as binding every item (and the panel itself) to EVT_MOUSEWHEEL. Then a logical self.inMLTxtCtrl tracks if the mouse if over any of the multiline text controls
self.panel.Bind(wx.EVT_MOUSEWHEEL, self.onWheel)
for sizerItem in self.panel.GetSizer().GetChildren():
try:
if sizerItem.GetWindow().IsMultiLine():
sizerItem.GetWindow().Bind(wx.EVT_ENTER_WINDOW, self.onMouseEnter)
sizerItem.GetWindow().Bind(wx.EVT_LEAVE_WINDOW, self.onMouseLeave)
sizerItem.GetWindow().Bind(wx.EVT_MOUSEWHEEL, self.onWheel)
except:
sizerItem.GetWindow().Bind(wx.EVT_MOUSEWHEEL, self.onWheel)
Then a logical self.inMLTxtCtrl tracks if the mouse if over any of the multiline text controls as shown below.
def onMouseEnter(self, event):
print "entering"
self.inMLTxtCtrl = True
def onMouseLeave(self, event):
print "leaving"
self.inMLTxtCtrl = False
Finally, the onWheel() function uses this logical flag to determine where to scroll. If the mouse is in a multiline text control when the scroll wheel is turned, it attempts to scroll in that text control. Othewise, the SetFocusIgnoringChildren() function is called and the panel is scrolled. Since the panel and text control use different scrolling methods, a try...except is needed.
def onWheel(self, event):
if self.inMLTxtCtrl:
print "in", event.GetWheelRotation()
else:
print "out", event.GetWheelRotation()
self.panel.SetFocusIgnoringChildren()
try:
currScroll = self.panel.FindFocus().GetViewStart()
newScroll = (currScroll[0],currScroll[1]- event.GetWheelRotation()/60)
self.panel.FindFocus().Scroll(*newScroll)
except:
self.panel.FindFocus().ScrollLines(event.GetWheelRotation()/-60)
class Tester(wx.Frame):
def OnTxtScroll(self,e):
currScroll = self.panel.GetViewStart()
newScroll = (currScroll[0],currScroll[1]- e.GetWheelRotation()/120)
self.panel.Scroll(*newScroll)
def __init__(self):
....
#your code
....
self.panel = panel
textBox.Bind(wx.EVT_MOUSEWHEEL,self.OnTxtScroll)
after your clarification ... I think that this would work (its a bit hacky and doesnt do exactly what you describe ... but it might work)
def OnTxtScroll(self,e):
print dir(e)
target = e.GetEventObject()
p1 = target.GetScrollPos(wx.VERTICAL)
e.Skip()
self.Update()
def updateScroll(p1,target,scroll_amt):
p2 = target.GetScrollPos(wx.VERTICAL)
if p1 ==p2:#scroll did not effect target object so lets scroll our main panel
currScroll = self.panel.GetViewStart()
newScroll = (currScroll[0],currScroll[1]- scroll_amt)
self.panel.Scroll(*newScroll)
wx.CallAfter(updateScroll,p1,target,e.GetWheelRotation()/120)

PyQt4 Toolbar Button Alignment

I have this code
Menu = self.menuBar()
EditMenu = Menu.addMenu("&File")
OptionMenu = Menu.addMenu("&Options")
HelpMenu = Menu.addMenu("&Help")
EditMenu.addActions((fileNewAction,faultAction,storeAction,localAction,scheduleAction))
OptionMenu.addAction(settingAction)
Toolbar = QtGui.QToolBar()
Toolbar.setIconSize(QtCore.QSize(50,50))
Toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon|QtCore.Qt.AlignLeading) #<= Toolbuttonstyle
self.addToolBar(QtCore.Qt.LeftToolBarArea,Toolbar)
Toolbar.addActions((fileNewAction,faultAction,scheduleAction,storeAction,localAction,settingAction))
settings = QtCore.QSettings()
self.restoreGeometry(settings.value("Geometry").toByteArray())
which give me this
i used
Toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon|QtCore.Qt.AlignLeading)
to display the text on the right side of the toolbar button and to align all the toolbar button images to the left. But the texts are not appearing on the right side.
If i remove the QtCore.Qt.AlignLeading,
I get unaligned (left side) buttons like this
(1) How do i get my toolbar button icons align to the left and display the text on the right side at the same time?
Another question is
(2) How do i adjust the width of the raised button effect when i mouse over on each button? I want the width of all the buttons to be the same. The width seems to be different depending on how long the text is.
IMHO, the problem is the QToolBar decides on a size of each button individually, disregarding neighboring buttons completely. But you can set the size manually:
for action in my_toolbar.actions():
widget = my_toolbar.widgetForAction(action)
widget.setFixedSize(width, height)

Delete image in wxpython?

I know this sounds easy but how can i delete a image with wxpython?
I am using wx.StaticBitmap and wx.Image to create the image, but is there a way to delete an image?
Maybe theres something like this:
bmp1 = wx.Image(filename, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
self.bitmap1 = wx.StaticBitmap(self.sizer, -1, bmp1, (0, 0))
delete_image(self.bitmap1)
delete_image(bmp1)
I have tried replacing the variable but the image still shows.
self.bitmap1.Hide() or self.bitmap1.Destroy(). The first will just hide it whereas the second will actually destroy the widget. You'll probably want to call self.Layout() to make your window redraw and update the layout of your widgets.

Categories