How to handle several keys pressed at the same time in Kivy? - python-2.7

I was planning on doing a little crossplatform game with kivy and, when I was testing the way to get input from the keyboard on the pc, I had a little surprise.
Kivy doesn't seem to handle several keys pressed at same with it's on_keyboard_down events, when you press more than one key at same in kivy, the keyboard class used in the official documentation passes the last pressed key in change of all the keys being pressed at the moment.
It looks like the keyboard class is designed to let the user type in the app because when you press a key for seconds, there's a little delay between the first key event and the rest of them (which are finally one per step), just like the one you can notice when typing.
This is the code I wrote for a keyboard-interactive hello world.
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.scatter import Scatter
from kivy.core.window import Window
class MyApp(App):
def __init__(self, **kwargs):
super(MyApp, self).__init__(**kwargs)
self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
self._keyboard.bind(on_key_down = self._on_keyboard_down)
def _keyboard_closed(self):
self._keyboard.unbind(on_key_down = self._on_keyboard_down)
self._keyboard = None
def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
print str(keyboard)+' '+str(keycode[1])+' '+str(text)+' '+str(modifiers)
if keycode[1] == 'w':
self.moveable.y += 1
if keycode[1] == 's':
self.moveable.y -= 1
if keycode[1] == 'd':
self.moveable.x += 1
if keycode[1] == 'a':
self.moveable.x -= 1
def build(self):
self.moveable = Scatter()
self.moveable.add_widget( Label(text = 'Hello moving world!') )
return self.moveable
if __name__ == '__main__':
MyApp().run()
Because of what I wrote before, this doesn't seem like the kind of keyboard input I'd use for a video game, and I haven't found anywhere how to get it better for that purpose.
Sorry if the question is inappropiate for some reason, I tried to find the answer to this, but I couldn't, so here it is.
Thanks you a lot in advance if you can help with it.

It looks like the keyboard class is designed to let the user type in the app because when you press a key for seconds, there's a little delay between the first key event and the rest of them
(Edit: Having reread your post, I think you don't want to change this at all and have misunderstood how to work with keyboard input, see the second part of this post)
I'm pretty sure this is down to operating system settings, not to do with Kivy. There's plenty of discussion about changing this value online if you want to, for instance on linux you can probably do xset r rate [delay] [rate], e.g. xset r rate 200 25, or in various desktop environments there's a gui setting for it. Similar things are true on windows. The keyword seems to be 'repeat delay'.
That said, I'm not sure why this actually matters. Kivy tells you when the key is pressed and when it is released, and you can perform an event however often you like during that time if you want to. Why does it matter whether the os keeps sending you extra keypresses?
Kivy doesn't seem to handle several keys pressed at same with it's on_keyboard_down events, when you press more than one key at same in kivy, the keyboard class used in the official documentation passes the last pressed key in change of all the keys being pressed at the moment.
I think you might be misunderstanding how Kivy is presenting you this information. When a key is pressed, kivy passes you an on_key_down event for that key. When it is released, kivy passes an on_key_up event for that key (your program doesn't do anything with this). In between those events you know the key is still pressed, it doesn't matter whether the system keeps feeding you fake keypresses.
Edit: Here is a modified version of your program that prints on_key_up event information as well:
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.scatter import Scatter
from kivy.core.window import Window
class MyApp(App):
def __init__(self, **kwargs):
super(MyApp, self).__init__(**kwargs)
self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
self._keyboard.bind(on_key_down = self._on_keyboard_down)
self._keyboard.bind(on_key_up = self._on_keyboard_up)
def _keyboard_closed(self):
self._keyboard.unbind(on_key_down = self._on_keyboard_down)
self._keyboard = None
def _on_keyboard_down(self, *args):
print 'down', args
def _on_keyboard_up(self, *args):
print 'up', args
def build(self):
self.moveable = Scatter()
self.moveable.add_widget( Label(text = 'Hello moving world!') )
return self.moveable
if __name__ == '__main__':
MyApp().run()

Related

How to disable OnClick() event when user clicks on winform created by Ironpython?

I'm writing an Ironpython 2.7.5 code to display Winform and take inputs from the user. Whenever i accidentally clicks on form, it throws error "OnClick() takes exactly 3 arguments (2 given)". How shall i disable this event?
I've used Notepad++ to write my code and running it in ANSYS tool. Below is the structure of my code:
def init(context):
#Something here
def OpenForm1(analysis_obj):
form = SimpleTextBoxForm()
Application.Run(form)
class SimpleTextBoxForm(Form):
def __init__(self):
self.Text = 'Material Tool V1.0'
self.Size = Size(Width, Height)
self.MaximizeBox = False
self.MinimizeBox = False
self.FormBorderStyle = FormBorderStyle.FixedDialog
#Some other controls
def OnChanged(self, sender, event): #Triggered on change
#Some Code
def OnClick(self, sender, event): #Triggered on button clicks
try:
#Something here
except:
#Something here
I want to stop this event from triggering wherever i click on form. But i am unable to.
I figured it out. I was using conflicting name in the click event
def OnClick()
which i changed to some other name and it worked fine.
If anyone runs out in similar problems, check the user defined method names which may be same as inbuilt ones.

PyQt :Parent Window not waiting until child window closes.

PyQt :Parent Window not waiting until child window closes. with reference to code shared below ,My welcome class object should wait till first_time class object completely finishes executing , but instead goes ahead and closes it self before first_time object finishes executing .
code :
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PySide.QtCore import QSettings
import sys
from PyQt4 import uic
#importing first configuration class
import configure as config_first
#loading initial settings
settings=QSettings('settings.ini',QSettings.IniFormat)
#loading the ui screens
form_class=uic.loadUiType("screens/firstscreen.ui")[0]
class welcome(QDialog,form_class):
#this signal is emitted when first configuration is done and ready to go
done_and_go_to_use = pyqtSignal()
def __init__(self):
super(welcome, self).__init__()
self.setupUi(self)
self.done_and_go_to_use.connect(self.close)
self.ready_btn.clicked.connect(self.ready)
def ready(self):
if_configured = settings.value('isConfigured', False)
if not if_configured :
first_time=config_first.configureFirst(self)
first_time.show()
self.close()
app = QApplication(sys.argv)
p = welcome()
p.show()
app.exec_()
below is the code for configure.py
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sqlite3
import csv
from PySide.QtCore import QSettings
from PyQt4 import uic
#loading initial settings
settings=QSettings('settings.ini',QSettings.IniFormat)
#loading ui screens
form_class=uic.loadUiType("screens/config_first.ui")[0]
#database connecting
con = sqlite3.connect("local.db")
cur = con.cursor()
cur.execute("CREATE TABLE if not exists marks (student_id int,student_name varchar(200));")
class configureFirst(QDialog,form_class):
done_configuring=pyqtSignal()
try_again=pyqtSignal()
def __init__(self,parent=None):
super(configureFirst, self).__init__(parent)
self.setupUi(self)
self.ok_btn.clicked.connect(self.ok_clicked)
self.cancel_btn.clicked.connect(self.cancel_clicked)
self.try_again.connect(self.ok_clicked)
self.done_configuring.connect(self.cancel_clicked)
self.show()
def ok_clicked(self):
file_select=QFileDialog.getOpenFileName(self,"open file","/")
if file_select:
with open(file_select, 'rb') as f:
reader = csv.reader(f)
ed = list(reader)
for row in ed:
if "name" not in row or "id" not in row:
cur.execute("Insert into marks Values (?,?);",(row[0],row[1]))
con.commit()
settings.setValue("isConfigured",True)
self.done_configuring.emit()
else:
#if recurssion is used the no of time it has to close increases and leads to integration problems
self.try_again.emit()
def cancel_clicked(self):
if_configured=settings.value("isConfigured")
if if_configured:
self.close()
else:
QMessageBox.critical(self,"PerfAnalyser","You Need to Configure For PerfAnalyser To Work")
def closeEvent(self,event):
#this method is triggered when 'X' is clicked i.e close button is clicked at the upper right corner
if_configured = settings.value("isConfigured")
if if_configured:
event.accept()
else:
QMessageBox.critical(self, "PerfAnalyser", "You Need to Configure For PerfAnalyser To Work")
event.ignore()
Thanks for the help in advance ...
I will try to help out since I notice few people have seen your post. I have had this happen a long time ago so I need a reminder, but I was unable to get your code running, I also tried to recreate your ui files and the screens directory but I was not successful. However, maybe the following is still useful.
In my working code, any time I needed to create a subwindow, I executed subwindows as follows from the main window's module:
dlg = SubWindowModuleName.StartSub()
dlg.exec_()
This will execute the subwindow and waits for it to close. Then, on the subwindow module (SubWindowModuleName in the above code, "configure" for you), I did this:
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName(_fromUtf8("Dialog"))
Dialog.resize(982, 521)
... # here I build the window (I noticed that you import UI files which is a much better way of doing this)
...
class StartSub(QtGui.QDialog, Ui_Dialog):
def __init__(self,parent=None):
QtGui.QDialog.__init__(self,parent)
self.setupUi(self)

PyQt 4: Get Position of Toolbar

Hy guys,
in my executable program there is a toolbar. Well, the user decides to move the toolbar. Now the toolbar is floating. I know I have to conntect the floating-signals that is emittted when the toolbar ist arranged by the user. How can I save the new position of the toolbar? I know the method of adding the toolbar to the main window with a position:self.addToolBar( Qt.LeftToolBarArea , toolbar_name). In the handle_floating()-method you see what I want: There I want to get the position currently, but how? You also see I have just added one member variable, named self.toolbar_pos, to hold the position of the toolbar. My idea is, when application is terminated I want to serialize this value to a file, and later, when application is ran again its will read that file and set the toolbar accordingly. But this is no problem. Currently I don't have no idea to get the position of the toolbar.
I need your help :)
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtGui, QtCore
class Example(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.toolbar_pos = None
self.initUI()
def initUI(self):
exitAction = QtGui.QAction(QtGui.QIcon('exit24.png'), 'Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.triggered.connect(QtGui.qApp.quit)
self.toolbar = QtGui.QToolBar(self)
self.toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
self.addToolBar(self.toolbar )
self.toolbar.addAction(exitAction)
self.toolbar.setAllowedAreas(QtCore.Qt.TopToolBarArea
| QtCore.Qt.BottomToolBarArea
| QtCore.Qt.LeftToolBarArea
| QtCore.Qt.RightToolBarArea)
self.addToolBar( QtCore.Qt.LeftToolBarArea , self.toolbar )
self.toolbar.topLevelChanged.connect(self.handle_floating)
def handle_floating(self, event):
# The topLevel parameter is true
# if the toolbar is now floating
if not event:
# If the toolbar no longer floats,
# then calculate the position where the
# toolbar is located currently.
self.toolbar_pos = None
print "Get position: ?"
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
ex.setGeometry(300, 300, 300, 200)
ex.setWindowTitle('Toolbar example')
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The QMainWindow class already has APIs for this: i.e. saveState and restoreState. These can be used to save and restore the state of all the toolbars and dock-widgets in your application.
To use them, you first need to make sure that all your toolbars and dock-widgets are given a unique object-name when they are created:
class Example(QtGui.QMainWindow):
...
def initUI(self):
...
self.toolbar = QtGui.QToolBar(self)
self.toolbar.setObjectName('foobar')
Then you can override closeEvent to save the state:
class Example(QtGui.QMainWindow):
...
def closeEvent(self, event):
with open('/tmp/test.conf', 'wb') as stream:
stream.write(self.saveState().data())
(NB: I've just used a temporary file here for testing, but it would obviously be much better to use something like QSettings in your real application).
Finally, you can restore the state that was saved previously:
class Example(QtGui.QMainWindow):
def __init__(self, parent=None):
...
self.initUI()
try:
with open('/tmp/test.conf', 'rb') as stream:
self.restoreState(QtCore.QByteArray(stream.read()))
except IOError:
pass

Binding keyboard shortcuts to GLCanvas in wxPython

Is it possible to bind keyboard shortcuts (like Ctrl+z to Undo, Ctrl+Shift+z to Redo, Ctrl+c, Ctrl+v, etc.) to GLCanvas in wxpython? If so, can someone provide a minimal example, maybe something like drawing a sphere on single-click and then Undo and Redo with a shortcut?
import wx
from wx import glcanvas
class myFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='test')
self.gl = MyCanvasBase(self)
self.Show()
class MyCanvasBase(glcanvas.GLCanvas):
def __init__(self, parent):
glcanvas.GLCanvas.__init__(self, parent, -1, style=wx.WANTS_CHARS)
self.Bind(wx.EVT_CHAR, self.OnKeyDown) # for wx.WANTS_CHARS
# self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
def OnKeyDown(self, evt):
keycode = evt.GetKeyCode()
print keycode
if __name__ == '__main__':
app = wx.App(0)
Frame = myFrame()
app.MainLoop()
app.Destroy()
(edit) control+c etc can be captured by setting window style to wx.WANTS_CHARS. I made changes to use that style.
This is my attempt to make a minimum example of capturing key down events but without cube.
Have you looked at wxphython demo? That is one of the best learning sources of wxpython. If you install wxPython2.8-win32-docs-demos-2.8.12.1.exe, you will find an OpenGL sample code with a cube that you can rotate with mouse.

gobject.io_add_watch continuous callback from pyalsaaudio

I'm trying to create a small custom mixer that suits my needs, using python 2.7 and pyalsaaudio 0.7, but I'm stuck with events got from alsamixer when another program changes the volume values. I tried to understand how other mixers work (for example volti) and, as far as I understand, it should work as expected, but even if the method is similar, I still get a continuous loop of event response from io_add_watch. So, I suppose that I don't understand how io_add_watch works yet.
This is a small version of the code:
class MyMixer(gtk.Window):
def __init__(self):
super(MyMixer, self).__init__()
self.m = alsaaudio.Mixer(control='Headphone', id=1, cardindex=0)
""" here go the mixer widgets """
self.show_all()
fd, event = self.m.polldescriptors()[0]
self.watch = gobject.io_add_watch(fd, event, self.update)
def update(self, *args):
print 'changed'
""" here I update mixer widgets """
return True
mixer = MyMixer()
gtk.main()
What am I getting wrong?
When you get an event from the poll descriptors, you must call snd_mixer_handle_events().
pyalsaaudio has no mechanism for that.