How to use winapi SetWinEventHook in python? - python

I want to get the handle of every new Dialog which pops up from a specific application.
I understand I should set a hook with SetWinEventHook which is in user32.dll in windows, but I don't know how to do that in python. Would you give me an example ?

Here's a very simple example that prints to the console the window text for each dialog that is opened:
import sys
import time
import ctypes
import ctypes.wintypes
EVENT_SYSTEM_DIALOGSTART = 0x0010
WINEVENT_OUTOFCONTEXT = 0x0000
user32 = ctypes.windll.user32
ole32 = ctypes.windll.ole32
ole32.CoInitialize(0)
WinEventProcType = ctypes.WINFUNCTYPE(
None,
ctypes.wintypes.HANDLE,
ctypes.wintypes.DWORD,
ctypes.wintypes.HWND,
ctypes.wintypes.LONG,
ctypes.wintypes.LONG,
ctypes.wintypes.DWORD,
ctypes.wintypes.DWORD
)
def callback(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime):
length = user32.GetWindowTextLengthA(hwnd)
buff = ctypes.create_string_buffer(length + 1)
user32.GetWindowTextA(hwnd, buff, length + 1)
print buff.value
WinEventProc = WinEventProcType(callback)
user32.SetWinEventHook.restype = ctypes.wintypes.HANDLE
hook = user32.SetWinEventHook(
EVENT_SYSTEM_DIALOGSTART,
EVENT_SYSTEM_DIALOGSTART,
0,
WinEventProc,
0,
0,
WINEVENT_OUTOFCONTEXT
)
if hook == 0:
print 'SetWinEventHook failed'
sys.exit(1)
msg = ctypes.wintypes.MSG()
while user32.GetMessageW(ctypes.byref(msg), 0, 0, 0) != 0:
user32.TranslateMessageW(msg)
user32.DispatchMessageW(msg)
user32.UnhookWinEvent(hook)
ole32.CoUninitialize()

Related

Pyinstaller misses modules for packaging an OpenGL application

I am writing a PyQt5 application that uses OpenGL to slice an STL into images and displays them as it goes. The application works fine when invoked from the command line (Windows 10, Python 3.7.1, PyInstaller 3.5), but when I try packaging it with pyinstaller it crashes with an error:
Traceback (most recent call last):
File "app_qt.py", line 45, in initializeGL
self.gl = self.context().versionFunctions()
ModuleNotFoundError: No module named 'PyQt5._QOpenGLFunctions_4_1_Core'`
The call comes from a QtGui.QOpenGLWindow object. I have followed the suggestions in this answer to no avail. I have tried importing PyQt5 directly, and adding it to the hidden imports in the .spec file as well for pyinstaller.
Since the application runs fine when invoked normally (i.e. python slicer_gui.py) I am inclined to believe pyinstaller is neglecting to add an import to the package somewhere.
Here is the full code. The GUI is simple (pun intended):
import PySimpleGUI as sg
import app_pyopengl
# define GUI layout
layout = [
[sg.Text('STL to Slice', size=(16, 1)), sg.Input(), sg.FileBrowse()],
[sg.Text('Layer Thickness (um) ', size=(16, 1)), sg.InputText('10')],
[sg.Submit(button_text="Slice"), sg.Cancel(button_text='Quit')]
]
# name the window
window = sg.Window('PyQT STL Slicer').Layout(layout)
# loop until user quits
while True:
button, values = window.Read() # read all values in the window
if button == "Quit":
exit()
if button == "Slice":
thickness = float(values[1]) / 1000 # convert um to mm
filename = values[0]
app_pyopengl.main(filename, thickness)
Here is the slicing application:
import sys
import os
import shutil
from ctypes import c_float, c_uint, sizeof
from PyQt5 import QtGui, QtCore, QtWidgets
from stl import mesh
import numpy as np
from printer import printer
GLfloat = c_float
GLuint = c_uint
EPSILON = 0.00001
SCR_WIDTH = 640
SCR_HEIGHT = int(SCR_WIDTH * printer.height / printer.width)
class Window(QtGui.QOpenGLWindow):
def __init__(self,
stlFilename,
layerThickness,
sliceSavePath,
*args,
**kwargs):
super().__init__(*args, **kwargs)
self.setTitle('STL Slicer')
self.vertVAO, self.vertVBO = 0, 0
self.maskVAO, self.maskVBO = 0, 0
self.numOfVerts = 0
self.bounds = dict()
self.totalThickness = 0.
self.currentLayer = 0
self.height = 0
self.stlFilename = stlFilename
self.layerThickness = layerThickness
self.sliceSavePath = sliceSavePath
def initializeGL(self):
self.gl = self.context().versionFunctions()
self.shaderProg = QtGui.QOpenGLShaderProgram()
self.shaderProg.create()
self.shaderProg.addShaderFromSourceFile(
QtGui.QOpenGLShader.Vertex, 'shaders/slice.vert')
self.shaderProg.addShaderFromSourceFile(
QtGui.QOpenGLShader.Fragment, 'shaders/slice.frag')
self.shaderProg.link()
self.loadMesh()
self.proj = QtGui.QMatrix4x4()
self.proj.setToIdentity()
self.proj.ortho(0, printer.width*printer.pixel,
0, printer.height*printer.pixel,
-self.totalThickness, self.totalThickness)
self.model = QtGui.QMatrix4x4()
self.model.setToIdentity()
self.model.translate(0, 0, self.totalThickness+EPSILON)
self.sliceFbo = QtGui.QOpenGLFramebufferObject(
printer.width,
printer.height
)
self.sliceFbo.setAttachment(
QtGui.QOpenGLFramebufferObject.CombinedDepthStencil
)
def loadMesh(self):
# Get information about our mesh
ourMesh = mesh.Mesh.from_file(self.stlFilename)
self.numOfVerts = ourMesh.vectors.shape[0] * 3
self.bounds = {
'xmin': np.min(ourMesh.vectors[:,:,0]),
'xmax': np.max(ourMesh.vectors[:,:,0]),
'ymin': np.min(ourMesh.vectors[:,:,1]),
'ymax': np.max(ourMesh.vectors[:,:,1]),
'zmin': np.min(ourMesh.vectors[:,:,2]),
'zmax': np.max(ourMesh.vectors[:,:,2])
}
self.totalThickness = self.bounds['zmax'] - self.bounds['zmin']
#######################################
# make VAO for drawing our mesh
self.vertVAO = QtGui.QOpenGLVertexArrayObject()
self.vertVAO.create()
self.vertVAO.bind()
self.vertVBO = QtGui.QOpenGLBuffer(QtGui.QOpenGLBuffer.VertexBuffer)
self.vertVBO.create()
self.vertVBO.bind()
self.vertVBO.setUsagePattern(QtGui.QOpenGLBuffer.StaticDraw)
data = ourMesh.vectors.astype(GLfloat).tostring()
self.vertVBO.allocate(data, len(data))
self.gl.glVertexAttribPointer(0, 3, self.gl.GL_FLOAT,
self.gl.GL_FALSE, 3*sizeof(GLfloat), 0)
self.gl.glEnableVertexAttribArray(0)
self.vertVBO.release()
self.vertVAO.release()
#######################################
# a mask vertex array for stencil buffer to subtract
maskVert = np.array(
[[0, 0, 0],
[printer.width*printer.pixel, 0, 0],
[printer.width*printer.pixel, printer.height*printer.pixel, 0],
[0, 0, 0],
[printer.width*printer.pixel, printer.height*printer.pixel, 0],
[0, printer.height*printer.pixel, 0]], dtype=GLfloat
)
#######################################
# make VAO for drawing mask
self.maskVAO = QtGui.QOpenGLVertexArrayObject()
self.maskVAO.create()
self.maskVAO.bind()
self.maskVBO = QtGui.QOpenGLBuffer(QtGui.QOpenGLBuffer.VertexBuffer)
self.maskVBO.create()
self.maskVBO.bind()
self.maskVBO.setUsagePattern(QtGui.QOpenGLBuffer.StaticDraw)
data = maskVert.tostring()
self.maskVBO.allocate(data, len(data))
self.gl.glVertexAttribPointer(0, 3, self.gl.GL_FLOAT,
self.gl.GL_FALSE, 3*sizeof(GLfloat), 0)
self.gl.glEnableVertexAttribArray(0)
self.maskVBO.release()
self.maskVAO.release()
#######################################
def paintGL(self):
if self.height >= self.totalThickness-EPSILON:
sys.exit()
else:
self.height += self.layerThickness
self.currentLayer += 1
self.draw()
self.renderSlice()
self.update()
def draw(self):
self.gl.glViewport(0, 0, self.size().width(), self.size().height())
self.gl.glEnable(self.gl.GL_STENCIL_TEST)
self.gl.glClearColor(0., 0., 0., 1.)
self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT | self.gl.GL_STENCIL_BUFFER_BIT)
self.vertVAO.bind()
self.shaderProg.bind()
self.model.translate(0, 0, -self.layerThickness)
self.shaderProg.setUniformValue('proj', self.proj)
self.shaderProg.setUniformValue('model', self.model)
self.gl.glEnable(self.gl.GL_CULL_FACE)
self.gl.glCullFace(self.gl.GL_FRONT)
self.gl.glStencilFunc(self.gl.GL_ALWAYS, 0, 0xFF)
self.gl.glStencilOp(self.gl.GL_KEEP, self.gl.GL_KEEP, self.gl.GL_INCR)
self.gl.glDrawArrays(self.gl.GL_TRIANGLES, 0, self.numOfVerts)
self.gl.glCullFace(self.gl.GL_BACK)
self.gl.glStencilOp(self.gl.GL_KEEP, self.gl.GL_KEEP, self.gl.GL_DECR)
self.gl.glDrawArrays(self.gl.GL_TRIANGLES, 0, self.numOfVerts)
self.gl.glDisable(self.gl.GL_CULL_FACE)
self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT)
self.maskVAO.bind()
self.gl.glStencilFunc(self.gl.GL_NOTEQUAL, 0, 0xFF)
self.gl.glStencilOp(self.gl.GL_KEEP, self.gl.GL_KEEP, self.gl.GL_KEEP)
self.gl.glDrawArrays(self.gl.GL_TRIANGLES, 0, 6)
self.gl.glDisable(self.gl.GL_STENCIL_TEST)
self.shaderProg.release()
def renderSlice(self):
self.sliceFbo.bind()
self.gl.glViewport(0, 0, printer.width, printer.height)
self.gl.glEnable(self.gl.GL_STENCIL_TEST)
self.gl.glClearColor(0., 0., 0., 1.)
self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT | self.gl.GL_STENCIL_BUFFER_BIT)
self.vertVAO.bind()
self.shaderProg.bind()
self.shaderProg.setUniformValue('proj', self.proj)
self.shaderProg.setUniformValue('model', self.model)
self.gl.glEnable(self.gl.GL_CULL_FACE)
self.gl.glCullFace(self.gl.GL_FRONT)
self.gl.glStencilFunc(self.gl.GL_ALWAYS, 0, 0xFF)
self.gl.glStencilOp(self.gl.GL_KEEP, self.gl.GL_KEEP, self.gl.GL_INCR)
self.gl.glDrawArrays(self.gl.GL_TRIANGLES, 0, self.numOfVerts)
self.gl.glCullFace(self.gl.GL_BACK)
self.gl.glStencilOp(self.gl.GL_KEEP, self.gl.GL_KEEP, self.gl.GL_DECR)
self.gl.glDrawArrays(self.gl.GL_TRIANGLES, 0, self.numOfVerts)
self.gl.glDisable(self.gl.GL_CULL_FACE)
self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT)
self.maskVAO.bind()
self.gl.glStencilFunc(self.gl.GL_NOTEQUAL, 0, 0xFF)
self.gl.glStencilOp(self.gl.GL_KEEP, self.gl.GL_KEEP, self.gl.GL_KEEP)
self.gl.glDrawArrays(self.gl.GL_TRIANGLES, 0, 6)
self.gl.glDisable(self.gl.GL_STENCIL_TEST)
image = self.sliceFbo.toImage()
# makes a QComboBox for different Image Format,
# namely Format_Mono, Format_MonoLSB, and Format_Grayscale8
image = image.convertToFormat(QtGui.QImage.Format_Grayscale8)
image.save(os.path.join(self.sliceSavePath,
'out{:04d}.png'.format(self.currentLayer)))
self.sliceFbo.release()
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Escape:
sys.exit()
event.accept()
def main(stlFilename, layerThickness):
temp = os.path.dirname(stlFilename)
sliceSavePath = os.path.join(temp, 'slices')
# remove old slices folder if there is one, and make a new empty one
if os.path.exists(sliceSavePath):
shutil.rmtree(sliceSavePath) # using shutil here avoids permissions errors
if not os.path.exists(sliceSavePath):
os.mkdir(sliceSavePath)
# Set format here, otherwise it throws error
# `QCocoaGLContext: Falling back to unshared context.`
# on Mac when use QOpenGLWidgets
# https://doc.qt.io/qt-5/qopenglwidget.html#details last paragraph
format = QtGui.QSurfaceFormat()
format.setRenderableType(QtGui.QSurfaceFormat.OpenGL)
format.setProfile(QtGui.QSurfaceFormat.CoreProfile)
format.setVersion(4, 1)
format.setStencilBufferSize(8)
QtGui.QSurfaceFormat.setDefaultFormat(format)
app = QtWidgets.QApplication(sys.argv)
window = Window(stlFilename, layerThickness, sliceSavePath)
window.resize(SCR_WIDTH, SCR_HEIGHT)
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main(sys.argv[1], float(sys.argv[2]))

API Exporting Issue

I need someone's expertise on this exporting problem of mine.
How it works: Select a camera (animated or not is optional) >> File >> Export Selection >> File Type : .chan (need to load this script as a plugin)
Here's where the problem starts. It is able to create a .text file, however, it is not 'exporting' or writing out the contents into the text file and the file size is of zero bytes.
I am making use of the current API that it has been coded, modifying the code to add in some maya cmds
Can someone kindly help me out?
import math, sys, string, os
import maya.OpenMaya as OpenMaya
import maya.OpenMayaMPx as OpenMayaMPx
import maya.OpenMayaAnim as OpenMayaAnim
import maya.cmds as cmds
import maya.mel as mel
kPluginTranslatorTypeName = "chan Export/Import"
kVersionNumber = "0.5a"
camSel = []
win_name = "chan_window"
class CustomNodeTranslator(OpenMayaMPx.MPxFileTranslator):
def __init__(self):
OpenMayaMPx.MPxFileTranslator.__init__(self)
def haveWriteMethod(self):
return True
def haveReadMethod(self):
return True
def filter(self):
return " .chan"
def defaultExtension(self):
return "chan"
def writer( self, fileObject, optionString, accessMode ):
try:
fullName = fileObject.fullName()
fileHandle = open(fullName,"w")
selectList = OpenMaya.MSelectionList()
OpenMaya.MGlobal.getActiveSelectionList(selectList)
node = OpenMaya.MObject()
depFn = OpenMaya.MFnDependencyNode()
path = OpenMaya.MDagPath()
iterator = OpenMaya.MItSelectionList(selectList)
animationTime = OpenMayaAnim.MAnimControl()
maxTime = int(animationTime.maxTime().value())
minTime = int(animationTime.minTime().value())
while (iterator.isDone() == 0):
iterator.getDependNode(node)
depFn.setObject(node)
iterator.getDagPath(path, node)
cameraObject = OpenMaya.MFnCamera(path)
transform = OpenMaya.MFnTransform(path)
chanMe = fileExporter(transform, minTime, maxTime, cameraObject)
for all in chanMe():
fileHandle.write(all)
iterator.next()
fileHandle.close()
except:
sys.stderr.write( "Failed to write file information\n")
raise
def processLine( self, lineStr ):
self.importTheChan.writeFrameData(lineStr)
class fileExporter():
""" module for exporting chan files from application. arguments: object, startFrame, endFrame """
def __init__(self, transform, startAnimation, endAnimation, cameraObj):
self.fileExport = []
self.transform = transform
self.cameraObj = cameraObj
self.start = startAnimation
self.end = endAnimation
self.exportWin()
def exportWin(self):
self.expWindow = cmds.window(w=150, h=100, title = "Export Selection" )
cmds.columnLayout( adjustableColumn=True )
form = cmds.formLayout(numberOfDivisions=100)
cmds.radioCollection()
self.chk1 = cmds.radioButton( label='option1', onc = self.opt1On, ofc = self.opt1Off )
self.chk2 = cmds.radioButton( label='option2', onc = self.opt2On, ofc = self.opt2Off )
self.okayBtn = cmds.button(label='okay!', command=self.runSel, width=150, height=35)
cmds.formLayout(form, edit=True, attachForm=[\
(self.chk1, 'top', 15),\
(self.chk1, 'left', 15),\
(self.chk2, 'top', 30),\
(self.chk2, 'left', 15),\
(self.okayBtn, 'top', 50),\
(self.okayBtn, 'left', 15)])
cmds.showWindow( self.expWindow )
def opt1On(self, args):
print "User checked option1"
startAnimation = cmds.playbackOptions(query=True, minTime=True)
endAnimation = cmds.playbackOptions(query=True, maxTime=True)
self.start = startAnimation
self.end = endAnimation
def opt1Off(self, args):
print "User un-checked option1"
cmds.radioButton(self.chk2, edit = True, enable = True)
self.start = ""
self.end = ""
def opt2On(self, args):
print "User checked option2"
startAnimation = cmds.findKeyframe(which='first')
endAnimation = cmds.findKeyframe(which='last')
self.start = startAnimation
self.end = endAnimation
#self.start.append(int(startAnimation))
#self.end.append(int(endAnimation))
def opt2Off(self, args):
print "User un-checked option2"
self.start = ""
self.end = ""
def runSel(self, args):
chkVal1 = cmds.radioButton(self.chk1, query=True, sl=1)
chkVal2 = cmds.radioButton(self.chk2, query=True, sl=1)
if chkVal1 == 1:
print "opt1 Pressed!"
print self.start
print self.end
self.test()
self.closeWindow()
elif chkVal2 == 1:
print "opt2 Pressed!"
print self.start
print self.end
self.test()
self.closeWindow()
else:
cmds.warning("Check an option")
def closeWindow(self):
cmds.deleteUI(self.expWindow, window=True)
def test(self):
self.actualExp(self.transform, self.start, self.end, self.cameraObj)
def actualExp(self, transform, startAnimation, endAnimation, cameraObj):
mayaGlobal = OpenMaya.MGlobal()
mayaGlobal.viewFrame(OpenMaya.MTime(1))
# Converts the float arguement into integer
for i in range(int(startAnimation), int(endAnimation + 1)):
focalLength = cameraObj.focalLength()
vFilmApp = cameraObj.verticalFilmAperture()
focalOut = 2 math.degrees(math.atan(vFilmApp 25.4/ (2 focalLength)))
myEuler = OpenMaya.MEulerRotation()
spc = OpenMaya.MSpace.kWorld
trans = transform.getTranslation(spc)
rotation = transform.getRotation(myEuler)
rotVector = OpenMaya.MVector(myEuler.asVector())
self.fileExport.append((str(i) + '\t' + str(trans[0]) + "\t" + str(trans[1]) + "\t" + str(trans[2]) + "\t" + str(math.degrees(rotVector[0])) + "\t" + str(math.degrees(rotVector[1])) + "\t" + str(math.degrees(rotVector[2])) + "\t" + str(focalOut) + "\n"))
mayaGlobal.viewFrame(OpenMaya.MTime(i+1))
def __call__(self, args):
return self.fileExport
def radianToDegree(self, radians):
outDegrees = 0.0
outDegrees = (float(radians) / (math.pi)) 180
return outDegrees
# creator
def translatorCreator():
return OpenMayaMPx.asMPxPtr( CustomNodeTranslator() )
# initialize the script plug-in
def initializePlugin(mobject):
mplugin = OpenMayaMPx.MFnPlugin(mobject)
try:
mplugin.registerFileTranslator(kPluginTranslatorTypeName, None, translatorCreator)
except:
sys.stderr.write( "Failed to register translator: %s" % kPluginTranslatorTypeName )
raise
# uninitialize the script plug-in
def uninitializePlugin(mobject):
mplugin = OpenMayaMPx.MFnPlugin(mobject)
try:
mplugin.deregisterFileTranslator( kPluginTranslatorTypeName )
except:
sys.stderr.write( "Failed to deregister translator: %s" % kPluginTranslatorTypeName )
raise
the __call__ method is what's supposed to provide the contents of the file. It returns self.fileExport, which is an empty list that is not getting populated.
The problem here is the writer method of the plugin will not wait for your exportWin UI to return the user inputs when you call
chanMe = fileExporter(transform, minTime, maxTime, cameraObject)
By the time the user has entered the inputs, the statements that follow have already been executed:
for all in chanMe():
fileHandle.write(all)
iterator.next()
fileHandle.close()
That is why plugin-based file exporters like these have their options UI tucked away in the option box. These options will be passed prior to call to the plugin's writer().
You will need to export your options UI code (in a certain specific format) using MEL in another script file and specify the name of that file in the optionsScriptName param of the registerFileTranslator call. There is a communication protocol that needs to be followed for communication between this options UI and the writer plugin itself. RobTheBloke's awesome post illustrates this process.
Ideally, the writer() method should have all the details it needs for computing and exporting without having to wait for user input.
Alternatively, if you prefer to have your own UI window and more control over the flow of things, you could write the exporter not as a plugin, but as a simple MEL/Python module. You could still use the power of the API.
Hope this helped!

Python capture Keystrokes values in text file on OS X

I am trying to monitor keystrokes on my Macbook on order to build a statistics analyzer. But how can I isolate the chars from "event" which is more something like :
NSEvent: type=KeyDown loc=(850,248) time=66551.8 flags=0x100 win=0x0
winNum=0 ctxt=0x0 chars="l" unmodchars="l" repeat=0 keyCode=37
So do anyone know how to, based on the script posted below, fulfill a .txt doc with the value of chars (from NSEvent) ? I need a text file with a the pressed keys in order to run my other script on it and analyze the frequency etc...
Thanks in advance ;)
#!/usr/bin/python
# -*- coding: utf-8 -*-
from AppKit import NSApplication, NSApp
from Foundation import NSObject, NSLog
from Cocoa import NSEvent, NSKeyDownMask
from PyObjCTools import AppHelper
class AppDelegate(NSObject):
def applicationDidFinishLaunching_(self, notification):
mask = NSKeyDownMask
NSEvent.addGlobalMonitorForEventsMatchingMask_handler_(mask, handler)
def handler(event):
try:
print event
except KeyboardInterrupt:
AppHelper.stopEventLoop()
def main():
app = NSApplication.sharedApplication()
delegate = AppDelegate.alloc().init()
NSApp().setDelegate_(delegate)
AppHelper.runEventLoop()
if __name__ == '__main__':
main()
#!/usr/bin/python2.6
# You _must_ turn on assistive devices under Accessibility prefpane
# for any of this code to work. Otherwise it won't do anything.
from Cocoa import *
from Foundation import *
from PyObjCTools import AppHelper
import keycode
import string
import sys
class AppDelegate(NSObject):
def applicationDidFinishLaunching_(self, aNotification):
NSEvent.addGlobalMonitorForEventsMatchingMask_handler_(NSKeyDownMask, handler)
def handler(event):
if event.type() == NSKeyDown and keycode.tostring(event.keyCode()) in string.printable:
print keycode.tostring(event.keyCode())
def main():
app = NSApplication.sharedApplication()
delegate = AppDelegate.alloc().init()
NSApp().setDelegate_(delegate)
AppHelper.runEventLoop()
if __name__ == '__main__':
main()
I dug around a bit and found github user 'gurgeh's OSX modifications for selfspy in his branch - https://github.com/gurgeh/selfspy/blob/new_activity/sniff_cocoa.py.
I put a few of my own ideas into it and this is the result. Kudos to gurgeh for figuring out how to capture window change events - this makes the key logger much more interesting since you could basically ignore apps that don't provide interesting events, or for the statistically minded, graph out which apps you use the most and what you're doing in them..
#!/usr/bin/python2.6
import exceptions
import sys
from Foundation import NSObject, NSLog
from AppKit import NSApplication, NSApp, NSWorkspace
from Cocoa import *
from Quartz import CGWindowListCopyWindowInfo, kCGWindowListOptionOnScreenOnly, kCGNullWindowID
from PyObjCTools import AppHelper
import keycode
evtypes = dict(
NSLeftMouseDown = 1,
NSLeftMouseUp = 2,
NSRightMouseDown = 3,
NSRightMouseUp = 4,
NSMouseMoved = 5,
NSLeftMouseDragged = 6,
NSRightMouseDragged = 7,
NSMouseEntered = 8,
NSMouseExited = 9,
NSKeyDown = 10,
NSKeyUp = 11,
NSFlagsChanged = 12,
NSAppKitDefined = 13,
NSSystemDefined = 14,
NSApplicationDefined = 15,
NSPeriodic = 16,
NSCursorUpdate = 17,
NSScrollWheel = 22,
NSTabletPoint = 23,
NSTabletProximity = 24,
NSOtherMouseDown = 25,
NSOtherMouseUp = 26,
NSOtherMouseDragged = 27
)
evtypes_rev = dict([[v,k] for k,v in evtypes.items()])
class Hooker(object):
def __call__(self, *args, **kwargs):
try:
evt = kwargs.get('event')
del kwargs['event']
items = ' '.join( [ x[0]+"="+unicode(x[1]) for x in kwargs.iteritems()] )
print "%20s | %22s | %s" % ( self.__class__.__name__, evtypes_rev[evt.type()], items)
except Exception as e:
print 'Horrific error!', e
AppHelper.stopEventLoop()
sys.exit(0)
class KeyHooker(Hooker): pass
class MouseButtonHooker(Hooker): pass
class MouseMoveHooker(Hooker): pass
class ScreenHooker(Hooker): pass
class SniffCocoa:
def __init__(self):
self.key_hook = KeyHooker()
self.mouse_button_hook = MouseButtonHooker()
self.mouse_move_hook = MouseMoveHooker()
self.screen_hook = ScreenHooker()
self.currentApp = None
def createAppDelegate (self) :
sc = self
class AppDelegate(NSObject):
def applicationDidFinishLaunching_(self, notification):
mask = (
NSKeyDownMask
| NSKeyUpMask
| NSLeftMouseDownMask
| NSLeftMouseUpMask
| NSRightMouseDownMask
| NSRightMouseUpMask
| NSMouseMovedMask
| NSScrollWheelMask
)
NSEvent.addGlobalMonitorForEventsMatchingMask_handler_(mask, sc.handler)
return AppDelegate
def run(self):
NSApplication.sharedApplication()
delegate = self.createAppDelegate().alloc().init()
NSApp().setDelegate_(delegate)
self.workspace = NSWorkspace.sharedWorkspace()
AppHelper.runEventLoop()
def cancel(self):
AppHelper.stopEventLoop()
def handler(self, event):
try:
activeApps = self.workspace.runningApplications()
for app in activeApps:
if app.isActive():
if app.localizedName() != self.currentApp:
self.currentApp = app.localizedName()
options = kCGWindowListOptionOnScreenOnly
windowList = CGWindowListCopyWindowInfo(options, kCGNullWindowID)
for window in windowList:
if window['kCGWindowOwnerName'] == self.currentApp:
geom = window['kCGWindowBounds']
self.screen_hook( event=event,
name = window['kCGWindowName'],
owner = window['kCGWindowOwnerName'],
x = geom['X'],
y = geom['Y'],
w = geom['Width'],
h = geom['Height'])
break
break
loc = NSEvent.mouseLocation()
# mouse clicky buttons
if event.type() in ( NSLeftMouseDown, NSRightMouseDown, NSLeftMouseUp, NSRightMouseUp):
self.mouse_button_hook(event=event, x=loc.x, y=loc.y)
# mouse scrolly buttons
elif event.type() == NSScrollWheel:
if event.deltaY() > 0 and event.deltaY() < 0:
self.mouse_button_hook(event=event, x=loc.x, y=loc.y)
if event.deltaX() > 0 and event.deltaX() < 0:
self.mouse_button_hook(event=event, x=loc.x, y=loc.y)
# keys down
elif event.type() in ( NSKeyDown, NSKeyUp ):
flags = event.modifierFlags()
modifiers = [] # OS X api doesn't care it if is left or right
if (flags & NSControlKeyMask):
modifiers.append('CONTROL')
if (flags & NSAlternateKeyMask):
modifiers.append('ALTERNATE')
if (flags & NSCommandKeyMask):
modifiers.append('COMMAND')
self.key_hook(event=event, key=event.keyCode(), char=keycode.tostring( event.keyCode() ), mods=modifiers, is_repeat=event.isARepeat())
# Mouse moved
elif event.type() == NSMouseMoved:
self.mouse_move_hook(event=event, x=loc.x, y=loc.y)
else:
pass
except ( KeyboardInterrupt ) as e:
print 'handler', e
AppHelper.stopEventLoop()
if __name__ == '__main__':
sc = SniffCocoa()
sc.run()
For using Keycode module. Just clone it from here and run "sudo setup.py install" . You will have keycode module
https://github.com/abarnert/pykeycode

python ctypes Win32 way window title gets truncted?

I been trying to create Win32 Application by using python (2.7) and ctypes module. Window is created and shown but title of window gets truncated. I got 'M' instead of 'My test window'. What I am doing wrong?
Thanks in advance
P.S. Here follows the code and screenshot:
# -*- coding: utf-8 -*-
from sys import platform, exit
from ctypes import *
from ctypes.wintypes import DWORD, HWND, HANDLE, LPCWSTR, WPARAM, LPARAM, RECT, POINT, MSG
WNDPROCTYPE = WINFUNCTYPE(c_int, HWND, c_uint, WPARAM, LPARAM)
WS_EX_APPWINDOW = 0x40000
WS_OVERLAPPEDWINDOW = 0xcf0000
WS_CAPTION = 0xc00000
SW_SHOWNORMAL = 1
SW_SHOW = 5
CS_HREDRAW = 2
CS_VREDRAW = 1
CW_USEDEFAULT = 0x80000000
WM_DESTROY = 2
WHITE_BRUSH = 0
class WNDCLASSEX(Structure):
_fields_ = [("cbSize", c_uint),
("style", c_uint),
("lpfnWndProc", WNDPROCTYPE),
("cbClsExtra", c_int),
("cbWndExtra", c_int),
("hInstance", HANDLE),
("hIcon", HANDLE),
("hCursor", HANDLE),
("hBrush", HANDLE),
("lpszMenuName", LPCWSTR),
("lpszClassName", LPCWSTR),
("hIconSm", HANDLE)]
def PyWndProcedure(hWnd, Msg, wParam, lParam):
if Msg == WM_DESTROY:
windll.user32.PostQuitMessage(0)
else:
return windll.user32.DefWindowProcA(hWnd, Msg, wParam, lParam)
return 0
WndProc = WNDPROCTYPE(PyWndProcedure)
hInst = windll.kernel32.GetModuleHandleW(0)
print(hInst)
wclassName = u'My Python Win32 Class'
wndClass = WNDCLASSEX()
wndClass.cbSize = sizeof(WNDCLASSEX)
wndClass.style = CS_HREDRAW | CS_VREDRAW
wndClass.lpfnWndProc = WndProc
wndClass.cbClsExtra = 0
wndClass.cbWndExtra = 0
wndClass.hInstance = hInst
wndClass.hIcon = 0
wndClass.hCursor = 0
wndClass.hBrush = windll.gdi32.GetStockObject(WHITE_BRUSH)
wndClass.lpszMenuName = 0
wndClass.lpszClassName = wclassName
wndClass.hIconSm = 0
print(wndClass)
regRes = windll.user32.RegisterClassExW(byref(wndClass))
print(regRes)
wname = u'My test window'
hWnd = windll.user32.CreateWindowExW(
0,
wclassName,
wname,
WS_OVERLAPPEDWINDOW | WS_CAPTION,
CW_USEDEFAULT,
CW_USEDEFAULT,
300,
300,
0,
0,
hInst,
0)
print('hWnd', hWnd)
if not hWnd:
print('Failed to create window')
exit(0)
print('ShowWindow', windll.user32.ShowWindow(hWnd, SW_SHOW))
print('UpdateWindow', windll.user32.UpdateWindow(hWnd))
msg = MSG()
lpmsg = pointer(msg)
print('Entering message loop')
while windll.user32.GetMessageA(lpmsg, 0, 0, 0) != 0:
windll.user32.TranslateMessage(lpmsg)
windll.user32.DispatchMessageA(lpmsg)
print('done.')
It is because you are creating a Unicode window with CreateWindowExW but then calling the ANSI DefWindowProcA. You are passing Unicode strings which typically have zero for every other byte since your text is in the ASCII range which explains what you observe.
The solution? Call DefWindowProcW instead.
Actually, a better solution would be to use win32gui instead which wraps this up a bit more for you.
If you provide a regular string rather than an unicode string, the text is displayed properly.
wname = 'My test window'
It looks strange to me because you are using the Unicode API (CreateWindowExW). Maybe the root cause is somewhere else.
I hope it helps

open a string in notepad at runtime in python

I have a string called 's' and I want to open it in notepad at runtime without saving it in/as a file. Is there any way to achieve this in python?
There is an example here.
#### Script to try to write something down in notepad
import win32api
import win32gui
import win32con
import time
import subprocess
#start notepad.exe asynchronously
subprocess.Popen('Notepad.exe')
# get the window handle of the blank, minimized notepad window
hwnd = win32gui.FindWindowEx(0, 0, 0, "Untitled - Notepad")
# print it just for kicks
print hwnd
win32gui.ShowWindow(hwnd, win32con.SW_SHOWNORMAL)
#this restores the proper window, so we know we have correct handle
#just to give it a little pause
time.sleep(2)
print "trying to post message"
#try to send it a return key
win32api.SendMessage(hwnd, win32con.WM_KEYDOWN, win32con.VK_RETURN, 0)
win32api.SendMessage(hwnd, win32con.WM_KEYUP, win32con.VK_RETURN, 0)
#the above generates absolutely no effect on the notepad window.
#same effect no matter what vk code i use (e.g. 65 for A, VK_SPACE for space, etc)
#### end of script
May I suggest you to use AutoIt3 facilities (http://www.autoitscript.com/autoit3/docs/tutorials/notepad/notepad.htm "AutoIt Notepad Tutorial")
AutoIt3 is a Windows scripting language to control quite anything in Windows. It provide a COM API so you can make integrate it in your Python script
from win32com.client import Dispatch
AutoIt = Dispatch("AutoItX3.Control")
AutoIt.Run('Notepad.exe')
AutoIt.WinWaitActive("Untitled - Notepad")
AutoIt.Send("This is some text.")
It may be also possible to use AutoHotKey (the fully GPL version of AutoIt)
Notepad has no facilities for doing such from an external source. Short of hooking into the Windows windowing API, finding the text area, and populating it yourself.
No. Notepad does not read data from stdin, so passing it a file or OS-level file-like is the only way for it to display text.
This code will send s into Notepad window from Python script.
class cls_KeyBdInput(ct.Structure):
_fields_ = [
("wVk", ct.c_ushort),
("wScan", ct.c_ushort),
("dwFlags", ct.c_ulong),
("time", ct.c_ulong),
("dwExtraInfo", ct.POINTER(ct.c_ulong) )
]
class cls_HardwareInput(ct.Structure):
_fields_ = [
("uMsg", ct.c_ulong),
("wParamL", ct.c_short),
("wParamH", ct.c_ushort)
]
class cls_MouseInput(ct.Structure):
_fields_ = [
("dx", ct.c_long),
("dy", ct.c_long),
("mouseData", ct.c_ulong),
("dwFlags", ct.c_ulong),
("time", ct.c_ulong),
("dwExtraInfo", ct.POINTER(ct.c_ulong) )
]
class cls_Input_I(ct.Union):
_fields_ = [
("ki", cls_KeyBdInput),
("mi", cls_MouseInput),
("hi", cls_HardwareInput)
]
class cls_Input(ct.Structure):
_fields_ = [
("type", ct.c_ulong),
("ii", cls_Input_I)
]
def make_input_objects( l_keys ):
p_ExtraInfo_0 = ct.pointer(ct.c_ulong(0))
l_inputs = [ ]
for n_key, n_updown in l_keys:
ki = cls_KeyBdInput( n_key, 0, n_updown, 0, p_ExtraInfo_0 )
ii = cls_Input_I()
ii.ki = ki
l_inputs.append( ii )
n_inputs = len(l_inputs)
l_inputs_2=[]
for ndx in range( 0, n_inputs ):
s2 = "(1, l_inputs[%s])" % ndx
l_inputs_2.append(s2)
s_inputs = ', '.join(l_inputs_2)
cls_input_array = cls_Input * n_inputs
o_input_array = eval( "cls_input_array( %s )" % s_inputs )
p_input_array = ct.pointer( o_input_array )
n_size_0 = ct.sizeof( o_input_array[0] )
# these are the args for user32.SendInput()
return ( n_inputs, p_input_array, n_size_0 )
def send_s( window1 ):
t_s = ( ( 0x53, 0 ), )
l_keys = [ ]
l_keys.extend( t_s )
t_inputs = make_input_objects( l_s )
win32gui.ShowWindow(window1, win32con.SW_SHOWNORMAL)
win32gui.SetForegroundWindow(window1)
rv = ct.windll.user32.SendInput( *t_inputs )
def find_window( s_app_name ):
try:
window1 = FindWindow( None, s_app_name,)
return window1
except ui_err:
pass
except:
raise
try:
window1 = FindWindow( s_app_name, None, )
return window1
except ui_err:
return None
except:
raise
def search_title(srch,ttls):
out=None
for i in range(len(ttls)):
#print i, ttls[i][1]
if srch in ttls[i][1]:
out= ttls[i][1]
return out
def get_window_titles():
titles = []
def foreach_window(hwnd, lParam):
if IsWindowVisible(hwnd):
length = GetWindowTextLength(hwnd)
buff = ctypes.create_unicode_buffer(length + 1)
GetWindowText(hwnd, buff, length + 1)
ttl=buff.value
titles.append((hwnd, ttl))
return True
EnumWindows(EnumWindowsProc(foreach_window), 0)
return titles
ttls=get_window_titles()
title=search_title('Notepad',ttls)
window1 = find_window( title )
send_s( window1)

Categories