[prev in list] [next in list] [prev in thread] [next in thread] 

List:       pykde
Subject:    Re: [PyQt] QMacPasteboardMime
From:       Michael Herrmann <michael () herrmann ! io>
Date:       2018-07-17 6:08:27
Message-ID: CABrKpmD8WJRSA+im4og0ueV52Eg8qSwdN6EeZ1zcDvrpf9eRKw () mail ! gmail ! com
[Download RAW message or body]

[Attachment #2 (multipart/related)]

[Attachment #4 (multipart/alternative)]


On 16 July 2018 at 19:57, Phil Thompson <phil@riverbankcomputing.com> wrote:

> A small, complete example would help.
>

Good suggestion. In creating a self-contained example, I actually figured
out how
to fix the problem. Here is my code in case others are interested:

from PyQt5.QtMacExtras import QMacPasteboardMime

class MacClipboardFix(QMacPasteboardMime):
"""
Work around QTBUG-61562 on Mac, which can be reproduced as follows:

from PyQt5.QtWidgets import QApplication
app = QApplication([])
app.clipboard().setText('Hello')

Now paste into Excel or Pycharm. The text that gets pasted is not just
'Hello' but '\uFEFFHello'.
"""
def __init__(self):
super().__init__(QMacPasteboardMime.MIME_CLIP)
def convertorName(self):
return 'UnicodeTextUtf8Default'
def flavorFor(self, mime):
if mime == 'text/plain':
return 'public.utf8-plain-text'
parts = mime.split('charset=', 1)
if len(parts) > 1:
charset = parts[1].split(';', 1)[0]
if charset == 'system':
return 'public.utf8-plain-text'
if charset in ('iso-106464-ucs-2', 'utf16'):
return 'public.utf16-plain-text'
return None
def canConvert(self, mime, flav):
return mime.startswith('text/plain') and \
flav in ('public.utf8-plain-text', 'public.utf16-plain-text')
def mimeFor(self, flavor):
if flavor == 'public.utf8-plain-text':
return 'text/plain'
if flavor == 'public.utf16-plain-text':
return 'text/plain;charset=utf16'
return None
def convertFromMime(self, mime, data, flavor):
if flavor == 'public.utf8-plain-text':
return [data.encode('utf-8')]
if flavor == 'public.utf16-plain-text':
return [data.encode('utf-16')]
return []
def convertToMime(self, mime, data, flavor):
if len(data) > 1:
raise ValueError('Cannot handle multiple data members')
data, = data
if flavor == 'public.utf8-plain-text':
return data.decode('utf-8')
if flavor == 'public.utf16-plain-text':
return data.decode('utf-16')
raise ValueError('Unhandled MIME type: ' + mime)

You then need to instantiate this class (and keep the instance around so it
isn't
garbage collected) somewhere in your code.

Thanks Phil, and sorry for unnecessarily having strained your time.

Best,
Michael

On 16 July 2018 at 19:57, Phil Thompson <phil@riverbankcomputing.com> wrote:

> On 16 Jul 2018, at 5:58 pm, Michael Herrmann <michael@herrmann.io> wrote:
> >
> > Hi all,
> >
> > I'm trying to work around QTBUG-61562: On Mac, this bug prepends
> invisible Unicode characters to the clipboard when copy/pasting.
> >
> > The class QMacPasteboardMime lets one customize copy/pasting on Mac. I
> created a subclass of it, which does get picked up by Qt. However, the
> crucial methods canConvert(...) and convertFromMime(...) aren't called.
> Only the more preliminary convertorName() and flavorFor(...) of my subclass
> are invoked.
> >
> > Does anyone have experience with this? Here is my code, adapted from a
> solution posted in the discussion of QTBUG-61652:
> >
> > from PyQt5.QtMacExtras import QMacPasteboardMime
> >
> > class MyMime(QMacPasteboardMime):
> >     def __init__(self):
> >         super().__init__(QMacPasteboardMime.MIME_CLIP)
> >     def convertorName(self):
> >         return 'UnicodeTextUtf8Default'
> >     def flavorFor(self, mime):
> >         if mime == 'text/plain':
> >             return 'public.utf8-plain-text'
> >         i = mime.find('charset=')
> >         if i >= 0:
> >             charset = mime[i + len('charset='):]
> >             charset = charset.split(';', 1)[0]
> >             if charset == 'system':
> >                 return 'public.utf8-plain-text'
> >             if charset in ('iso-10646-ucs-2', 'ut16'):
> >                 return 'public.utf16-plain-text'
> >         return None
> >     def canConvert(self, mime, flav):
> >         return mime.startswith('text/plain') and \
> >             flav in ('public.utf8-plain-text', 'public.utf16-plain-text')
> >     def mimeFor(self, flavor):
> >         if flavor == 'public.utf8-plain-text':
> >             return 'text/plain'
> >         if flavor == 'public.utf16-plain-text':
> >             return 'text/plain;charset=utf16'
> >         return None
> >     def convertToMime(self, mimetype, data, flavor):
> >         if len(data) > 1:
> >             raise ValueError('Cannot handle multiple member data')
> >         first, = data
> >         if flavor == 'public.utf8-plain-text':
> >             return first.decode('utf-8')
> >         if flavor == 'public.utf16-plain-text':
> >             return first.decode('utf-16')
> >         raise ValueError('Unhandled MIME type: ' + mimetype)
> >     def convertFromMime(self, mime, data, flavor):
> >         string = data.toString()
> >         if flavor == 'public.utf-8.plain-text':
> >             return string.encode('utf-8')
> >         if flavor == 'public.utf16-plain-text':
> >             return string.encode('utf-16')
> >         return b''
> >
> > As I said, flavorFor(...) gets called with 'text/plain'. And
> convertorName() gets called. But not canConvert(...) and
> convertFromMime(...).
>
> A small, complete example would help.
>
> Phil




-- 
[image: Inline image 1]

Michael Herrmann, MSc
Vienna, Austria
Follow me on Twitter <https://twitter.com/m_herrmann>

[Attachment #7 (text/html)]

<div dir="ltr"><div class="gmail_extra" \
style="font-size:12.8px;text-decoration-style:initial;text-decoration-color:initial"><span \
class="gmail-im" style="color:rgb(80,0,80)"><div class="gmail_quote">On 16 July 2018 \
at 19:57, Phil Thompson<span>  </span><span dir="ltr">&lt;<a \
href="mailto:phil@riverbankcomputing.com" target="_blank" \
style="color:rgb(17,85,204)">phil@riverbankcomputing.com</a>&gt;</span><span>  \
</span>wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px \
0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div \
class="gmail-m_7833282403167241560gmail-m_3415079511633522678HOEnZb"><div \
class="gmail-m_7833282403167241560gmail-m_3415079511633522678h5"><span \
style="color:rgb(34,34,34)">A small, complete example would \
help.</span><br></div></div></blockquote></div><div \
class="gmail_extra"><br></div></span><div class="gmail_extra">Good suggestion. In \
creating a self-contained example, I actually figured out how</div><div \
class="gmail_extra">to fix the problem. Here is my code in case others are \
interested:</div><div class="gmail_extra"><br></div><div class="gmail_extra"><div \
class="gmail_extra">from PyQt5.QtMacExtras import QMacPasteboardMime</div><div \
class="gmail_extra"><br></div><div class="gmail_extra">class \
MacClipboardFix(<wbr>QMacPasteboardMime):</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">	</span>&quot;&quot;&quot;</div><div \
class="gmail_extra"><span style="white-space:pre-wrap">	</span>Work around \
QTBUG-61562 on Mac, which can be reproduced as follows:</div><div \
class="gmail_extra"><br></div><div class="gmail_extra"><span \
style="white-space:pre-wrap">		</span>from PyQt5.QtWidgets import \
QApplication</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">		</span>app = QApplication([])</div><div \
class="gmail_extra"><span \
style="white-space:pre-wrap">		</span>app.clipboard().setText(&#39;<wbr>Hello&#39;)</div><div \
class="gmail_extra"><br></div><div class="gmail_extra"><span \
style="white-space:pre-wrap">	</span>Now paste into Excel or Pycharm. The text that \
gets pasted is not just</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">	</span>&#39;Hello&#39; but \
&#39;\uFEFFHello&#39;.</div><span class="gmail-im" style="color:rgb(80,0,80)"><div \
class="gmail_extra"><span \
style="white-space:pre-wrap">	</span>&quot;&quot;&quot;</div><div \
class="gmail_extra"><span style="white-space:pre-wrap">	</span>def \
__init__(self):</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">		</span>super().__init__(<wbr>QMacPasteboardMime.MIME_CLIP)</div><div \
class="gmail_extra"><span style="white-space:pre-wrap">	</span>def \
convertorName(self):</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">		</span>return \
&#39;UnicodeTextUtf8Default&#39;</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">	</span>def flavorFor(self, mime):</div><div \
class="gmail_extra"><span style="white-space:pre-wrap">		</span>if mime == \
&#39;text/plain&#39;:</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">			</span>return \
&#39;public.utf8-plain-text&#39;</div></span><div class="gmail_extra"><span \
style="white-space:pre-wrap">		</span>parts = mime.split(&#39;charset=&#39;, \
1)</div><div class="gmail_extra"><span style="white-space:pre-wrap">		</span>if \
len(parts) &gt; 1:</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">			</span>charset = parts[1].split(&#39;;&#39;, \
1)[0]</div><span class="gmail-im" style="color:rgb(80,0,80)"><div \
class="gmail_extra"><span style="white-space:pre-wrap">			</span>if charset == \
&#39;system&#39;:</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">				</span>return \
&#39;public.utf8-plain-text&#39;</div></span><div class="gmail_extra"><span \
style="white-space:pre-wrap">			</span>if charset in (&#39;iso-106464-ucs-2&#39;, \
&#39;utf16&#39;):</div><span class="gmail-im" style="color:rgb(80,0,80)"><div \
class="gmail_extra"><span style="white-space:pre-wrap">				</span>return \
&#39;public.utf16-plain-text&#39;</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">		</span>return None</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">	</span>def canConvert(self, mime, flav):</div><div \
class="gmail_extra"><span style="white-space:pre-wrap">		</span>return \
mime.startswith(&#39;text/plain&#39;) and \</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">			</span>flav in (&#39;public.utf8-plain-text&#39;, \
&#39;public.utf16-plain-text&#39;)</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">	</span>def mimeFor(self, flavor):</div><div \
class="gmail_extra"><span style="white-space:pre-wrap">		</span>if flavor == \
&#39;public.utf8-plain-text&#39;:</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">			</span>return &#39;text/plain&#39;</div><div \
class="gmail_extra"><span style="white-space:pre-wrap">		</span>if flavor == \
&#39;public.utf16-plain-text&#39;:</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">			</span>return \
&#39;text/plain;charset=utf16&#39;</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">		</span>return None</div></span><div \
class="gmail_extra"><span style="white-space:pre-wrap">	</span>def \
convertFromMime(self, mime, data, flavor):</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">		</span>if flavor == \
&#39;public.utf8-plain-text&#39;:</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">			</span>return \
[data.encode(&#39;utf-8&#39;)]</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">		</span>if flavor == \
&#39;public.utf16-plain-text&#39;:</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">			</span>return \
[data.encode(&#39;utf-16&#39;)]</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">		</span>return []</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">	</span>def convertToMime(self, mime, data, \
flavor):</div><div class="gmail_extra"><span style="white-space:pre-wrap">		</span>if \
len(data) &gt; 1:</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">			</span>raise ValueError(&#39;Cannot handle multiple \
data members&#39;)</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">		</span>data, = data</div><div \
class="gmail_extra"><span style="white-space:pre-wrap">		</span>if flavor == \
&#39;public.utf8-plain-text&#39;:</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">			</span>return data.decode(&#39;utf-8&#39;)</div><div \
class="gmail_extra"><span style="white-space:pre-wrap">		</span>if flavor == \
&#39;public.utf16-plain-text&#39;:</div><div class="gmail_extra"><span \
style="white-space:pre-wrap">			</span>return data.decode(&#39;utf-16&#39;)</div><div \
class="gmail_extra"><span style="white-space:pre-wrap">		</span>raise \
ValueError(&#39;Unhandled MIME type: &#39; + mime)</div><div \
class="gmail_extra"><br></div><div class="gmail_extra">You then need to instantiate \
this class (and keep the instance around so it isn&#39;t</div><div \
class="gmail_extra">garbage collected) somewhere in your code.</div><div \
class="gmail_extra"><br></div></div></div><div class="gmail_extra" \
style="font-size:12.8px;text-decoration-style:initial;text-decoration-color:initial">Thanks \
Phil, and sorry for unnecessarily having strained your time.</div><div \
class="gmail_extra" style="font-size:12.8px;text-decoration-style:initial;text-decoration-color:initial"><br></div><div \
class="gmail_extra" style="font-size:12.8px;text-decoration-style:initial;text-decoration-color:initial">Best,</div><div \
class="gmail_extra" style="font-size:12.8px;text-decoration-style:initial;text-decoration-color:initial">Michael</div></div><div \
class="gmail_extra"><br><div class="gmail_quote">On 16 July 2018 at 19:57, Phil \
Thompson <span dir="ltr">&lt;<a href="mailto:phil@riverbankcomputing.com" \
target="_blank">phil@riverbankcomputing.com</a>&gt;</span> wrote:<br><blockquote \
class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc \
solid;padding-left:1ex"><div class="HOEnZb"><div class="h5">On 16 Jul 2018, at 5:58 \
pm, Michael Herrmann &lt;<a \
href="mailto:michael@herrmann.io">michael@herrmann.io</a>&gt; wrote:<br> &gt; <br>
&gt; Hi all,<br>
&gt; <br>
&gt; I&#39;m trying to work around QTBUG-61562: On Mac, this bug prepends invisible \
Unicode characters to the clipboard when copy/pasting.<br> &gt; <br>
&gt; The class QMacPasteboardMime lets one customize copy/pasting on Mac. I created a \
subclass of it, which does get picked up by Qt. However, the crucial methods \
canConvert(...) and convertFromMime(...) aren&#39;t called. Only the more preliminary \
convertorName() and flavorFor(...) of my subclass are invoked.<br> &gt; <br>
&gt; Does anyone have experience with this? Here is my code, adapted from a solution \
posted in the discussion of QTBUG-61652:<br> &gt; <br>
&gt; from PyQt5.QtMacExtras import QMacPasteboardMime<br>
&gt; <br>
&gt; class MyMime(QMacPasteboardMime):<br>
&gt;        def __init__(self):<br>
&gt;              super().__init__(<wbr>QMacPasteboardMime.MIME_CLIP)<br>
&gt;        def convertorName(self):<br>
&gt;              return &#39;UnicodeTextUtf8Default&#39;<br>
&gt;        def flavorFor(self, mime):<br>
&gt;              if mime == &#39;text/plain&#39;:<br>
&gt;                    return &#39;public.utf8-plain-text&#39;<br>
&gt;              i = mime.find(&#39;charset=&#39;)<br>
&gt;              if i &gt;= 0:<br>
&gt;                    charset = mime[i + len(&#39;charset=&#39;):]<br>
&gt;                    charset = charset.split(&#39;;&#39;, 1)[0]<br>
&gt;                    if charset == &#39;system&#39;:<br>
&gt;                          return &#39;public.utf8-plain-text&#39;<br>
&gt;                    if charset in (&#39;iso-10646-ucs-2&#39;, \
&#39;ut16&#39;):<br> &gt;                          return \
&#39;public.utf16-plain-text&#39;<br> &gt;              return None<br>
&gt;        def canConvert(self, mime, flav):<br>
&gt;              return mime.startswith(&#39;text/plain&#39;) and \<br>
&gt;                    flav in (&#39;public.utf8-plain-text&#39;, \
&#39;public.utf16-plain-text&#39;)<br> &gt;        def mimeFor(self, flavor):<br>
&gt;              if flavor == &#39;public.utf8-plain-text&#39;:<br>
&gt;                    return &#39;text/plain&#39;<br>
&gt;              if flavor == &#39;public.utf16-plain-text&#39;:<br>
&gt;                    return &#39;text/plain;charset=utf16&#39;<br>
&gt;              return None<br>
&gt;        def convertToMime(self, mimetype, data, flavor):<br>
&gt;              if len(data) &gt; 1:<br>
&gt;                    raise ValueError(&#39;Cannot handle multiple member \
data&#39;)<br> &gt;              first, = data<br>
&gt;              if flavor == &#39;public.utf8-plain-text&#39;:<br>
&gt;                    return first.decode(&#39;utf-8&#39;)<br>
&gt;              if flavor == &#39;public.utf16-plain-text&#39;:<br>
&gt;                    return first.decode(&#39;utf-16&#39;)<br>
&gt;              raise ValueError(&#39;Unhandled MIME type: &#39; + mimetype)<br>
&gt;        def convertFromMime(self, mime, data, flavor):<br>
&gt;              string = data.toString()<br>
&gt;              if flavor == &#39;public.utf-8.plain-text&#39;:<br>
&gt;                    return string.encode(&#39;utf-8&#39;)<br>
&gt;              if flavor == &#39;public.utf16-plain-text&#39;:<br>
&gt;                    return string.encode(&#39;utf-16&#39;)<br>
&gt;              return b&#39;&#39;<br>
&gt; <br>
&gt; As I said, flavorFor(...) gets called with &#39;text/plain&#39;. And \
convertorName() gets called. But not canConvert(...) and convertFromMime(...).<br> \
<br> </div></div>A small, complete example would help.<br>
<span class="HOEnZb"><font color="#888888"><br>
Phil</font></span></blockquote></div><br><br clear="all"><div><br></div>-- <br><div \
class="gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><div><div \
dir="ltr"><div><div dir="ltr"><div><div dir="ltr"><div dir="ltr"><div><img \
src="cid:ii_148f0c6115a835fe" alt="Inline image 1" width="142" \
height="1"><br></div><div><br></div><div><font color="#999999">Michael Herrmann, \
MSc</font></div><div><font color="#999999">Vienna, Austria</font></div><div><font \
color="#999999"><a href="https://twitter.com/m_herrmann" target="_blank">Follow me on \
Twitter</a></font></div></div></div></div></div></div></div></div></div></div> </div>


["Strich.png" (image/png)]
[Attachment #9 (text/plain)]

_______________________________________________
PyQt mailing list    PyQt@riverbankcomputing.com
https://www.riverbankcomputing.com/mailman/listinfo/pyqt

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic