cefpython icon indicating copy to clipboard operation
cefpython copied to clipboard

cef.DpiAware.EnableHighDpiSupport() doesn't work well. Update pyinstaller example. Update docs.

Open amstaff8 opened this issue 6 years ago • 21 comments

Hi

I can't get cef python to work with dpi adware. I have cef python 66 with pywin32 224 (python 3.6.8).

I added the method cef.DpiAware.EnableHighDpiSupport() after line 28 of your example cefpython/examples/pywin32.py

But it doesn’t work (see the screenshot). I’ve tested it also with PyQt but the result is the same.

21_06_2019__12_12_02 21_06_2019__12_13_06

amstaff8 avatar Jun 24 '19 09:06 amstaff8

The wxpython.py example supports High DPI by default. Does it work for you?

Please provide exact steps to reproduce your issue from the screenshot.

cztomczak avatar Jun 24 '19 11:06 cztomczak

Same result. My steps:

  1. I’ve downloaded cefpython/examples/wxpython.py
  2. I ran pip install wxpython (and it has installed wxpython-4.0.6)
  3. I ran the command: python wxpython.py

This is the output:

[wxpython.py] System DPI settings: (192, 192) [wxpython.py] wx.GetDisplayPPI = (267, 267) [wxpython.py] wx.GetDisplaySize = (2736, 1824) [wxpython.py] MainFrame declared size: (900, 640) [wxpython.py] MainFrame DPI scaled size: (1800, 1280) [wxpython.py] MainFrame actual size: (1800, 1280)

And this is the result:

24_06_2019__16_38_34

This is my resolution:

24_06_2019__16_48_47

amstaff8 avatar Jun 24 '19 14:06 amstaff8

This is DPI awareness issue. The example sets DPI awareness via a call to cef.DpiAware.EnableHighDpiSupport, but the call occurs too late in Python programs on some machines, it's a race condition. DPI awareness needs to be set for the executable before Python program executes. Same for the subprocess.exe executable that runs the Renderer subprocess. If you remove the call to EnableHighDpiSupport you will get rid of double rendering, but it will result in a different rendering issue with app appearing blurry.

It is recommended to set High DPI awareness through manifest that is attached inside or next to executable. Such manifest must be added for both main executable and subprocess executable. Sometimes it is too late to set DPI awareness during runtime, so it must be set through manifest. If you want to fix the issue on a developer machine then go find python.exe executable and set appropriate file properties. On Windows 7 select "Compatibility" tab and check "Disable display scaling on high DPI settings".

For example to set DPI awareness through manifest create two files: myapp.exe.manifest and subprocess.exe.manifest in the same directory where myapp.exe and subprocess.exe executables reside. Edit both of these manifest files, so that they contain these contents:

<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"></supportedOS> 
      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"></supportedOS> 
      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"></supportedOS> 
      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"></supportedOS> 
    </application>
  </compatibility>

  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
        <requestedExecutionLevel level="asInvoker"></requestedExecutionLevel>
      </requestedPrivileges>
    </security>
  </trustInfo>

  <asmv3:application>
    <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
      <dpiAware>true/PM</dpiAware>
    </asmv3:windowsSettings>
  </asmv3:application>

</asmv1:assembly>

Some users report that this still doesn't work for them and it works only after embedding manifest inside the exe with such command:

mt.exe -nologo -manifest myapp.exe.manifest -outputresource:"myapp.exe;1"
mt.exe -nologo -manifest subprocess.exe.manifest -outputresource:"subprocess.exe;1"

When using pyinstaller you might not be able to embed manifest inside myapp.exe executable, in my case it resulted in errors. In such case you have to modify the existing manifest file that is placed next to exe, it was generated by pyinstaller. You cannot overwrite it entirely with a new file, because it contains other necessary options, so manual editing is required. Embedding manifest inside subprocess.exe executable will alway work fine.

cztomczak avatar Jun 24 '19 15:06 cztomczak

I've recently spent some time working through a similar issue in our cefpython-based app.

The app is intended to run on a Dell tablet running Windows 10 where the system Display Scale setting is very likely to be greater than 100%. It is using CEF 66, python 3.7.3 and wxPython 4.0.6.

The app was exhibiting the rendering artefact described and illustrated in this issue whereby the UI is drawn twice - once at the desired scale, and a duplicated, smaller version aligned in the top left of the screen. Touch and mouse events would go to the right place in the full-sized UI, but the updates would only be rendered in the smaller version.

Setting a chrome switch to disable GPU compositing has 'fixed' this issue.

Using any of these switches 'fixes' the issue.

--disable-gpu
--disable-gpu-compositing
--disable-gpu-driver-bug-workarounds

The solution in my situation is;

cef.Initialize(settings={}, switches={'disable-gpu-compositing': None})

I've seen this issue on Dell machines with integrated Intel GPUs and with an Nvidia card.

I haven't identified a root cause.

pi-slh avatar Jul 31 '19 03:07 pi-slh

@pi-slh Have you embedded a DPI aware manifest in your app? See my comment: https://github.com/cztomczak/cefpython/issues/530#issuecomment-505066492

cztomczak avatar Jul 31 '19 05:07 cztomczak

@cztomczak I saw the comment and tried putting a .manifest next to both the python.exe and the subprocess.exe that are the entry points for our app. I'm not sure that they are doing anything - my reading suggests the python.exe has an embedded manifest and therefore ignores the .manifest next to it. We're not building our own executable at the moment, just running python.exe scriptname.py to startup. I've been unable to get the layout I want in the browser by setting the High DPI scaling override on the application executable, so I haven't looked into manifests any further, assuming that it wouldn't give me the result I was after. Thanks for your attention :)

pi-slh avatar Jul 31 '19 06:07 pi-slh

@pi-slh The issue is with DPI support and fixing it by disabling GPU is not a good idea. Unless you have a different issue. What do you mean by "I've been unable to get the layout I want in the browser by setting the High DPI scaling override on the application executable"? Are you sure you've checked the right option? Have you tested with original wxpython.py example?

cztomczak avatar Jul 31 '19 06:07 cztomczak

For anyone stuck on this, it never worked for me using a manifest file next to the exe, but it did work if I embedded the manifest inside the exe:

mt.exe -nologo -manifest subprocess.exe.manifest -outputresource:"subprocess.exe;1"

dfb avatar Nov 30 '19 21:11 dfb

We should update the pyinstaller example to embed the necessary High DPI manifest files in the exe's. Marking this for next release.

cztomczak avatar Jan 16 '20 13:01 cztomczak

@cztomczak I think I found an easier solution... check this out...

import ctypes

Set DPI Awareness (Windows 10 and 8)

errorCode = ctypes.windll.shcore.SetProcessDpiAwareness(0)

Set DPI Awareness (Windows 7 and Vista)

success = ctypes.windll.user32.SetProcessDPIAware()

More info here: https://stackoverflow.com/questions/44398075/can-dpi-scaling-be-enabled-disabled-programmatically-on-a-per-session-basis

marcelomanzo avatar Apr 13 '20 18:04 marcelomanzo

It might fix the issue, but it's still not guaranteed.

cztomczak avatar Apr 13 '20 18:04 cztomczak

It might fix the issue, but it's still not guaranteed.

FWIW we tried this fix but it didn't work reliably for us - so far only the embedded manifest file approach has worked.

dfb avatar Apr 13 '20 19:04 dfb

It might fix the issue, but it's still not guaranteed.

FWIW we tried this fix but it didn't work reliably for us - so far only the embedded manifest file approach has worked.

It didn't work reliably or didn't work at all for you? Since I am not having any issues with setting dpi scale programmatically

bekatd avatar Oct 13 '20 07:10 bekatd

It didn't work reliably or didn't work at all for you? Since I am not having any issues with setting dpi scale programmatically

IIRC it seemed to work on some machines but not others, or it seemed to work sometimes but not always (which is consistent with the problem being a race condition). But with the manifest file embedded, it has worked reliably.

dfb avatar Oct 13 '20 12:10 dfb

Tried embedding the manifest on subprocess.exe. However, the browser stopped loading, giving the below error

[1005/141244.841:ERROR:browser_gpu_channel_host_factory.cc(119)] Failed to launch GPU process. [1005/141244.859:ERROR:gpu_process_transport_factory.cc(1017)] Lost UI shared context.

bnbchu avatar Oct 05 '21 06:10 bnbchu

@bnbchu What does the manifest look like?

cztomczak avatar Oct 05 '21 16:10 cztomczak

@bnbchu What does the manifest look like?

@cztomczak thanks for the follow-up

I couldn't find a manifest file for subprocess.exe so created one based on the guide above.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>  
  <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
      <application xmlns="urn:schemas-microsoft-com:asm.v3">
          <windowsSettings>
          <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
          <dpiAware>true</dpiAware>
      </windowsSettings>
  </application>
</assembly>

For my main exe, I edited the manifest file generated by pyinstaller.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity name="JD Tool" processorArchitecture="amd64" type="win32" version="1.0.0.0"/>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
      </requestedPrivileges>
    </security>
  </trustInfo>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity language="*" name="Microsoft.Windows.Common-Controls" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" type="win32" version="6.0.0.0"/>
      <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"/>
    </dependentAssembly>
  </dependency>
  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
    </application>
  </compatibility>
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
    </windowsSettings>
  </application>
</assembly>

bnbchu avatar Oct 06 '21 03:10 bnbchu

@bnbchu Try embedding this manifest in subprocess.exe:

<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"></supportedOS> 
      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"></supportedOS> 
      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"></supportedOS> 
      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"></supportedOS> 
    </application>
  </compatibility>

  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
        <requestedExecutionLevel level="asInvoker"></requestedExecutionLevel>
      </requestedPrivileges>
    </security>
  </trustInfo>

  <asmv3:application>
    <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
      <dpiAware>true/PM</dpiAware>
    </asmv3:windowsSettings>
  </asmv3:application>

</asmv1:assembly>

cztomczak avatar Oct 06 '21 15:10 cztomczak

@bnbchu Try embedding this manifest in subprocess.exe:

<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"></supportedOS> 
      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"></supportedOS> 
      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"></supportedOS> 
      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"></supportedOS> 
    </application>
  </compatibility>

  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
        <requestedExecutionLevel level="asInvoker"></requestedExecutionLevel>
      </requestedPrivileges>
    </security>
  </trustInfo>

  <asmv3:application>
    <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
      <dpiAware>true/PM</dpiAware>
    </asmv3:windowsSettings>
  </asmv3:application>

</asmv1:assembly>

@cztomczak That worked! Appreciate the support and much thanks!

bnbchu avatar Oct 06 '21 16:10 bnbchu

Can someone show how to include the manifest data with just a basic cefpython script? I don't have any exe's. Just running a script that uses cefpython that launches an HTML file in the cef browser. Calling: cef.DpiAware.EnableHighDpiSupport() does not work.

allsyntax avatar Jun 30 '22 19:06 allsyntax