I have a wxPython gui with a grid. When a user clicks on certain cells, a drop-down menu will pop up.
self.window.Bind(wx.grid.EVT_GRID_SELECT_CELL, lambda event: self.on_left_click(event, self.grid, choices), self.grid)
The method on_left_click pulls up the drop-down menu and works as expected. The problem is that I want the default behavior for wx.grid.EVT_GRID_SELECT_CELL to happen first, and then for my custom method to fire. Normally, when you click on a grid cell, that cell's border gets bold. With my binding, the my drop-down menu appears first, and only once the drop-down menu is dismissed does the cell border get selected. I tried calling self.grid.SetGridCursor(row, col), but that generates a wx.grid.EVT_GRID_SELECT_CELL event, which causes a recursion max out and obviously is no good. I also tried using event.Skip() in the on_left_click code, but it doesn't seem to have an effect.
Here is the awkward, objectionable behavior (I just clicked on row 0, col 2):
Here is the desired behavior:
I am happy to provide more of the code if needed. I would like to do one of the following. One, somehow force wxPython to fully process wx.grid.EVT_GRID_SELECT_CELL before moving on to my custom drop-down menu. Two, manually duplicate the effect of "highlighting" the selected cell in my own code.
Thanks in advance for any help.
edit:
Here's what I ended up doing.
In place of wx.grid.EVT_GRID_SELECT_CELL, I used wx.grid.EVT_GRID_CELL_LEFT_CLICK which is essentially the first event to be fired when a user clicks on a grid cell, so I could fully customize the behavior following that initial click. Then, I could use this:
self.grid.SetGridCursor(row, col)
to outline the cell without creating the infinite recursion error, as well as implementing a bunch of other behavior that is now working more smoothly. I also needed to use self.grid.Refresh().
Create a separate method for showing drop down menu and call it with wx.CallAfter.
def on_left_click(self, event, grid, choices):
event.Skip()
self.grid.Refresh()
wx.CallAfter(self.showMenu, grid=grid, choices=choices)
def showMenu(self, grid=None, choices=None):
# Drop down menu code goes here
Related
My Current Project is to create a On Screen Keyboard for my personal usage with my personal functionality. I made gui and primary function in PyQt5 with Python. I managed to type the letter on button click with pyautogui.write() method. But the problem is, where I want to type there is no focus. suppose I want to write on chrome's address bar or any other input field on my monitor. when I click on button to type a letter, chrome lost focus. I want to set focus to the old window while press on any button. I searched on google about this but didn't found any answer. How can i set focus to old window? or is there any better way to type on focus lost state?
You should not try to "set the focus back", as it would be almost impossible to know what window had the focus before (and a new window might raise in the meantime). What you should actually do is to prevent your window to get focus at all, thus avoiding it to steal focus from the others.
In order to achieve this, you must set the appropriate window flag (or initialize the widget with it using the flags keyword argument), which for this is Qt.WindowDoesNotAcceptFocus.
Note that you might also want to set the Qt.WindowStaysOnTopHint in order to always keep your window above the others:
class MyKeyboard(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowFlags(
QtCore.Qt.WindowDoesNotAcceptFocus
| QtCore.Qt.WindowStaysOnTopHint
)
I am trying to get click events (ultimately left, right and double click) to "pass through" an editor widget to the underlying QListView so that selections can happen. Figured Event Filters were probably the way to go, but I am a little confused as to where the eventFilter(object,event) function and the installEventFilter() call need to go in order for that to happen.
In my case, I am using a custom delegate class to draw my data in my QListView and am using an editor to update the model based upon cursor positions. I want this to just be constantly active, so I made my QListView active the editor upon entering an item.
dataView=QListView(self)
dataView.setGeometry(self.rect())
dataView.setViewMode(1)
dataView.setMovement(0)
dataView.setSelectionMode(QAbstractItemView.ExtendedSelection)
dataView.setMouseTracking(True)
dataView.entered.connect(dataView.edit)
delegate=CustomDelegate(self)
dataModel=QStandardItemModel(self)
dataView.setModel(dataModel)
dataView.setItemDelegate(delegate)
The downside to this is that while your mouse is on an item, it is now covered by the editor widget which I believe is collecting mouse click data thus blocking clicks from selecting the items within the QListView.
In my delegate, I have it create the editor as so and connect signals to update my data (frames) in my model to change the way the delegate displays and to close the editor
def createEditor(self,parent,option,index):
editor=TestEditor(parent)
editor.editingFinished.connect(self.deleteEditor)
editor.updateFrame.connect(self.updateFrames)
return editor
Where would I create my event filter? In my main where I am generating my QListView? Within a custom QListView? Within my Delegate? Or within my editor widget? And then where would I call the installEventFilter()?
It turns out in my case, the QListView wasn't responding to selection clicks because when you call the edit() function, the state of the viewer changes to "QAbstractItemView.EditingState" and in this state, apparently selection is disabled. I just subclassed QListView and added a beginEdit function.
class CustomList(QListView):
def __init__(self,parent=None):
super().__init__(parent)
def beginEdit(self,index):
state=self.state()
self.edit(index)
self.setState(state)
And then just connected this instead of edit.
dataView=CustomList(self)
dataView.entered.connect(dataView.beginEdit)
I have two questions regarding tkinter that I cant seem to find the answer too.
1) I currently have 2 radio buttons. I have programmed them in such a way that when one of them is clicked another definition is called and that creates a regular button on the screen. Now my problem is, if the user toggles between the both radio buttons, each time a new button will be created (instead of just having 1 the first time he toggles the options). Is there any way I can stop the extra buttons from being created if one already exsists?
2) Is there a widget that can easily be used to create check box lists?
EDIT: Sorry, I meant check box list not radio button list.
1> Define you button outside the eventHandler of the radiobutton.
inside the event handler only grid/pack it,whenever the radiobutton is pressed. In this way the button will not be defined multiple times.
N.B. Use fix row & column with the grid if you need.
Suppose I have a lovely window full of tkinter widgets all set with a function. One of these many widgets is a button. When this button is pressed, I want to 'move on to the next screen'. The next screen is in another function(including all the widgets I want to appear on that screen). I have tried to simply run the next procedure from the button, but If it does run correctly, it only adds the widgets to the existing window, and you end up with both screen#1 and screen#2 jumbled together. I have a feeling I need to use destroy, but I'm not sure how to do such, as the only way I could come up with was to group all the widgets in window 1 together in a frame, and destroy it, but I cant get access to destroy the frame from within function #2, as its a variable only within function/window #1. Sorry if that's confusing, The other option is the source, but there's a ton of widgets and other windows in progress which leads me to believe that would be even more confusing.
The simplest thing is to have your function create a single frame, and then place all of the widgets in that frame. The frame can then be placed in the main window such that it fills the whole window. Then, to delete everything you simply need to delete that one frame.
Another way to "move on to the next screen" is to use this same method, but create all of the frames ahead of time. You can stack these frames on top of each other, and use lift and/or lower to determine which one is on top. The one on top will obscure the ones below.
For an example of stacking, see Switch between two frames in tkinter
As for the problem of frame2 not knowing how to destroy frame1, you simply need to pass in a reference to the existing frame when creating a new frame, or pass in a reference to a "controller" - a function that knows about all the frames. You then ask the controller to delete the current frame, and the controller will know what the current frame is.
A button calling a function that deletes all existing frames and rebuilds another sounds like a design flaw. The propensity for errors (forgetting to delete certain elements in some places of the code etc) is pretty large.
If you don't have an insane number of UI elements, I suggest creating them all at once, and hiding/showing various elements as necessary.
Take a look at this SO answer for how you might go about creating GUI elements that can be shown/hidden, and how the callback function might look.
Edit: If you really need to do it based on these functions, then I guess an alternative approach might be this:
Say 'top_frame' is the frame that includes all your widgets which you want to destroy when you run function #2. Change all of your GUI elements in function #1 so that when you create them, you explicitly pass them top_frame so that they have a link to it (self.top_frame = top_frame). This means your button will also have an attribute self.top_frame. You pass that as one of the arguments to function #2, and function #2 now can refer to top_frame and destroy it.
But definitely prone to error and probably slower due to all the creation/destruction of GUI elements. I recommend going through the code in the answer above when you have the time, it really is a much better solution.
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.