PyQt5 应用在 TeamViewer 下无法使用全屏模式

问题描述

使用 PyQt5 (版本为 5.7)中的 QtWebEngineView 构建的桌面应用,部署到远程机器(Windows 7 平台)上以全屏模式运行时,通过 teamviewer 观察到远程桌面没有变化,但是鼠标右键后会弹出菜单选项,与正常打开的 QtWebEngineView 弹出的菜单选项一致。而且直接在远程机器上运行的应用程序在显示器上显示正常,但是无法通过 teamviewer 远程观察。

部分代码:

view = QtWebEngineView()
view.setWindowFlags(Qt.FramelessWindowHint)
view.setUrl(QUrl("http://localhost:{}/".format(port)))
view.show()

分析问题并解决新产生的问题

推测是 teamviewer 无法捕捉到应用全屏后的界面,使用 Print Screen 按键截图后显示的图片也表示该应用的界面无法被捕捉。由于应用的使用场景需要,应用必须全屏,运行时不能漏出 Windows 的任务栏。所以尝试了多种解决方案:

  1. view.showFullScreen() 这种方法尝试过,但很快就发现有问题,并且不仅仅是 PyQt5 构建的应用无法远程监控,包括直接使用 Qt C++ 的代码编译的程序也无法通过远程监控。推测是 QWebEngineView 的原因导致的。
  2. 根据目标机器的屏幕分辨率,调整应用窗口的大小,比如目标机器的分辨率为 1024x768,在高度上减少一个像素,使应用窗口为 1024x767,然后使用 view.show(),即可显示,并能通过 teamviewer 远程观察。

第二种方法是可行的,但它仍然存在一个问题,Windows 桌面底部的任务栏会把应用程序的界面挡住。为了让这个应用程序界面置顶,需要设置窗口的标志位为 Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint

然后程序就可以正常运行了。但是新的问题又出现了,使用了 Qt.WindowStaysOnTopHint 标志位后,程序确实能够置顶,并且不会被任务栏挡住,但是当使用 alt + tab 组合键切换程序时,其他程序也会被该应用遮挡住,导致无法显示。(在实际使用中没有关系,但是在开发过程中有些头疼)

为了保持应用窗口的置顶效果,并使切换应用时,PyQt 应用不再置顶,可以定义一个 QWebEngineView 的子类,重写 changeEvent 方法:

class MyWebView(QWebEngineView):
def __init__(self, app):
super(MyWebView, self).__init__()
self.app = app def changeEvent(self, event):
if event.type() == QEvent.ActivationChange:
active = self.isActiveWindow()
if active:
self.setWindowFlags( self.windowFlags() | Qt.WindowStaysOnTopHint )
if not self.isVisible():
self.show()
self.activateWindow()
else:
self.setWindowFlags( self.windowFlags() & ~Qt.WindowStaysOnTopHint )
if not self.isVisible():
self.show()

changeEvent 中,处理窗口激活事件,当窗口处于激活状态时,在标志位中增加 Qt.WindowStaysOnTopHint,否则就去除这个标志位。当窗口的标志位改变时,窗口将会处于不可见的状态(可以打印 self.isVisible() 的值查看),所以需要再次调用 self.show() 方法显示窗口。

这里有个很坑的地方,如果不设置窗口的属性,调用 show() 方法后会自动激活窗口,即 self.isActiveWindow() 会返回 True,首次打开应用时这是理所应当的。但是当切换几次后就会发现窗口将会永远置顶(或者没法置顶了)。分析一下 changeEvent 的代码流程就清楚了。

永远置顶的情况是:

  1. 首次打开应用时,窗口处于 visible、active 状态;
  2. 切换应用时,此时将会触发 changeEvent 事件,调用 MyWebView 实例的 changeEvent() 方法,判断当前窗口是非激活状态的,因此修改标志位为 self.windowFlags() & ~Qt.WindowStaysOnTopHint,修改标志位后重新显示窗口,调用 self.show() 方法,显示窗口。
  3. 由于 show() 方法调用后,会自动激活窗口,Qt 的事件循环机制导致 changeEvent() 方法再次被调用,此时窗口处于激活状态,将重新修改标志位为 self.windowFlags() | Qt.WindowStaysOnTopHint,使得切换窗口后,其他程序仍然会被本应用遮挡。

永不置顶的情况是最后不论是哪种状态,标志位都会被改成不含 Qt.WindowStaysOnTopHint 的,因此就永远无法置顶了。

为了解决这个问题,就必须要将 show() 的调用和窗口激活分开,为此需要设置窗口的属性为:

view.setAttribute( Qt.WA_ShowWithoutActivating )

这样就可以实现在激活应用时窗口置顶,切换到其他程序时,应用窗口不置顶(如果其他应用显示的窗口下,可以看到本应用将堆叠在其他程序下面)。

关闭程序

上述代码解决了窗口置顶和显示其他程序时不置顶的需求,但是当你打开任务栏想要关闭程序的时候,发现程序关不掉了。只好重写 closeEvent() 方法:

def closeEvent(self, event):
self.activateWindow()
self.destroy()
self.app.quit()
from time import sleep
sleep(1)
sys.exit(0)

当点击窗口关闭按钮(实际上是没有的,因为会被设置成无边框窗口),或在任务栏中点击右键关闭时,程序将会终止,退出 Python 进程。

其他值得提及的部分

在主函数中,有行代码是特意加上去的:

view.settings().setAttribute( QWebEngineSettings.Accelerated2dCanvasEnabled, False)

这是因为远程机器没有显卡,因此无法使用显卡加速功能,但 QWebEngineView 如果默认会启用 QWebEngineSettings.Accelerated2dCanvasEnabled 这个属性,导致程序在远程机器上运行的时候,图表显示的功能会出现异常。因此需要显式关闭该属性。

小结

Qt 的界面为 Python 桌面应用开发带来了很多方便,但是由于环境的差异导致程序会出现一些不可名状的异常。在资料比较少的情况下,自己尝试摸索出代码的执行流程并且给出解决方案其实是挺耗时间的。不过好在最终成功解决了问题。

参考代码可在 github 上获取。

PyQt5 应用在 TeamViewer 下无法使用全屏模式的更多相关文章

  1. centos6下无法使用lsof命令"-bash: lsof: command not found"

    1. 问题描述在centos下, 无法使用命令lsof, 出现以下信息:# lsof -i:3690-bash: lsof: command not found2. 解决方法我们可以通过yum来安装: ...

  2. aspupload ,在winows server 2008 下无法使用

    aspupload ,在winows server 2008 下无法使用.求助解决办法 2014-01-12 13:31 goolean | 浏览 775 次 操作系统 aspupload64位,安装 ...

  3. 解决Windows x64bit环境下无法使用PLSQL Developer连接到Oracle DB中的问题

    本文是原创文章,转载请注明出处: http://blog.csdn.net/msdnchina/article/details/46416455 解决Windows x64bit环境下无法使用PLSQ ...

  4. 关于Markdown下无法使用表格的解决方案

    关于Markdown下无法使用表格的解决方案 写表格,出现如下场景 解决方法.点击左下角M的表示,切换到extra模式 打开了新世界.如果不能点击,估计是你没有激活pro的权限,百度下就可以了. 或者 ...

  5. 解决JSON.stringify()在IE10下无法使用的问题

    今天在IE10下遇到了JSON.stringify()无法使用的问题,错误信息为:'JSON' is undefined . 开始以为是没有添加json2.js引用的原因.后来发现,其他地方也没添加j ...

  6. 解决Oracle在命令行下无法使用del等键问题

    前言: Oracle使用Linux命令行进行编辑? 有PL/SQL development,SQL development等工具,为何用Linux命令行? 但也免不了有用的的时候 以下是解决在Linu ...

  7. PyQt5之使用Qt下的designer工具将.ui文件转换成.py文件后添加什么东西后方可运行

    首先证明我是加了那些鬼东西以后可以成功运行的. 然后来叙述一下我的过程. 这是一个.ui文件生成的.py文件.(把主要的内容省去了,但是没有影响结构) # -*- coding: utf-8 -*- ...

  8. [linux]关于deepin截图软件在KDE桌面下无法使用粘贴的解决方法

    -------更新----- 1.其实不如直接关闭klipper启动程序 # rm -rf /usr/share/plasma/plasmoids/org.kde.plasma.clipboard 2 ...

  9. Winio驱动在64位windows下无法使用的解决方法

    C#在使用WinIo的驱动开发类似按键精灵一类工具的时候,需要对相关的驱动进行注册才能正常启动,找了下资料,资料来自: http://jingyan.baidu.com/article/642c9d3 ...

随机推荐

  1. Centos7 因内存 可用大小不足,被killed的解决办法

    Linux的内存分配采取的是一种更加积极的分配策略,它假设应用申请了内存空间后并不会立即去使用它,所以允许一定量的超售,当应用真的需要使用它的时候,操作系统可能已经通过回收了其他应用的内存空间而变得有 ...

  2. ES6—— iterator和for-of循环

    Iterator 遍历器的作用:为各种数据结构,提供一个同意的,简便的访问接口.是的数据结构的成员能够按某种次序排列.ES6 新增了遍历命令 for...of 循环,Iterator接口主要供 for ...

  3. Swagger简单实例

    随着技术的不断发展,网站框架也开始向:前后端分离的形态发展,而且前端技术和后端技术在各自的道路上越走越远.而web api 接口成了前后端唯一的联系.所以web api会变得越来越重要. 那么什么是W ...

  4. Visual Studio 2008 SP1键盘F10单步调试超慢解决方法

    症状: 中断程序调试时,F10或者其它键盘操作都超级慢. 鼠标点击工具栏的按钮速度正常. 解决方法: 网上说的什么删掉所有断点啦,关掉几个窗口啦,重置用户设置啦,关掉某某调试选项啦,关掉防火墙啦,都是 ...

  5. Oracle.ManagedDataAccess.dll方式操作oracle数据库

    Oracle.ManagedDataAccess.dll方式操作oracle数据库 一.查询语句: using (OracleConnection conn = new OracleConnectio ...

  6. .CHM文件使用问题

    1.查看时内容为空---解除锁定 解决方案:选中xx.CHM,右键点击属性,常规选项卡中点击“解除锁定”.OK了 2.内容显示乱码---修改浏览器编码 IE中的查看 > 编码 > 自动选择 ...

  7. Linux 开启路由转发功能

    想让一台Red Hat Enterprise Linux 7开通iptables的nat转发功能 A服务器:192.168.30.20/24 B服务器:192.168.30.1/24,eth0; 19 ...

  8. ubuntu15.04下安装jdk8

    前几天手贱,删掉了ubuntu自带的java,最后安装时遇到了Picked up JAVA_TOOL_OPTIONS的问题,经过网上各种找,终于被我弄成功了.下面将经验下载下面供大家方便: jdk8的 ...

  9. Using Request Headers for Metadata Address

    问题描述 我将一个在本地调试正常的service部署到服务器后遇到了添加服务引用失败的问题.在把配置文件中基址使用的localhost替换成服务器的ip地址后问题得到了解决.但我感觉这并不是一个因为粗 ...

  10. 使用ceph-deploy进行ceph安装

    ceph安装包介绍: 1.ceph-deploy: ceph的部署软件,通过该软件可以简便部署,这个软件并非整个ceph集群系统中必须的 2.ceph: ceph整个服务集群中的每个节点必须的软件.提 ...