Python event binding with tkinter - events

So guys I'm new to GUIs in Python and I've been trying to understand the envents process, here's my code, and the intention that whenever I press the 'a' key it should print "key pressed'. But it won't work for me.
#!/usr/bin/env python3
# -*-coding:UTF-8 -*
from tkinter import *
root = Tk()
def callback(event):
print("key pressed")
canvas = Canvas(root, width=100, height=100, bg='blue')
canvas.bind("a", callback)
canvas.pack()
root.mainloop()

It doesn't execute the callback function because the focus of the program is on the Tk element. If you replace that line with root.bind("a", callback), it will work as you expect.
The problem is that the canvas element doesn't receive the focus when you click on it like other widgets as Entry, so it will only respond to keydown events if you call first canvas.focus_set().

Related

QComboBox popup animation glitch

When using a QComboBox in PySide2 the popup menu seems to initially start about 10 pixels or so to the left until its finished animating down at which point it pops (about) 10 pixels to the right into the correct position.
How can I fix this? Or am I able to disable the animation so the menu just opens without animating? And am I able to control the animation time for the popup?
Here are two screenshots, the top one is while the combobox dropdown is animating down and the bottom one is after the dropdown is open:
Here's the simple example code use to produce the combobox above:
from PySide2 import QtCore, QtWidgets
import sys
class MyDialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(MyDialog, self).__init__(parent)
self.setWindowTitle('Modal Dialogs')
self.setMinimumSize(300,80)
# remove help icon (question mark) from window
self.setWindowFlags(self.windowFlags() ^ QtCore.Qt.WindowContextHelpButtonHint)
# create widgets, layouts and connections (signals and slots)
self.create_widgets()
self.create_layouts()
self.create_connections()
def create_widgets(self):
self.combo = QtWidgets.QComboBox()
self.combo.addItems(['one','two','three'])
def create_layouts(self):
# self must be passed to the main_layout so it is parented to the dialog instance
main_layout = QtWidgets.QVBoxLayout(self)
main_layout.addWidget(self.combo)
def create_connections(self):
pass
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
my_dialog = MyDialog()
my_dialog.show() # Show the UI
sys.exit(app.exec_())

GUI Tkinter method which returns a list of all the widgets currently packed with .pack()?

Is there a method in Tkinter (Python 3.2) which returns a list of all the widgets currently packed with .pack()?
I can't find anything in the documentation
There is a method exactly for this. Which is called pack_slaves()
import tkinter as tk
root = tk.Tk()
button = tk.Button(root, text="this is button")
label = tk.Label(root, text="this is label")
button.pack()
label.pack()
slaves = root.pack_slaves()
print (slaves)
root.mainloop()
This is output
>>> [<tkinter.Button object at 0x000000000325E160>, <tkinter.Label object at 0x000000000322C160>]
Also you might want to check this question. Accessing objects added to the Tkinter root

Pyside - Change entire GUI when button is pressed

I'm totally new to pyside and I'm having a problem with my little program (and pyside layouts in general).
What I have is an UI with some QlineEdits, comboboxes and a button. After I have filled out the Qlines and press the button I want to either to open a new window with a completely new layout or preferably clear out the open window and fill it with a new layout based on the input from the qlines. Perhaps this is super basic but I can't get it to work. The reason is that I can't grasp how I would be able to replace or add new stuff to my gui when it's already set and shown.
Let's say I have a script like this:
import sys
import os
from PySide import QtCore, QtGui
class BasicGui(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.projectNameLbl1 = QtGui.QLabel('Label1')
self.projectNameLbl2 = QtGui.QLabel('Label2')
self.nextBtn = QtGui.QPushButton("Next")
self.projectNameEdit = QtGui.QLineEdit(self)
self.projectNameEdit2 = QtGui.QLineEdit(self)
grid = QtGui.QGridLayout()
grid.setSpacing(10)
grid.addWidget(self.projectNameLbl1, 2, 0)
grid.addWidget(self.projectNameEdit, 2, 1)
grid.addWidget(self.projectNameLbl2, 3, 0)
grid.addWidget(self.projectNameEdit2, 3, 1)
grid.addWidget(self.nextBtn, 4, 1)
self.setLayout(grid)
self.setGeometry(300, 300, 350, 300)
self.setWindowTitle('projectCreator')
self.show()
self.nextBtn.clicked.connect(self.nextPressed)
def nextPressed(self):
self.msgBox = QtGui.QMessageBox()
self.msgBox.setText("When this button is pressed I want to generate a new layout")
self.msgBox.exec_()
def main():
app = QtGui.QApplication(sys.argv)
ex = BasicGui()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Say that I enter 10 in the line next to label1 and 2 in the other and press Next.Now I want to clear everything out and create 2 new columns with 10 qlines in each (or something like that).
Excuse me if I'm being either to vague or if I'm just repeating myself. I'm tired and irritated and English is not my first language.
I would deeply appreciate any help I could get or a push in the right direction.
Edit: If it's easier to accomplish this with some other widgetype with tabs or something that's fine. All i want to do is generate new widgets after i have recieved input from the user.
What you'll want to do is used a QStackedLayout[1].
Create a QWidget.
Create your layout.
Call setLayout() on the widget with your layout as the argument.
Push the new widget onto the QStackedLayout.
Use QStackedLayout's setCurrentIndex() or setCurrentWidget() functions to set the current layout.
I did something similar in a project of mine. See https://github.com/shanet/Cryptully/blob/master/cryptully/qt/qChatTab.py for a more complete example. Also see Pyside Changing Layouts for a similar problem.
[1] http://qt-project.org/doc/qt-4.8/qstackedlayout.html

wxPython ListCtrl OnClick Event

So, I have a wxPython ListCtrl which contains rows of data. How can I make an event that calls a function, with the row contents, when one of the rows if clicked on?
You can use the Bind function to bind a method to an event. For example,
import wx
class MainWidget(wx.Frame):
def __init__(self, parent, title):
super(MainWidget, self).__init__(parent, title=title)
self.list = wx.ListCtrl(parent=self)
for i,j in enumerate('abcdef'):
self.list.InsertStringItem(i,j)
self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnClick, self.list)
self.Layout()
def OnClick(self, event):
print event.GetText()
if __name__ == '__main__':
app = wx.App(redirect=False)
frame = MainWidget(None, "ListCtrl Test")
frame.Show(True)
app.MainLoop()
This app will print the item in the ListCtrl that is activated (by pressing enter or double-clicking). If you just want to catch a single click event, you could use wx.EVT_LIST_ITEM_SELECTED.
The important point is that the Bind function specifies the method to be called when a particular event happens. See the section in the wxPython Getting Started guide on event handling. Also see the docs on ListCtrl for the events that widget uses.

How to get the details of an event in wxPython

I have a section of code which returns events generated by a slider.
I bind the event with self.Bind(wx.EVT_SCROLL,self.OnSlide).
The code which handles the event reads something like:
def OnSlide(self,event):
widget = event.GetEventObject()
This is great but an error gets thrown every time the code is executed. It reads:
AttributeError: 'PyEventBinder' object has no attribute 'GetEventObject'
I want to be able to see which of the sliders generated the event but the error appears every time I attempt to find out.
How can I get the code to execute correctly?
Many thanks in advance.
To debug something like this, put the following as the first statement in your event handler:
import pdb; pdb.set_trace()
This will stop the execution of the program at this point and give you an interactive prompt. You can then issue the following command to find out what methods are available:
print dir(event)
When I was first learning wxPython I found this technique invaluable.
The following works for me on Windows 7, wxPython 2.8.10.1, Python 2.5
import wx
class MyForm(wx.Frame):
#----------------------------------------------------------------------
def __init__(self):
wx.Frame.__init__(self, None, title="Tutorial")
# Add a panel so it looks the correct on all platforms
panel = wx.Panel(self, wx.ID_ANY)
slider = wx.Slider(panel, size=wx.DefaultSize)
slider.Bind(wx.EVT_SLIDER, self.onSlide)
#----------------------------------------------------------------------
def onSlide(self, event):
""""""
obj = event.GetEventObject()
print obj
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm().Show()
app.MainLoop()

Resources