I18n for wxpython wx.AboutBox - python

I am about to make a wxpython programm translatable.
I invoke python gettext with:
import gettext
languagelist = [locale.getdefaultlocale()[0], 'en_US']
t = gettext.translation('myProgram', localedir, ['de_DE','en_US'])
_ = t.ugettext
This works fine for everything like:
self.statusbar.PushStatusText(_('Connecting service ...'))
But now there is this wx.AboutBox
info = wx.AboutDialogInfo()
info.Name = swname
info.Version = swversion
info.Developers = swdevelopers
info.License = wordwrap(swlicense, 500, wx.ClientDC(self))
wx.AboutBox(info)
This AboutBox has buttons labeled "Developers" and "License" and these buttons do not get translated.
No surprise I just ran pygettext -d myProgram mainFile.py to create the .pot file.
So how do I get the text from wx.AboutBix into my .pot file?

They are already translated for you and are contained in the wxstd.pot, respectively in the wxstd.mo of the relevant language.
In the wxPython Phoenix documentation is some more information and a small sample application - http://wxpython.org/Phoenix/docs/html/internationalization.html?highlight=i18n this also works for the wxPython Classic, which you probably use.

Related

how do i copy a file to clipboard in python? [duplicate]

I'm trying to make a basic Windows application that builds a string out of user input and then adds it to the clipboard. How do I copy a string to the clipboard using Python?
Actually, pywin32 and ctypes seem to be an overkill for this simple task. tkinter is a cross-platform GUI framework, which ships with Python by default and has clipboard accessing methods along with other cool stuff.
If all you need is to put some text to system clipboard, this will do it:
from tkinter import Tk # in Python 2, use "Tkinter" instead
r = Tk()
r.withdraw()
r.clipboard_clear()
r.clipboard_append('i can has clipboardz?')
r.update() # now it stays on the clipboard after the window is closed
r.destroy()
And that's all, no need to mess around with platform-specific third-party libraries.
If you are using Python 2, replace tkinter with Tkinter.
I didn't have a solution, just a workaround.
Windows Vista onwards has an inbuilt command called clip that takes the output of a command from command line and puts it into the clipboard. For example, ipconfig | clip.
So I made a function with the os module which takes a string and adds it to the clipboard using the inbuilt Windows solution.
import os
def addToClipBoard(text):
command = 'echo ' + text.strip() + '| clip'
os.system(command)
# Example
addToClipBoard('penny lane')
# Penny Lane is now in your ears, eyes, and clipboard.
As previously noted in the comments however, one downside to this approach is that the echo command automatically adds a newline to the end of your text. To avoid this you can use a modified version of the command:
def addToClipBoard(text):
command = 'echo | set /p nul=' + text.strip() + '| clip'
os.system(command)
If you are using Windows XP it will work just following the steps in Copy and paste from Windows XP Pro's command prompt straight to the Clipboard.
You can use pyperclip - cross-platform clipboard module. Or Xerox - similar module, except requires the win32 Python module to work on Windows.
You can also use ctypes to tap into the Windows API and avoid the massive pywin32 package. This is what I use (excuse the poor style, but the idea is there):
import ctypes
# Get required functions, strcpy..
strcpy = ctypes.cdll.msvcrt.strcpy
ocb = ctypes.windll.user32.OpenClipboard # Basic clipboard functions
ecb = ctypes.windll.user32.EmptyClipboard
gcd = ctypes.windll.user32.GetClipboardData
scd = ctypes.windll.user32.SetClipboardData
ccb = ctypes.windll.user32.CloseClipboard
ga = ctypes.windll.kernel32.GlobalAlloc # Global memory allocation
gl = ctypes.windll.kernel32.GlobalLock # Global memory Locking
gul = ctypes.windll.kernel32.GlobalUnlock
GMEM_DDESHARE = 0x2000
def Get():
ocb(None) # Open Clip, Default task
pcontents = gcd(1) # 1 means CF_TEXT.. too lazy to get the token thingy...
data = ctypes.c_char_p(pcontents).value
#gul(pcontents) ?
ccb()
return data
def Paste(data):
ocb(None) # Open Clip, Default task
ecb()
hCd = ga(GMEM_DDESHARE, len(bytes(data,"ascii")) + 1)
pchData = gl(hCd)
strcpy(ctypes.c_char_p(pchData), bytes(data, "ascii"))
gul(hCd)
scd(1, hCd)
ccb()
The simplest way is with pyperclip. Works in python 2 and 3.
To install this library, use:
pip install pyperclip
Example usage:
import pyperclip
pyperclip.copy("your string")
If you want to get the contents of the clipboard:
clipboard_content = pyperclip.paste()
You can use the excellent pandas, which has a built in clipboard support, but you need to pass through a DataFrame.
import pandas as pd
df=pd.DataFrame(['Text to copy'])
df.to_clipboard(index=False,header=False)
Here's the most easy and reliable way I found if you're okay depending on Pandas. However I don't think this is officially part of the Pandas API so it may break with future updates. It works as of 0.25.3
from pandas.io import clipboard
clipboard.copy("test")
For some reason I've never been able to get the Tk solution to work for me. kapace's solution is much more workable, but the formatting is contrary to my style and it doesn't work with Unicode. Here's a modified version.
import ctypes
from ctypes.wintypes import BOOL, HWND, HANDLE, HGLOBAL, UINT, LPVOID
from ctypes import c_size_t as SIZE_T
OpenClipboard = ctypes.windll.user32.OpenClipboard
OpenClipboard.argtypes = HWND,
OpenClipboard.restype = BOOL
EmptyClipboard = ctypes.windll.user32.EmptyClipboard
EmptyClipboard.restype = BOOL
GetClipboardData = ctypes.windll.user32.GetClipboardData
GetClipboardData.argtypes = UINT,
GetClipboardData.restype = HANDLE
SetClipboardData = ctypes.windll.user32.SetClipboardData
SetClipboardData.argtypes = UINT, HANDLE
SetClipboardData.restype = HANDLE
CloseClipboard = ctypes.windll.user32.CloseClipboard
CloseClipboard.restype = BOOL
CF_UNICODETEXT = 13
GlobalAlloc = ctypes.windll.kernel32.GlobalAlloc
GlobalAlloc.argtypes = UINT, SIZE_T
GlobalAlloc.restype = HGLOBAL
GlobalLock = ctypes.windll.kernel32.GlobalLock
GlobalLock.argtypes = HGLOBAL,
GlobalLock.restype = LPVOID
GlobalUnlock = ctypes.windll.kernel32.GlobalUnlock
GlobalUnlock.argtypes = HGLOBAL,
GlobalSize = ctypes.windll.kernel32.GlobalSize
GlobalSize.argtypes = HGLOBAL,
GlobalSize.restype = SIZE_T
GMEM_MOVEABLE = 0x0002
GMEM_ZEROINIT = 0x0040
unicode_type = type(u'')
def get():
text = None
OpenClipboard(None)
handle = GetClipboardData(CF_UNICODETEXT)
pcontents = GlobalLock(handle)
size = GlobalSize(handle)
if pcontents and size:
raw_data = ctypes.create_string_buffer(size)
ctypes.memmove(raw_data, pcontents, size)
text = raw_data.raw.decode('utf-16le').rstrip(u'\0')
GlobalUnlock(handle)
CloseClipboard()
return text
def put(s):
if not isinstance(s, unicode_type):
s = s.decode('mbcs')
data = s.encode('utf-16le')
OpenClipboard(None)
EmptyClipboard()
handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, len(data) + 2)
pcontents = GlobalLock(handle)
ctypes.memmove(pcontents, data, len(data))
GlobalUnlock(handle)
SetClipboardData(CF_UNICODETEXT, handle)
CloseClipboard()
paste = get
copy = put
The above has changed since this answer was first created, to better cope with extended Unicode characters and Python 3. It has been tested in both Python 2.7 and 3.5, and works even with emoji such as \U0001f601 (๐Ÿ˜).
Update 2021-10-26: This was working great for me in Windows 7 and Python 3.8. Then I got a new computer with Windows 10 and Python 3.10, and it failed for me the same way as indicated in the comments. This post gave me the answer. The functions from ctypes don't have argument and return types properly specified, and the defaults don't work consistently with 64-bit values. I've modified the above code to include that missing information.
I've tried various solutions, but this is the simplest one that passes my test:
#coding=utf-8
import win32clipboard # http://sourceforge.net/projects/pywin32/
def copy(text):
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardText(text, win32clipboard.CF_UNICODETEXT)
win32clipboard.CloseClipboard()
def paste():
win32clipboard.OpenClipboard()
data = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT)
win32clipboard.CloseClipboard()
return data
if __name__ == "__main__":
text = "Testing\nthe โ€œclipโ€”boardโ€: ๐Ÿ“‹"
try: text = text.decode('utf8') # Python 2 needs decode to make a Unicode string.
except AttributeError: pass
print("%r" % text.encode('utf8'))
copy(text)
data = paste()
print("%r" % data.encode('utf8'))
print("OK" if text == data else "FAIL")
try: print(data)
except UnicodeEncodeError as er:
print(er)
print(data.encode('utf8'))
Tested OK in Python 3.4 on Windows 8.1 and Python 2.7 on Windows 7. Also when reading Unicode data with Unix linefeeds copied from Windows. Copied data stays on the clipboard after Python exits: "Testing
the โ€œclipโ€”boardโ€: ๐Ÿ“‹"
If you want no external dependencies, use this code (now part of cross-platform pyperclip - C:\Python34\Scripts\pip install --upgrade pyperclip):
def copy(text):
GMEM_DDESHARE = 0x2000
CF_UNICODETEXT = 13
d = ctypes.windll # cdll expects 4 more bytes in user32.OpenClipboard(None)
try: # Python 2
if not isinstance(text, unicode):
text = text.decode('mbcs')
except NameError:
if not isinstance(text, str):
text = text.decode('mbcs')
d.user32.OpenClipboard(0)
d.user32.EmptyClipboard()
hCd = d.kernel32.GlobalAlloc(GMEM_DDESHARE, len(text.encode('utf-16-le')) + 2)
pchData = d.kernel32.GlobalLock(hCd)
ctypes.cdll.msvcrt.wcscpy(ctypes.c_wchar_p(pchData), text)
d.kernel32.GlobalUnlock(hCd)
d.user32.SetClipboardData(CF_UNICODETEXT, hCd)
d.user32.CloseClipboard()
def paste():
CF_UNICODETEXT = 13
d = ctypes.windll
d.user32.OpenClipboard(0)
handle = d.user32.GetClipboardData(CF_UNICODETEXT)
text = ctypes.c_wchar_p(handle).value
d.user32.CloseClipboard()
return text
Use pyperclip module
Install using pip pip install pyperclip.
https://pypi.org/project/pyperclip/
Copy text "Hello World!" to clip board
import pyperclip
pyperclip.copy('Hello World!')
You can use Ctrl+V anywhere to paste this somewhere.
Paste the copied text using python
pyperclip.paste() # This returns the copied text of type <class 'str'>
If you don't like the name you can use the derivative module clipboard.
Note: It's just a selective wrapper of pyperclip
After installing, import it:
import clipboard
Then you can copy like this:
clipboard.copy("This is copied")
You can also paste the copied text:
clipboard.paste()
Looks like you need to add win32clipboard to your site-packages. It's part of the pywin32 package
Not all of the answers worked for my various python configurations so this solution only uses the subprocess module. However, copy_keyword has to be pbcopy for Mac or clip for Windows:
import subprocess
subprocess.run('copy_keyword', universal_newlines=True, input='New Clipboard Value ๐Ÿ˜€')
Here's some more extensive code that automatically checks what the current operating system is:
import platform
import subprocess
copy_string = 'New Clipboard Value ๐Ÿ˜€'
# Check which operating system is running to get the correct copying keyword.
if platform.system() == 'Darwin':
copy_keyword = 'pbcopy'
elif platform.system() == 'Windows':
copy_keyword = 'clip'
subprocess.run(copy_keyword, universal_newlines=True, input=copy_string)
I think there is a much simpler solution to this.
name = input('What is your name? ')
print('Hello %s' % (name) )
Then run your program in the command line
python greeter.py | clip
This will pipe the output of your file to the clipboard
Widgets also have method named .clipboard_get() that returns the contents of the clipboard (unless some kind of error happens based on the type of data in the clipboard).
The clipboard_get() method is mentioned in this bug report:
http://bugs.python.org/issue14777
Strangely, this method was not mentioned in the common (but unofficial) online TkInter documentation sources that I usually refer to.
Solution with stdlib, without security issues
The following solution works in Linux without any additional library and without the risk of executing unwanted code in your shell.
import subprocess
def to_clipboard(text: str) -> None:
sp = subprocess.Popen(["xclip"], stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
sp.communicate(text.encode("utf8"))
Note that there multiple clipboard in Linux, the you use with the Middle Mouse (Primary) and yet another that you use pressing STRG+C,STRG+V.
You can define which clipboard is used by adding a selection parameter i.e. ["xclip", "-selection", "clipboard"].
See the man xclip for details.
If you using Windows, just replace xclip with clip.
This solution works without Tkinter, which not available some Python installations (i.e. the custom build I am currently using).
In addition to Mark Ransom's answer using ctypes:
This does not work for (all?) x64 systems since the handles seem to be truncated to int-size.
Explicitly defining args and return values helps to overcomes this problem.
import ctypes
import ctypes.wintypes as w
CF_UNICODETEXT = 13
u32 = ctypes.WinDLL('user32')
k32 = ctypes.WinDLL('kernel32')
OpenClipboard = u32.OpenClipboard
OpenClipboard.argtypes = w.HWND,
OpenClipboard.restype = w.BOOL
GetClipboardData = u32.GetClipboardData
GetClipboardData.argtypes = w.UINT,
GetClipboardData.restype = w.HANDLE
EmptyClipboard = u32.EmptyClipboard
EmptyClipboard.restype = w.BOOL
SetClipboardData = u32.SetClipboardData
SetClipboardData.argtypes = w.UINT, w.HANDLE,
SetClipboardData.restype = w.HANDLE
CloseClipboard = u32.CloseClipboard
CloseClipboard.argtypes = None
CloseClipboard.restype = w.BOOL
GHND = 0x0042
GlobalAlloc = k32.GlobalAlloc
GlobalAlloc.argtypes = w.UINT, w.ctypes.c_size_t,
GlobalAlloc.restype = w.HGLOBAL
GlobalLock = k32.GlobalLock
GlobalLock.argtypes = w.HGLOBAL,
GlobalLock.restype = w.LPVOID
GlobalUnlock = k32.GlobalUnlock
GlobalUnlock.argtypes = w.HGLOBAL,
GlobalUnlock.restype = w.BOOL
GlobalSize = k32.GlobalSize
GlobalSize.argtypes = w.HGLOBAL,
GlobalSize.restype = w.ctypes.c_size_t
unicode_type = type(u'')
def get():
text = None
OpenClipboard(None)
handle = GetClipboardData(CF_UNICODETEXT)
pcontents = GlobalLock(handle)
size = GlobalSize(handle)
if pcontents and size:
raw_data = ctypes.create_string_buffer(size)
ctypes.memmove(raw_data, pcontents, size)
text = raw_data.raw.decode('utf-16le').rstrip(u'\0')
GlobalUnlock(handle)
CloseClipboard()
return text
def put(s):
if not isinstance(s, unicode_type):
s = s.decode('mbcs')
data = s.encode('utf-16le')
OpenClipboard(None)
EmptyClipboard()
handle = GlobalAlloc(GHND, len(data) + 2)
pcontents = GlobalLock(handle)
ctypes.memmove(pcontents, data, len(data))
GlobalUnlock(handle)
SetClipboardData(CF_UNICODETEXT, handle)
CloseClipboard()
#Test run
paste = get
copy = put
copy("Hello World!")
print(paste())
also you can use > clipboard
import clipboard
def copy(txt):
clipboard.copy(txt)
copy("your txt")
If (and only if) the application already uses Qt, you can use this (with the advantage of no additional third party dependency)
from PyQt5.QtWidgets import QApplication
clipboard = QApplication.clipboard()
# get text (if there's text inside instead of e.g. file)
clipboard.text()
# set text
clipboard.setText(s)
This requires a Qt application object to be already constructed, so it should not be used unless the application already uses Qt.
Besides, as usual, in X systems (and maybe other systems too), the content only persist until the application exists unless you use something like parcellite or xclipboard.
Documentation:
QGuiApplication Class | Qt GUI 5.15.6
QClipboard Class | Qt GUI 5.15.6
See also: python - PyQT - copy file to clipboard - Stack Overflow
import wx
def ctc(text):
if not wx.TheClipboard.IsOpened():
wx.TheClipboard.Open()
data = wx.TextDataObject()
data.SetText(text)
wx.TheClipboard.SetData(data)
wx.TheClipboard.Close()
ctc(text)
The snippet I share here take advantage of the ability to format text files: what if you want to copy a complex output to the clipboard ? (Say a numpy array in column or a list of something)
import subprocess
import os
def cp2clip(clist):
#create a temporary file
fi=open("thisTextfileShouldNotExist.txt","w")
#write in the text file the way you want your data to be
for m in clist:
fi.write(m+"\n")
#close the file
fi.close()
#send "clip < file" to the shell
cmd="clip < thisTextfileShouldNotExist.txt"
w = subprocess.check_call(cmd,shell=True)
#delete the temporary text file
os.remove("thisTextfileShouldNotExist.txt")
return w
works only for windows, can be adapted for linux or mac I guess. Maybe a bit complicated...
example:
>>>cp2clip(["ET","phone","home"])
>>>0
Ctrl+V in any text editor :
ET
phone
home
This is the improved answer of atomizer.
Note 2 calls of update() and 200 ms delay between them. They protect freezing applications due to an unstable state of the clipboard:
from Tkinter import Tk
import time
r = Tk()
r.withdraw()
r.clipboard_clear()
r.clipboard_append('some string')
r.update()
time.sleep(.2)
r.update()
r.destroy()
Use python's clipboard library!
import clipboard as cp
cp.copy("abc")
Clipboard contains 'abc' now. Happy pasting!
You can use winclip32 module!
install:
pip install winclip32
to copy:
import winclip32
winclip32.set_clipboard_data(winclip32.UNICODE_STD_TEXT, "some text")
to get:
import winclip32
print(winclip32.get_clipboard_data(winclip32.UNICODE_STD_TEXT))
for more informations: https://pypi.org/project/winclip32/
On Windows, you can use this. No external dependencies neither have to open sub-process:
import win32clipboard
def to_clipboard(txt):
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardText(txt)
win32clipboard.CloseClipboard()
My multiplatform solution base on this question:
import subprocess
import distutils.spawn
def clipit(text):
if distutils.spawn.find_executable("xclip"):
# for Linux
subprocess.run(["xclip", "-i"], input=text.encode("utf8"))
elif distutils.spawn.find_executable("xsel"):
# for Linux
subprocess.run(["xsel", "--input"], input=text.encode("utf8"))
elif distutils.spawn.find_executable("clip"):
# for Windows
subprocess.run(["clip"], input=text.encode("utf8"))
else:
import pyperclip
print("I use module pyperclip.")
pyperclip.copy(text)
Code snippet to copy the clipboard:
Create a wrapper Python code in a module named (clipboard.py):
import clr
clr.AddReference('System.Windows.Forms')
from System.Windows.Forms import Clipboard
def setText(text):
Clipboard.SetText(text)
def getText():
return Clipboard.GetText()
Then import the above module into your code.
import io
import clipboard
code = clipboard.getText()
print code
code = "abcd"
clipboard.setText(code)
I must give credit to the blog post Clipboard Access in IronPython.
you can try this:
command = 'echo content |clip'
subprocess.check_call(command, shell=True)

Have icon overlays persist after machine restart in Python

So, thanks to the Tim Golden guide and other questions here I have a script that will show overlays on files and folders based on their "state" similar to Tortoise SVN or Dropbox.
My problem is that once I restart the explorer.exe process or the OS itself and open explorer there are no longer any overlays.
My first thought:
Have the service that actually manages file state detect that no requests have come in and just re-register the overlay handler
The problem here is that registration requires elevated permissions which is acceptable on initial install of the application by the end user but not every time they restart their machine.
Can anyone suggest what I might be missing here?
I have the class BaseOverlay and its children in a single .py file and register from my main app by calling this script using subprocess.
subprocess.check_call(script_path, shell=True)
Is Explorer not able to re-load the script as it is Python? Do I need to compile into a DLL or EXE? Would that change the registration process?
Here's the registration call:
win32com.server.register.UseCommandLine(BaseOverlay)
Here's the class(simplified):
class BaseOverlay:
_reg_clsid_ = '{8D4B1C5D-F8AC-4FDA-961F-A0143CD97C41}'
_reg_progid_ = 'someoverlays'
_reg_desc_ = 'Icon Overlay Handler'
_public_methods_ = ['GetOverlayInfo', 'GetPriority', 'IsMemberOf']
_com_interfaces_ = [shell.IID_IShellIconOverlayIdentifier]
def GetOverlayInfo(self):
return icon_path, 0, shellcon.ISIOI_ICONFILE
def GetPriority(self):
return 50
def IsMemberOf(self, fname, attributes):
return winerror.S_OK

How to use KDE Okular's document view widget in a Python Qt app?

I am writing a desktop application in Python (3.4) on Linux using Qt (4.8) and PyQt.
Is there a way to use/import Okular's pdf view functionality as a widget? If yes, how?
This works for me:
import sys
from PyKDE4.kdecore import ki18n, KAboutData, KCmdLineArgs
from PyKDE4.kdecore import KLibLoader as ll
from PyKDE4.kdeui import KApplication
import PyKDE4.kparts as kp
appName = "KApplication"
catalog = ""
programName = ki18n ("KApplication")
version = "1.0"
description = ki18n ("KApplication")
license = KAboutData.License_GPL
copyright = ki18n ("(c) 2007 John Doe")
text = ki18n ("none")
homePage = "www.johndoe.com"
bugEmail = "johndoe#nowhere.com "
aboutData = KAboutData(
appName, catalog, programName, version, description,
license, copyright, text, homePage, bugEmail
)
KCmdLineArgs.init(sys.argv, aboutData)
app = KApplication()
win = kp.KParts.MainWindow()
okupart = ll.self().factory('okularpart').create()
win.setCentralWidget(okupart.widget())
win.show()
app.exec_()
pykde could provide the required functionality. I have not figured out how, though.
Get the latest version of pykde:
git clone git://anongit.kde.org/pykde4
Documentation:
https://techbase.kde.org/Development/Languages/Python
http://api.kde.org/pykde-4.3-api/install.html
Building it required PyQt4 and CMAKE. Some linux distributions ship an outdated version of FindPyQt.py (causing an error along the lines of Python importerror : pyqtconfig). A functional one can be found here:
https://github.com/qgis/QGIS/blob/master/cmake/FindPyQt.py
Place it in /usr/share/kde4/apps/cmake/modules/
For compiling your UI files into Python code, use pykdeuic4 instead of pyuic4:
pykdeuic4 file.ui > file.py

NTEventLogHandler from a Python executable

import logging, logging.handlers
def main():
ntl = logging.handlers.NTEventLogHandler("Python Logging Test")
logger = logging.getLogger("")
logger.setLevel(logging.DEBUG)
logger.addHandler(ntl)
logger.error("This is a '%s' message", "Error")
if __name__ == "__main__":
main()
The Python (2.7.x) script above writes "This is a 'Error' message" to the Windows Event Viewer. When I run it as a script, I get the expected output. If I convert the script to an executable via PyInstaller, I get an entry in the event log but it says something completely different.
The description for Event ID ( 1 ) in Source ( Python Logging Test ) cannot be found. The local computer may not have the necessary registry information or message DLL files to display messages from a remote computer. You may be able to use the /AUXSOURCE= flag to retrieve this description; see Help and Support for details. The following information is part of the event: This is a 'Error' message.
This is the command I use to convert the script into an executable: pyinstaller.py --onefile --noconsole my_script.py though the command line parameters do not appear to have any impact on this behaviour and it will suffice to just call pyinstaller.py my_script.py.
I would appreciate any help in understanding what is going on and how I go about fixing this.
Final solution
I didn't want to go down the resource hacker route, as that is going to be a difficult step to automate. Instead, the approach I took was to grab the win32service.pyd file from c:\Python27\Lib\site-packages\win32 and place it next to my executable. The script was then modified pass the full path to the copy of the win32service.pyd file and this works in both script and exe form. The final script is included below:
import logging, logging.handlers
import os
import sys
def main():
base_dir = os.path.dirname(sys.argv[0])
dllname = os.path.join(base_dir, "win32service.pyd")
ntl = logging.handlers.NTEventLogHandler("Python Logging Test", dllname=dllname)
logger = logging.getLogger("")
logger.setLevel(logging.DEBUG)
logger.addHandler(ntl)
logger.error("This is a '%s' message", "Error")
if __name__ == "__main__":
main()
Usually, the Windows Event Log doesn't store error messages in plain text, but rather message ID references and insertion strings.
Instead of storing a message like Service foo crashed unexpectedly, it stores a message ID which points to a resource string stored in a DLL. In this case, the resource would be something like Service %s crashed unexpectedly and foo would be stored as insertion string. The program which writes the message registers the resource DLL.
The reason for this is localization. DLLs can store lots of different resources (dialog layout, strings, iconsโ€ฆ), and one DLL can contain the same resource in many different languages. The operating system automatically chooses the right resources depending on the system locale. Resource DLLs are used by virtually all Microsoft utilities and core utilities.
Side note: Nowadays, the preferred (and cross-platform) way for localization is gettext.
This is used for the message log as well โ€“ ideally, you could open a log from an German Windows installation on an English one with all messages in English.
I suspect that the pywin32 implementation skips that mechanism by only having one single message ID (1) which is just something like "%s". It is stored in win32service.pyd and registered by pywin32. This works fine as long as this file exists on the file system, but breaks as soon as it's hidden inside a PyInstaller executable. I guess you have to embed the message ID into your executable directly.
Edit: suspicion confirmed, the message table is indeed stored inside win32service.pyd
Resource Hacker showing the message table http://media.leoluk.de/evlog_rh.png
Try to copy the message table resource from win32service.pyd to your PyInstaller executable (for example using Resource Hacker).
Looking at the logging handler implementation, this might work:
def __init__(self, appname, dllname=None, logtype="Application"):
logging.Handler.__init__(self)
try:
import win32evtlogutil, win32evtlog
self.appname = appname
self._welu = win32evtlogutil
if not dllname:
dllname = os.path.split(self._welu.__file__)
dllname = os.path.split(dllname[0])
dllname = os.path.join(dllname[0], r'win32service.pyd')
You'd have to set dllname to os.path.dirname(__file__). Use something like this if you want it to continue working for the unfrozen script:
if getattr(sys, 'frozen', False):
dllname = None
elif __file__:
dllname = os.path.dirname(__file__)
ntl = logging.handlers.NTEventLogHandler("Python Logging Test", dllname=dllname)

How do I copy a string to the clipboard?

I'm trying to make a basic Windows application that builds a string out of user input and then adds it to the clipboard. How do I copy a string to the clipboard using Python?
Actually, pywin32 and ctypes seem to be an overkill for this simple task. tkinter is a cross-platform GUI framework, which ships with Python by default and has clipboard accessing methods along with other cool stuff.
If all you need is to put some text to system clipboard, this will do it:
from tkinter import Tk # in Python 2, use "Tkinter" instead
r = Tk()
r.withdraw()
r.clipboard_clear()
r.clipboard_append('i can has clipboardz?')
r.update() # now it stays on the clipboard after the window is closed
r.destroy()
And that's all, no need to mess around with platform-specific third-party libraries.
If you are using Python 2, replace tkinter with Tkinter.
I didn't have a solution, just a workaround.
Windows Vista onwards has an inbuilt command called clip that takes the output of a command from command line and puts it into the clipboard. For example, ipconfig | clip.
So I made a function with the os module which takes a string and adds it to the clipboard using the inbuilt Windows solution.
import os
def addToClipBoard(text):
command = 'echo ' + text.strip() + '| clip'
os.system(command)
# Example
addToClipBoard('penny lane')
# Penny Lane is now in your ears, eyes, and clipboard.
As previously noted in the comments however, one downside to this approach is that the echo command automatically adds a newline to the end of your text. To avoid this you can use a modified version of the command:
def addToClipBoard(text):
command = 'echo | set /p nul=' + text.strip() + '| clip'
os.system(command)
If you are using Windows XP it will work just following the steps in Copy and paste from Windows XP Pro's command prompt straight to the Clipboard.
You can use pyperclip - cross-platform clipboard module. Or Xerox - similar module, except requires the win32 Python module to work on Windows.
The simplest way is with pyperclip. Works in python 2 and 3.
To install this library, use:
pip install pyperclip
Example usage:
import pyperclip
pyperclip.copy("your string")
If you want to get the contents of the clipboard:
clipboard_content = pyperclip.paste()
You can use the excellent pandas, which has a built in clipboard support, but you need to pass through a DataFrame.
import pandas as pd
df=pd.DataFrame(['Text to copy'])
df.to_clipboard(index=False,header=False)
You can also use ctypes to tap into the Windows API and avoid the massive pywin32 package. This is what I use (excuse the poor style, but the idea is there):
import ctypes
# Get required functions, strcpy..
strcpy = ctypes.cdll.msvcrt.strcpy
ocb = ctypes.windll.user32.OpenClipboard # Basic clipboard functions
ecb = ctypes.windll.user32.EmptyClipboard
gcd = ctypes.windll.user32.GetClipboardData
scd = ctypes.windll.user32.SetClipboardData
ccb = ctypes.windll.user32.CloseClipboard
ga = ctypes.windll.kernel32.GlobalAlloc # Global memory allocation
gl = ctypes.windll.kernel32.GlobalLock # Global memory Locking
gul = ctypes.windll.kernel32.GlobalUnlock
GMEM_DDESHARE = 0x2000
def Get():
ocb(None) # Open Clip, Default task
pcontents = gcd(1) # 1 means CF_TEXT.. too lazy to get the token thingy...
data = ctypes.c_char_p(pcontents).value
#gul(pcontents) ?
ccb()
return data
def Paste(data):
ocb(None) # Open Clip, Default task
ecb()
hCd = ga(GMEM_DDESHARE, len(bytes(data,"ascii")) + 1)
pchData = gl(hCd)
strcpy(ctypes.c_char_p(pchData), bytes(data, "ascii"))
gul(hCd)
scd(1, hCd)
ccb()
Here's the most easy and reliable way I found if you're okay depending on Pandas. However I don't think this is officially part of the Pandas API so it may break with future updates. It works as of 0.25.3
from pandas.io import clipboard
clipboard.copy("test")
For some reason I've never been able to get the Tk solution to work for me. kapace's solution is much more workable, but the formatting is contrary to my style and it doesn't work with Unicode. Here's a modified version.
import ctypes
from ctypes.wintypes import BOOL, HWND, HANDLE, HGLOBAL, UINT, LPVOID
from ctypes import c_size_t as SIZE_T
OpenClipboard = ctypes.windll.user32.OpenClipboard
OpenClipboard.argtypes = HWND,
OpenClipboard.restype = BOOL
EmptyClipboard = ctypes.windll.user32.EmptyClipboard
EmptyClipboard.restype = BOOL
GetClipboardData = ctypes.windll.user32.GetClipboardData
GetClipboardData.argtypes = UINT,
GetClipboardData.restype = HANDLE
SetClipboardData = ctypes.windll.user32.SetClipboardData
SetClipboardData.argtypes = UINT, HANDLE
SetClipboardData.restype = HANDLE
CloseClipboard = ctypes.windll.user32.CloseClipboard
CloseClipboard.restype = BOOL
CF_UNICODETEXT = 13
GlobalAlloc = ctypes.windll.kernel32.GlobalAlloc
GlobalAlloc.argtypes = UINT, SIZE_T
GlobalAlloc.restype = HGLOBAL
GlobalLock = ctypes.windll.kernel32.GlobalLock
GlobalLock.argtypes = HGLOBAL,
GlobalLock.restype = LPVOID
GlobalUnlock = ctypes.windll.kernel32.GlobalUnlock
GlobalUnlock.argtypes = HGLOBAL,
GlobalSize = ctypes.windll.kernel32.GlobalSize
GlobalSize.argtypes = HGLOBAL,
GlobalSize.restype = SIZE_T
GMEM_MOVEABLE = 0x0002
GMEM_ZEROINIT = 0x0040
unicode_type = type(u'')
def get():
text = None
OpenClipboard(None)
handle = GetClipboardData(CF_UNICODETEXT)
pcontents = GlobalLock(handle)
size = GlobalSize(handle)
if pcontents and size:
raw_data = ctypes.create_string_buffer(size)
ctypes.memmove(raw_data, pcontents, size)
text = raw_data.raw.decode('utf-16le').rstrip(u'\0')
GlobalUnlock(handle)
CloseClipboard()
return text
def put(s):
if not isinstance(s, unicode_type):
s = s.decode('mbcs')
data = s.encode('utf-16le')
OpenClipboard(None)
EmptyClipboard()
handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, len(data) + 2)
pcontents = GlobalLock(handle)
ctypes.memmove(pcontents, data, len(data))
GlobalUnlock(handle)
SetClipboardData(CF_UNICODETEXT, handle)
CloseClipboard()
paste = get
copy = put
The above has changed since this answer was first created, to better cope with extended Unicode characters and Python 3. It has been tested in both Python 2.7 and 3.5, and works even with emoji such as \U0001f601 (๐Ÿ˜).
Update 2021-10-26: This was working great for me in Windows 7 and Python 3.8. Then I got a new computer with Windows 10 and Python 3.10, and it failed for me the same way as indicated in the comments. This post gave me the answer. The functions from ctypes don't have argument and return types properly specified, and the defaults don't work consistently with 64-bit values. I've modified the above code to include that missing information.
I've tried various solutions, but this is the simplest one that passes my test:
#coding=utf-8
import win32clipboard # http://sourceforge.net/projects/pywin32/
def copy(text):
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardText(text, win32clipboard.CF_UNICODETEXT)
win32clipboard.CloseClipboard()
def paste():
win32clipboard.OpenClipboard()
data = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT)
win32clipboard.CloseClipboard()
return data
if __name__ == "__main__":
text = "Testing\nthe โ€œclipโ€”boardโ€: ๐Ÿ“‹"
try: text = text.decode('utf8') # Python 2 needs decode to make a Unicode string.
except AttributeError: pass
print("%r" % text.encode('utf8'))
copy(text)
data = paste()
print("%r" % data.encode('utf8'))
print("OK" if text == data else "FAIL")
try: print(data)
except UnicodeEncodeError as er:
print(er)
print(data.encode('utf8'))
Tested OK in Python 3.4 on Windows 8.1 and Python 2.7 on Windows 7. Also when reading Unicode data with Unix linefeeds copied from Windows. Copied data stays on the clipboard after Python exits: "Testing
the โ€œclipโ€”boardโ€: ๐Ÿ“‹"
If you want no external dependencies, use this code (now part of cross-platform pyperclip - C:\Python34\Scripts\pip install --upgrade pyperclip):
def copy(text):
GMEM_DDESHARE = 0x2000
CF_UNICODETEXT = 13
d = ctypes.windll # cdll expects 4 more bytes in user32.OpenClipboard(None)
try: # Python 2
if not isinstance(text, unicode):
text = text.decode('mbcs')
except NameError:
if not isinstance(text, str):
text = text.decode('mbcs')
d.user32.OpenClipboard(0)
d.user32.EmptyClipboard()
hCd = d.kernel32.GlobalAlloc(GMEM_DDESHARE, len(text.encode('utf-16-le')) + 2)
pchData = d.kernel32.GlobalLock(hCd)
ctypes.cdll.msvcrt.wcscpy(ctypes.c_wchar_p(pchData), text)
d.kernel32.GlobalUnlock(hCd)
d.user32.SetClipboardData(CF_UNICODETEXT, hCd)
d.user32.CloseClipboard()
def paste():
CF_UNICODETEXT = 13
d = ctypes.windll
d.user32.OpenClipboard(0)
handle = d.user32.GetClipboardData(CF_UNICODETEXT)
text = ctypes.c_wchar_p(handle).value
d.user32.CloseClipboard()
return text
Use pyperclip module
Install using pip pip install pyperclip.
https://pypi.org/project/pyperclip/
Copy text "Hello World!" to clip board
import pyperclip
pyperclip.copy('Hello World!')
You can use Ctrl+V anywhere to paste this somewhere.
Paste the copied text using python
pyperclip.paste() # This returns the copied text of type <class 'str'>
If you don't like the name you can use the derivative module clipboard.
Note: It's just a selective wrapper of pyperclip
After installing, import it:
import clipboard
Then you can copy like this:
clipboard.copy("This is copied")
You can also paste the copied text:
clipboard.paste()
Looks like you need to add win32clipboard to your site-packages. It's part of the pywin32 package
Not all of the answers worked for my various python configurations so this solution only uses the subprocess module. However, copy_keyword has to be pbcopy for Mac or clip for Windows:
import subprocess
subprocess.run('copy_keyword', universal_newlines=True, input='New Clipboard Value ๐Ÿ˜€')
Here's some more extensive code that automatically checks what the current operating system is:
import platform
import subprocess
copy_string = 'New Clipboard Value ๐Ÿ˜€'
# Check which operating system is running to get the correct copying keyword.
if platform.system() == 'Darwin':
copy_keyword = 'pbcopy'
elif platform.system() == 'Windows':
copy_keyword = 'clip'
subprocess.run(copy_keyword, universal_newlines=True, input=copy_string)
I think there is a much simpler solution to this.
name = input('What is your name? ')
print('Hello %s' % (name) )
Then run your program in the command line
python greeter.py | clip
This will pipe the output of your file to the clipboard
Widgets also have method named .clipboard_get() that returns the contents of the clipboard (unless some kind of error happens based on the type of data in the clipboard).
The clipboard_get() method is mentioned in this bug report:
http://bugs.python.org/issue14777
Strangely, this method was not mentioned in the common (but unofficial) online TkInter documentation sources that I usually refer to.
Solution with stdlib, without security issues
The following solution works in Linux without any additional library and without the risk of executing unwanted code in your shell.
import subprocess
def to_clipboard(text: str) -> None:
sp = subprocess.Popen(["xclip"], stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
sp.communicate(text.encode("utf8"))
Note that there multiple clipboard in Linux, the you use with the Middle Mouse (Primary) and yet another that you use pressing STRG+C,STRG+V.
You can define which clipboard is used by adding a selection parameter i.e. ["xclip", "-selection", "clipboard"].
See the man xclip for details.
If you using Windows, just replace xclip with clip.
This solution works without Tkinter, which not available some Python installations (i.e. the custom build I am currently using).
In addition to Mark Ransom's answer using ctypes:
This does not work for (all?) x64 systems since the handles seem to be truncated to int-size.
Explicitly defining args and return values helps to overcomes this problem.
import ctypes
import ctypes.wintypes as w
CF_UNICODETEXT = 13
u32 = ctypes.WinDLL('user32')
k32 = ctypes.WinDLL('kernel32')
OpenClipboard = u32.OpenClipboard
OpenClipboard.argtypes = w.HWND,
OpenClipboard.restype = w.BOOL
GetClipboardData = u32.GetClipboardData
GetClipboardData.argtypes = w.UINT,
GetClipboardData.restype = w.HANDLE
EmptyClipboard = u32.EmptyClipboard
EmptyClipboard.restype = w.BOOL
SetClipboardData = u32.SetClipboardData
SetClipboardData.argtypes = w.UINT, w.HANDLE,
SetClipboardData.restype = w.HANDLE
CloseClipboard = u32.CloseClipboard
CloseClipboard.argtypes = None
CloseClipboard.restype = w.BOOL
GHND = 0x0042
GlobalAlloc = k32.GlobalAlloc
GlobalAlloc.argtypes = w.UINT, w.ctypes.c_size_t,
GlobalAlloc.restype = w.HGLOBAL
GlobalLock = k32.GlobalLock
GlobalLock.argtypes = w.HGLOBAL,
GlobalLock.restype = w.LPVOID
GlobalUnlock = k32.GlobalUnlock
GlobalUnlock.argtypes = w.HGLOBAL,
GlobalUnlock.restype = w.BOOL
GlobalSize = k32.GlobalSize
GlobalSize.argtypes = w.HGLOBAL,
GlobalSize.restype = w.ctypes.c_size_t
unicode_type = type(u'')
def get():
text = None
OpenClipboard(None)
handle = GetClipboardData(CF_UNICODETEXT)
pcontents = GlobalLock(handle)
size = GlobalSize(handle)
if pcontents and size:
raw_data = ctypes.create_string_buffer(size)
ctypes.memmove(raw_data, pcontents, size)
text = raw_data.raw.decode('utf-16le').rstrip(u'\0')
GlobalUnlock(handle)
CloseClipboard()
return text
def put(s):
if not isinstance(s, unicode_type):
s = s.decode('mbcs')
data = s.encode('utf-16le')
OpenClipboard(None)
EmptyClipboard()
handle = GlobalAlloc(GHND, len(data) + 2)
pcontents = GlobalLock(handle)
ctypes.memmove(pcontents, data, len(data))
GlobalUnlock(handle)
SetClipboardData(CF_UNICODETEXT, handle)
CloseClipboard()
#Test run
paste = get
copy = put
copy("Hello World!")
print(paste())
also you can use > clipboard
import clipboard
def copy(txt):
clipboard.copy(txt)
copy("your txt")
If (and only if) the application already uses Qt, you can use this (with the advantage of no additional third party dependency)
from PyQt5.QtWidgets import QApplication
clipboard = QApplication.clipboard()
# get text (if there's text inside instead of e.g. file)
clipboard.text()
# set text
clipboard.setText(s)
This requires a Qt application object to be already constructed, so it should not be used unless the application already uses Qt.
Besides, as usual, in X systems (and maybe other systems too), the content only persist until the application exists unless you use something like parcellite or xclipboard.
Documentation:
QGuiApplication Class | Qt GUI 5.15.6
QClipboard Class | Qt GUI 5.15.6
See also: python - PyQT - copy file to clipboard - Stack Overflow
import wx
def ctc(text):
if not wx.TheClipboard.IsOpened():
wx.TheClipboard.Open()
data = wx.TextDataObject()
data.SetText(text)
wx.TheClipboard.SetData(data)
wx.TheClipboard.Close()
ctc(text)
The snippet I share here take advantage of the ability to format text files: what if you want to copy a complex output to the clipboard ? (Say a numpy array in column or a list of something)
import subprocess
import os
def cp2clip(clist):
#create a temporary file
fi=open("thisTextfileShouldNotExist.txt","w")
#write in the text file the way you want your data to be
for m in clist:
fi.write(m+"\n")
#close the file
fi.close()
#send "clip < file" to the shell
cmd="clip < thisTextfileShouldNotExist.txt"
w = subprocess.check_call(cmd,shell=True)
#delete the temporary text file
os.remove("thisTextfileShouldNotExist.txt")
return w
works only for windows, can be adapted for linux or mac I guess. Maybe a bit complicated...
example:
>>>cp2clip(["ET","phone","home"])
>>>0
Ctrl+V in any text editor :
ET
phone
home
This is the improved answer of atomizer.
Note 2 calls of update() and 200 ms delay between them. They protect freezing applications due to an unstable state of the clipboard:
from Tkinter import Tk
import time
r = Tk()
r.withdraw()
r.clipboard_clear()
r.clipboard_append('some string')
r.update()
time.sleep(.2)
r.update()
r.destroy()
Use python's clipboard library!
import clipboard as cp
cp.copy("abc")
Clipboard contains 'abc' now. Happy pasting!
You can use winclip32 module!
install:
pip install winclip32
to copy:
import winclip32
winclip32.set_clipboard_data(winclip32.UNICODE_STD_TEXT, "some text")
to get:
import winclip32
print(winclip32.get_clipboard_data(winclip32.UNICODE_STD_TEXT))
for more informations: https://pypi.org/project/winclip32/
On Windows, you can use this. No external dependencies neither have to open sub-process:
import win32clipboard
def to_clipboard(txt):
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardText(txt)
win32clipboard.CloseClipboard()
My multiplatform solution base on this question:
import subprocess
import distutils.spawn
def clipit(text):
if distutils.spawn.find_executable("xclip"):
# for Linux
subprocess.run(["xclip", "-i"], input=text.encode("utf8"))
elif distutils.spawn.find_executable("xsel"):
# for Linux
subprocess.run(["xsel", "--input"], input=text.encode("utf8"))
elif distutils.spawn.find_executable("clip"):
# for Windows
subprocess.run(["clip"], input=text.encode("utf8"))
else:
import pyperclip
print("I use module pyperclip.")
pyperclip.copy(text)
Code snippet to copy the clipboard:
Create a wrapper Python code in a module named (clipboard.py):
import clr
clr.AddReference('System.Windows.Forms')
from System.Windows.Forms import Clipboard
def setText(text):
Clipboard.SetText(text)
def getText():
return Clipboard.GetText()
Then import the above module into your code.
import io
import clipboard
code = clipboard.getText()
print code
code = "abcd"
clipboard.setText(code)
I must give credit to the blog post Clipboard Access in IronPython.
you can try this:
command = 'echo content |clip'
subprocess.check_call(command, shell=True)

Categories