wxPython, button design - python

How to change the button decoration with wxPython, generally when the button is clicked, a dotted lines appear on the button.. any way to make that button not show the dotted lines?
Thanks

Assuming you're running your program on Windows (you didn't say which OS, but dotted lines are used by Windows Classic look), the dotted lines are called the focus rect, and they appear to mark a button or widget as focused. They are a system setting, and your program is acting as it should - wxWidgets is meant to emulate the underlying OS default behaviour as closely as possible.
Update
I don't think you can change this behaviour from inside the program. I really doubt that wxWidgets has a setting somewhere for this, as it is OS-dependent and is the standard and correct behaviour for the Classic theme. But the focus rect is shown by default only on the Classic Look which few people use.
Try switching to Luna theme (the default on XP), and you'll see that the focus rect won't appear unless you start hitting Tab while your window is in focus. By the way, the focus rect is needed exactly for when you are switching the focus using the Tab key. You need to see where the focus is, after all. That way you know when you press Enter or Space, which button is going to be pressed. Not everyone uses only the mouse.

You can use a custom button, for instance wx.lib.buttons.GenButton which is in pure python so you can overwrite the look, feel etc.
This also has a method SetUseFocusIndicator to turn off the dotted focus indicator

Related

Custom window using Perl TK or Python Tkinter

All windows made through Perl TK or Python Tkinter look like default Windows-styled window, with red cancel button on top right, preceded by maximize and minimize buttons, blue top bar, etc. Is it possible to make custom windows, like those we see for downloaded softwares, where everything, right from the color, to position of buttons, their styles, etc are customized?
You can turn off the standard decorations in a few ways, e.g., by setting the toolwindow boolean attribute (Windows only), by making it an overrideredirect window, or (with a new-enough Tk) by setting the type attribute of the window to something like utility (X11 only). With the standard decorations disabled, you can then draw anything you want (which is how the other programs you mention do things), though there are a number of restrictions, particularly with focus handling. Override-redirected windows often don't participate in the keyboard management regime, because they're mostly invisible to the window manager which doesn't know to direct focus to them in the first place. (IIRC, you can force it but then you're getting into a fight with the WM and that's difficult to get right; “don't fight the WM” is one of the good rules of thumb for GUI design.) You can also set the window as transient (i.e., working for another window) which often reduces decoration levels.
The way you set these things depends on the language you're using. I can point to the places to look in the “mothership” documentation, but how they work in different languages does vary.

Read Only Text widget in python3-tkinter; cross platform

How to suppress end user ability to edit/add/delete text in a Text widget? (Python v3.2.. and tkinter)
The point is to suppress only the ability to change/add/delete text but not to castrate other features. Perhaps a NoEdit Text widged would be a better name.
I've tried .text['state'] = 'disabled' and it works almost OK in Windows (it still allows user to select/copy text highlights the selection, page up/down and up/down buttons work. The only thing broken seems to be the cursor made invisible.)
But on MacIntosh everything is broken. No highlights, no select/copy,... UGH
Since Tkinter has practically no documentation in Python, I've searched and found some TCL advise, to derive a new class and suppress the insert and delete functions.
So, I've tried as so:
class roText(tk.Text):
def insert(self,*args,**kwargs):
print(" Hey - Im inside roText.insert")
pass
def delete(self,*args,**twargs):
pass
def pInsert(self,*args,**twargs):
super().insert(*args,**twargs)
Unfortunately it didn't work right. Apparently tkinter does not use those insert and delete functions when end user enters/deletes code. Perhaps those TCL insert/delete are something else, and I lost something in translation from TCL and Swahili. What functions does tkinter.Text use for end user editing text? Hopefully they are not internal...
So, is there a way to modify the Text widget to suppress only end user editing?
Is there a way to do it without diving inside and overriding internal Tkinter code, so the stuff doesn't get broken by next releases of Tkinter?
Looking at the Idle shell window, I see that they've managed to suppress edits (except for the last line). So there is a way. But what is it and how costly?
Sorry for bumping an old question, but I was searching for an answer to this question also and finally found a solution. The solution I found involves overriding the key bindings when the text widget has focus and is pretty simple. Found here.
To override the bindings of a widget there is a bind function where you pass a string of what is to be overridden and the new function you want it to call.
self.txtBox.bind("<Key>", self.empty)
Somewhere else in the class you'll need to define the function to handle the event.
def empty(self, event):
return "break"
By returning the string "break" the event handler knows to stop after your function, instead of continuing with the default action.
I hope this answers your question. Cheers.
The reason the disabled state doesn't seem to work on the Mac is because it turns off the binding that gives focus to the widget. Without focus, the highlighting on a Mac doesn't show up. If you set the state to disabled but then assign a binding to <ButtonPress-1> to explicitly set focus to the disabled text widget, you can then select and copy text and the highlighting will show.
As for the cursor disappearing... arguably, that's what's supposed to happen. The cursor tells the user "this is where text will get inserted". Since no text will get inserted, having that visual clue would be confusing to the user. What you could do instead, if it was really important, is to insert a small image wherever they click to simulate the cursor.
To answer your question about whether the widget actually uses the insert and delete methods: the methods on the actual underlying widget are what the default bindings use, so overriding them in a subclass has no effect. You would need to redo all the default bindings for that to work. It's doable, but a lot of work.
Unfortunately, this is one area where programming in Tcl really shines, because you can simply disable the insert and delete commands of the widget. Of course, you can do that directly in Tkinter also since ultimately it runs tcl code to do everything, but that would involve writing some tcl code which is not a very good solution from the perspective of a Python coder.
I think the best solution is to use the disabled state, then add in just enough bindings to do what you want.
Here's a simple example that works by explicitly setting focus on a mouse button click. With this code I'm able to click and swipe to select a region, or double- or triple-click to select words and lines:
import Tkinter as tk
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.text = tk.Text(width=40, height=20)
self.text.bind("<1>", self.set_focus)
self.text.insert("end", "\n".join(dir(tk.Tk)))
self.text.configure(state="disabled")
self.text.pack(fill="both", expand=True)
def set_focus(self, event):
'''Explicitly set focus, so user can select and copy text'''
self.text.focus_set()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
#BryanOakley It took me a while to test your suggestion since I have no Mac.
Unfortunately Mac implementation of Python is buggy.
I've added focus, ie my disable function which I call after creating a window and inserting text, now calls first:
self.txt['state'] = 'disabled'
and then
self.txt.focus_set()
Which is what I think you've suggested.
It "kind of" worked. Ie: when selecting text (click and drag or double-click) highlighting works most of the time. Python must have some bad memory references or such bugs: Sometimes highlighting doesn't work at first, then it starts working (in the same window) after more clicking. Sometimes when program is invoked it works right of the bat. Sometimes selecting with Shift-rightArrow key will work but selecting with the mouse will not. Then starts working again. Or it will work fine in one window but not in another one (both of the same class), then starts working in all windows...etc...
The good thing is that adding focus did not affect badly Windows (ie all works fine as without focus.
I guess at this point I will just hope that future/next release of Python for Mac will fix those bugs..
BTW, it seems that Mac is a bit of an orphan for Python. Implementation is much uglier then for Windows. I mean the fonts look worse, the buttons, etc.. Or it could be due to different screen resolutions and Python ports that poorly account for those. Not sure
Anyway. Thank you for your help and suggestion to use focus for Mac.

Tell windows which monitor to display dialogs on

I've got a program which is using multiple monitors. The program is showing special visualizations on the second monitor. At one point, the program uses windows shell functions to send files to the recycle bin. However, when it does this, the delete confirmation dialog comes on top of my visualization. This is particularly problematic, as when the mouse is on the second monitor, my program uses mouse hooks to capture all mouse input, so the user cannot even click the confirmation dialog.
Is it possible to somehow tell Windows to only place dialog boxes on a particular display?
I'm using python, though if I have to call C WinAPI functions that shouldn't be a problem
which function are you using to send the files to the recycle bin? if you use SHFileOperation you can pass a parent HWND. perhaps make that an invisible WS_EX_TOOLWINDOW window on the other monitor.
i would expect the API, treating that window as a parent, would center relative to that window, but i haven't tried it.
depending on which version of Windows you are targeting, there used to be a capability to create desk bands that 'dock' to the sides of the screen. this automatically gets factored into the area returned as rcWork by GetMonitorInfo and should prevent dialogs from overlapping this space. There might be another way to declare that a region is "in use" in a way that declares space off-limits, but I don't know of it so it probably doesn't exist...
the ugly and crude thing you could do is poll and move the dialog yourself, but if this is any kind of widely deployed or commercial app that would likely cause more harm than good.

Can you auto hide frames/dialogs using wxPython?

I would like to create an application that has 3-4 frames (or windows) where each frame is attached/positioned to a side of the screen (like a task bar). When a frame is inactive I would like it to auto hide (just like the Windows task bar does; or the dock in OSX). When I move my mouse pointer to the position on the edge of the screen where the frame is hidden, I would like it to come back into focus.
The application is written in Python (using wxPython for the basic GUI aspects). Does anyone know how to do this in Python? I'm guessing it's probably OS dependent? If so, I'd like to focus on Windows first.
I don't do GUI programming very often so my apologies if this makes no sense at all.
As far as I know, there's nothing built in for this.
When the window is hidden, do you want it completely invisible or can a border of a few pixels be showing? That would be an easy way to get a mouse hover event. Otherwise you might have to use something like pyHook to get system-wide mouse events to know when to expand your window.
The events EVT_ENTER_WINDOW and EVT_LEAVE_WINDOW might also be useful here to know when the user has entered/left the window so you can expand/collapse it.
Expanding/collapsing can just be done by showing/hiding windows or resizing them. Standard window functions, nothing fancy.
By the way, you might want to use wx.ClientDisplayRect to figure out where to position your window. That will give you a rectangle of the desktop that does NOT include the task bar or any other toolbars the user has, assuming you want to avoid overlapping with those things.
Personally, I would combine the EVT_ENTER_WINDOW and EVT_LEAVE_WINDOW that FogleBird mentioned with a wx.Timer. Then whenever it the frame or dialog is inactive for x seconds, you would just call its Hide() method.
I think you could easily just make a window that is the same size as the desktop then do some while looping for an inactivity variable based on mouse position, then thread off a timer for loop for the 4 inactivity variables. I'd personally design it so that when they reach 0 from 15, they change size and position to become tabular and create a button on them to reactivate. lots of technical work on this one, but easily done if you figure it out

Change the focus from one Text widget to another

I'm new to Python and I'm trying to create a simple GUI using Tkinter.
So often in many user interfaces, hitting the tab button will change the focus from one Text widget to another. Whenever I'm in a Text widget, tab only indents the text cursor.
Does anyone know if this is configurable?
This is very easy to do with Tkinter.
There are a couple of things that have to happen to make this work. First, you need to make sure that the standard behavior doesn't happen. That is, you don't want tab to both insert a tab and move focus to the next widget. By default events are processed by a specific widget prior to where the standard behavior occurs (typically in class bindings). Tk has a simple built-in mechanism to stop events from further processing.
Second, you need to make sure you send focus to the appropriate widget. There is built-in support for determining what the next widget is.
For example:
def focus_next_window(event):
event.widget.tk_focusNext().focus()
return("break")
text_widget=Text(...)
text_widget.bind("<Tab>", focus_next_window)
Important points about this code:
The method tk_focusNext() returns the next widget in the keyboard traversal hierarchy.
the method focus() sets the focus to that widget
returning "break" is critical in that it prevents the class binding from firing. It is this class binding that inserts the tab character, which you don't want.
If you want this behavior for all text widgets in an application you can use the bind_class() method instead of bind() to make this binding affect all text widgets.
You can also have the binding send focus to a very specific widget but I recommend sticking with the default traversal order, then make sure the traversal order is correct.
It is really simple in PyQt4 simply use this one single line below and you will be able to change focus by pressing tab button:
self.textEdit.setTabChangesFocus(True)
The focus traversal is somewhat customizable, usually letting the X windows manager handle it (with focus follows mouse, or click). According to the manual it should be possible to bind an event to the key press event, for tab presses, and triggering a focusNext event in those cases.

Categories