简体   繁体   中英

Kivy copy button text to clipboard

This is my first Kivy app, surprisingly there wasn't the usual amount of documentation on copy/paste button text with the clipboard which I assume because it is simple, however I get a traceback saying ValueError embedded null character.

I thought that it was because the button produces text of recently hashed text and it still was contained in a byte string, but when decoding it acts if its already decoded and states string doesn't have a decode attribute. I apologize in advance for any "playing" in my code and if the answer has been staring at me

kivy clipboard doc: https://kivy.org/docs/api-kivy.core.clipboard.html#

** Update I believe I found the issue, regardless of the data type that is passed to the clipboard function there is a Value error, I took a look at the kivy file for the clipboard "clipboard_winctypes.py" and under the put() function the function msvcrt.wcscpy_s() is called. When this is commented out the clipboard will copy the button text however, I receive weird things like ⫐ᵄƅ

also under the put() function where text is set to text += u'x00', if this is commented out and msvcrt.wscpy_s() is left uncommented to be called it executes without error but nothing is copied to the clipboard however msvcrt is an object of ctypes.cdll.msvcrt and I don't where to go from here

clipboard_winctypes.py:

'''
Clipboard windows: an implementation of the Clipboard using ctypes.
'''

__all__ = ('ClipboardWindows', )

from kivy.utils import platform
from kivy.core.clipboard import ClipboardBase

if platform != 'win':
    raise SystemError('unsupported platform for Windows clipboard')

import ctypes
from ctypes import wintypes
user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32
msvcrt = ctypes.cdll.msvcrt
c_char_p = ctypes.c_char_p
c_wchar_p = ctypes.c_wchar_p


class ClipboardWindows(ClipboardBase):

    def get(self, mimetype='text/plain'):
        GetClipboardData = user32.GetClipboardData
        GetClipboardData.argtypes = [wintypes.UINT]
        GetClipboardData.restype = wintypes.HANDLE

        user32.OpenClipboard(user32.GetActiveWindow())
        # 1 is CF_TEXT
        pcontents = GetClipboardData(13)
        if not pcontents:
            return ''
        data = c_wchar_p(pcontents).value.encode(self._encoding)
        user32.CloseClipboard()
        return data

    def put(self, text, mimetype='text/plain'):
        text = text.decode(self._encoding)  # auto converted later
        text += u'\x00'

        SetClipboardData = user32.SetClipboardData
        SetClipboardData.argtypes = [wintypes.UINT, wintypes.HANDLE]
        SetClipboardData.restype = wintypes.HANDLE

        GlobalAlloc = kernel32.GlobalAlloc
        GlobalAlloc.argtypes = [wintypes.UINT, ctypes.c_size_t]
        GlobalAlloc.restype = wintypes.HGLOBAL

        CF_UNICODETEXT = 13

        user32.OpenClipboard(user32.GetActiveWindow())
        user32.EmptyClipboard()
        hCd = GlobalAlloc(0, len(text) * ctypes.sizeof(ctypes.c_wchar))
        msvcrt.wcscpy_s(c_wchar_p(hCd), len(text), c_wchar_p(text))
        SetClipboardData(CF_UNICODETEXT, hCd)
        user32.CloseClipboard()

    def get_types(self):
        return ['text/plain']

cry_hash.py

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.label import Label
import hashlib


class Hasher:
    def __init__(self, to_hash, hash_alg, hash_length):
        if to_hash != type(bytes):
            self.to_hash = bytes(to_hash, encoding='utf-8')
        else:
            self.to_hash = to_hash
        self.hash_alg = hash_alg
        self.hash_length = int(hash_length)

    def create_hash(self):
        hash_object = hashlib.new(self.hash_alg)
        hash_object.update(self.to_hash)
        result = hash_object.hexdigest()[:self.hash_length]
        del hash_object
        return result


class LabelBackground(Label):
    pass


class CryHashWidgetBoxLayout(BoxLayout):
    def get_hash(self, user_hash, hash_length):
        tb = next((t for t in ToggleButton.get_widgets('hash_type') if  t.state == 'down'), None)
        hash_alg = tb.text if tb else None
        krypt_tool = Hasher(user_hash, hash_alg, hash_length)
        hashed_input = krypt_tool.create_hash()

        self.ids.hash_return.text = hashed_input

    def reset(self, text_reset):
        incoming = text_reset
        del incoming
        incoming = ''
        self.ids.hash_return.text = incoming


class CryHashApp(App):
    def build(self):
        return CryHashWidgetBoxLayout()


if __name__ == '__main__':
    CryHashApp().run()

KV file: cryhash.kv

#File name: cry_hash.py
#:import utils kivy.utils
#:import Clipboard kivy.core.clipboard.Clipboard


<ToggleButton>:
    background_color: utils.get_color_from_hex('#E00000')

<TextInput>:
    background_color: utils.get_color_from_hex('#5F9B9F')

<Label>
    font_name: 'fonts/arialbd.ttf'

<CryHashWidgetBoxLayout>:
    orientation: 'vertical'

    Label:
        font_name: 'fonts/welga.ttf'
        color: utils.get_color_from_hex('#E00000')
        text: 'Welcome to Cry Hash!'
        font_size: 80

    Button:
        id: hash_return
        background_color: utils.get_color_from_hex('#F15E92')
        font_size: 40
        text: ''
        on_release:
            Clipboard.copy(hash_return.text)

    BoxLayout:
        orientation: 'horizontal'
        BoxLayout:
            orientation: 'vertical'

            Label:
                id: bg_hash
                color: utils.get_color_from_hex('#E00000')
                text: 'Enter text to hash'

            TextInput:
                id: user_hash
                multiline: False
                text: ''


            Label:
                id: bg_length
                color: utils.get_color_from_hex('#E00000')
                text: 'Enter length'

            TextInput:
                id: get_hash_length
                multiline: False
                text: '10'

            Button:
                id: get_data
                background_color: utils.get_color_from_hex('#1900FF')
                text: 'get hash!'
                on_release: root.get_hash(user_hash.text, get_hash_length.text)

        BoxLayout:
            orientation: 'vertical'

            ToggleButton:
                id: SHA256
                text: 'SHA256'
                state: 'down'
                group: 'hash_type'

            ToggleButton:
                id: SHA512
                text: 'SHA512'
                group: 'hash_type'

            ToggleButton:
                id: SHA1
                text: 'SHA1'
                group: 'hash_type'

            ToggleButton:
                id: MD5
                text: 'MD5'
                group: 'hash_type'

To summarize other answers:

Issue :

"ValueError: embedded null character" when using copy to clipboard (Kivy)

Solution :

  1. pip install pyperclip
  2. pyperclip.copy('text that you want into clipboard')

I have found a work around, I believe that is just simply a bug in the Kivy framework, if someone can find a true solution in the kivy code please let me know, otherwise I simply imported pyperclip, created a pyperclip copy function in the .py file and called to the function in the .kv file. Works flawlessly!

cry_hash.py:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.label import Label
import hashlib
import pyperclip

class Hasher:
    def __init__(self, to_hash, hash_alg, hash_length):
        if to_hash != type(bytes):
            self.to_hash = bytes(to_hash, encoding='utf-8')
        else:
            self.to_hash = to_hash
        self.hash_alg = hash_alg
        self.hash_length = int(hash_length)

    def create_hash(self):
        hash_object = hashlib.new(self.hash_alg)
        hash_object.update(self.to_hash)
        result = hash_object.hexdigest()[:self.hash_length]
        del hash_object
        return result


class LabelBackground(Label):
    pass


class CryHashWidgetBoxLayout(BoxLayout):
    def get_hash(self, user_hash, hash_length):
        tb = next((t for t in ToggleButton.get_widgets('hash_type') if t.state == 'down'), None)
        hash_alg = tb.text if tb else None
        krypt_tool = Hasher(user_hash, hash_alg, hash_length)
        hashed_input = krypt_tool.create_hash()
        self.ids.hash_return.text = str(hashed_input)

    def reset(self, text_reset):
        incoming = text_reset
        del incoming
        incoming = ''
        self.ids.hash_return.text = incoming

    def copy_text(self, text):
        pyperclip.copy(text)


class CryHashApp(App):
    def build(self):
        return CryHashWidgetBoxLayout()


if __name__ == '__main__':
    CryHashApp().run()

cryhash.kv:

#File name: cry_hash.py
#:import utils kivy.utils
#:import Clipboard kivy.core.clipboard.Clipboard


<ToggleButton>:
    background_color: utils.get_color_from_hex('#E00000')

<TextInput>:
    background_color: utils.get_color_from_hex('#5F9B9F')

<Label>
    font_name: 'fonts/arialbd.ttf'

<CryHashWidgetBoxLayout>:
    orientation: 'vertical'

    Label:
        font_name: 'fonts/welga.ttf'
        color: utils.get_color_from_hex('#E00000')
        text: 'Welcome to Cry Hash!'
        font_size: 80

    Button:
        id: hash_return
        background_color: utils.get_color_from_hex('#F15E92')
        font_size: 40
        text: ''
        on_release: root.copy_text(hash_return.text)

    BoxLayout:
        orientation: 'horizontal'
        BoxLayout:
            orientation: 'vertical'

            Label:
                id: bg_hash
                color: utils.get_color_from_hex('#E00000')
                text: 'Enter text to hash'

            TextInput:
                id: user_hash
                multiline: False
                text: ''


            Label:
                id: bg_length
                color: utils.get_color_from_hex('#E00000')
                text: 'Enter length'

            TextInput:
                id: get_hash_length
                multiline: False
                text: '10'

            Button:
                id: get_data
                background_color: utils.get_color_from_hex('#1900FF')
                text: 'get hash!'
                on_release: root.get_hash(user_hash.text, get_hash_length.text)

        BoxLayout:
            orientation: 'vertical'

            ToggleButton:
                id: SHA256
                text: 'SHA256'
                state: 'down'
                group: 'hash_type'

            ToggleButton:
                id: SHA512
                text: 'SHA512'
                group: 'hash_type'

            ToggleButton:
                id: SHA1
                text: 'SHA1'
                group: 'hash_type'

            ToggleButton:
                id: MD5
                text: 'MD5'
                group: 'hash_type'

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM