Hello,
I’d like to catch when the user hits CTRL+C, and copy the contents of a label (StaticText) into the clipboard.
How can it be done in wxPython?
Thank you.
Hello,
I’d like to catch when the user hits CTRL+C, and copy the contents of a label (StaticText) into the clipboard.
How can it be done in wxPython?
Thank you.
Hi Shohreh,
StaticText controls do not appear to accept the focus, so binding keyboard events to them has no effect as far as I can tell.
A similar question was ask on StackOverflow a few years ago:
A reply to that question suggested two possible workarounds:
wx.EVT_RIGHT_DOWN
for the StaticText to a handler that displays a popup menu that contains an option to copy the StaticText’s label to the clipboard so that it can then be pasted into the destination control. However, the example given assumes the parent window contains only one StaticText control and hard codes it as the source of the text to be copied.or
This is a simple demo of the second technique:
import wx
class StaticTextCtrl(wx.TextCtrl):
def __init__(self, parent, id=wx.ID_ANY, value=''):
wx.TextCtrl.__init__(self, parent, id, value=value, style=wx.BORDER_NONE|wx.TE_READONLY)
bg = self.GetParent().GetBackgroundColour()
self.SetBackgroundColour(bg)
width, height, descent, _leading = self.GetFullTextExtent(value)
self.SetMinSize((width+2, height+descent))
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__(self, *args, **kwds)
self.SetSize((400, 300))
self.SetTitle("Test StaticTextCtrl")
self.panel = wx.Panel(self, wx.ID_ANY)
sizer = wx.BoxSizer(wx.VERTICAL)
self.static_text_ctrl = StaticTextCtrl(self.panel, wx.ID_ANY, "This is a custom StaticTextCtrl")
sizer.Add(self.static_text_ctrl, 0, wx.TOP, 16)
self.text_ctrl = wx.TextCtrl(self.panel, wx.ID_ANY, "Paste here...")
self.text_ctrl.SetMinSize((160, 27))
sizer.Add(self.text_ctrl, 0, wx.TOP, 16)
self.panel.SetSizer(sizer)
self.Layout()
class MyApp(wx.App):
def OnInit(self):
self.frame = MyFrame(None, wx.ID_ANY, "")
self.frame.Show()
return True
if __name__ == "__main__":
app = MyApp(0)
app.MainLoop()
Tested on Python 3.10.6 + wxPython 4.2.0 gtk3 (phoenix) wxWidgets 3.2.0 + Linux Mint 21
Here is an alternative suggestion. It uses the GenStaticText class and overrides its AcceptsFocus()
method to return True. You have to click on one of the MyGenStaticText controls in the demo and then press Ctrl+C
. Then you can paste the text from the clipboard into the destination text control.
import wx
from wx.lib.stattext import GenStaticText
class MyGenStaticText(GenStaticText):
def AcceptsFocus(self):
return True
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__(self, *args, **kwds)
self.SetSize((400, 300))
self.SetTitle("Test MyGenStaticText")
self.panel = wx.Panel(self, wx.ID_ANY)
sizer = wx.BoxSizer(wx.VERTICAL)
self.gen_static_text_1 = MyGenStaticText(self.panel, wx.ID_ANY, label="MyGenStaticText1")
sizer.Add(self.gen_static_text_1, 0, wx.TOP, 16)
self.gen_static_text_2 = MyGenStaticText(self.panel, wx.ID_ANY, label="MyGenStaticText2")
sizer.Add(self.gen_static_text_2, 0, wx.TOP, 16)
self.text_ctrl_2 = wx.TextCtrl(self.panel, wx.ID_ANY, "Paste text here...")
self.text_ctrl_2.SetMinSize((200, 27))
sizer.Add(self.text_ctrl_2, 0, wx.TOP, 16)
self.panel.SetSizer(sizer)
self.Layout()
self.gen_static_text_1.Bind(wx.EVT_KEY_UP, self.OnChar)
self.gen_static_text_2.Bind(wx.EVT_KEY_UP, self.OnChar)
def OnChar(self, event):
keycode = event.GetKeyCode()
if event.GetModifiers() == wx.MOD_CONTROL:
if keycode == ord('C'):
static_text = event.GetEventObject()
label = static_text.GetLabelText()
clip_data = wx.TextDataObject()
clip_data.SetText(label)
wx.TheClipboard.Open()
wx.TheClipboard.SetData(clip_data)
wx.TheClipboard.Close()
else:
event.Skip()
else:
event.Skip()
class MyApp(wx.App):
def OnInit(self):
self.frame = MyFrame(None, wx.ID_ANY, "")
self.frame.Show()
return True
if __name__ == "__main__":
app = MyApp(0)
app.MainLoop()
Tested on Python 3.10.6 + wxPython 4.2.0 gtk3 (phoenix) wxWidgets 3.2.0 + Linux Mint 21
Thanks for the suggestions.
Can’t the application itself catch keyboard events? Then, I could simply read what it says in the label.
Here’s what I use to close the app when the use hits Escape:
def OnKeyUP(self, event):
keyCode = event.GetKeyCode()
if keyCode == wx.WXK_ESCAPE:
self.Close()
event.Skip()
Edit: What about this?
If you do that, how will you know that the user intended the Ctrl-C to refer to the wx.StaticText
, and not some other control?
The advice to use a TextCtrl
with TE_READONLY
is good. I would hesitate to add BORDER_NONE
though, unless you really want to make your app a frustrating guessing game in modern design style.
Actually, I don’t care: I simply need to catch CTRL+C somehow, and copy the content of a specific label into the clipboard.
Edit: It displays “308” when I hit CTRL, and “67” when I add “C”, but it doesn’t see CTRL+C
self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyUP) #ESC closes app
self.Show()
#CTRL=308, C=67
def OnKeyUP(self, event):
keyCode = event.GetKeyCode()
print("Keycode is ",keyCode)
#Close app if hit ESC
if keyCode == wx.WXK_ESCAPE:
self.Close()
if keyCode == wx.WXK_CONTROL_C:
print("Found !")
event.Skip()
Since wx.WXK_CONTROL_C doesn’t work, I found a work-around:
def OnKeyUP(self, event):
keyCode = event.GetKeyCode()
#print("Keycode is ",keyCode)
"""
#not called
if keyCode == wx.WXK_CONTROL_C:
print("Found !")
"""
#ord() returns integer representing Unicode character
if keyCode in (ord('C'), ord('c')):
if wx.GetKeyState(wx.WXK_CONTROL):
print("Found !!")
event.Skip()