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. jQuery,Table表头固定插件chromatable存在的问题及解决办法

    在最近的项目中需要将表格的表头固定,搜寻了大量的资料,发现chromatable插件还是比较方便的.但是当我在一个页面中多次使用 chromatable固定对个表格的表头时问题就出现了,首先说明系统的 ...

  2. [Erlang08] 使用Erlang application有什么好处?

    问题: 当我们把一个项目中所有的supervision tree通过一个简单的函数game: start(),会发现这个树结构特别复杂,只能有一个根节点,然后一直扩展. 那么有时,我们会需要:有些功能 ...

  3. WPF 实现INotifyPropertyChanged .Net Framework 4.5

    自己动手写了一个基类来实现INotifyPropertyChanged接口,以后可以直接使用. using System.ComponentModel; using System.Runtime.Co ...

  4. cobbler PXE 安装系统时出现的问题

    cobbler PXE 安装系统时出现的问题 1.安装包未找到.解决方法:ios镜像里没有软件包,换掉该软件包的ios镜像,或者在ks.cfg 文件里删去或注释掉%packages 里指定安装的软件包 ...

  5. nowcoder(牛客网)普及组模拟赛第一场 解题报告

    蒟蒻我可能考了一场假试 T1 绩点 这题没什么好说的,应该是只要会语言的就会做. T2 巨大的棋盘 一个模拟题吧qwq,但是要注意取模的时候先加上n或者m再取模,要不然会错的. #include< ...

  6. 波利亚(Polya)罐子模型

    (波利亚(Polya)罐子模型)罐中有a个白球,b个黑球,每次从罐中随机抽取一球,观察其颜色后,连同附加的c个同色球   (波利亚(Polya)罐子模型)罐中有a个白球,b个黑球,每次从罐中随机抽取一 ...

  7. 在Java中如何优雅地判空

    判空灾难 ​ 作为搬砖党的一族们,我们对判空一定再熟悉不过了,不要跟我说你很少进行判空,除非你喜欢NullPointerException. 不过NullPointerException对于很多猿们来 ...

  8. cookie的优缺点

    优点  :极高的扩展性和可用性 1.通过良好的编程,控制保存在cookie中的session对象的大小. 2.通过加密和安全传输技术(ssl),减少cookie被破解的可能性 3.只有cookie中存 ...

  9. fdisk命令总结

    fdisk - Partition table manipulator for Linux 一.通过fdisk -l 查看机器所挂硬盘个数及分区情况: fdisk 能划分磁盘成为若干个区,同时也能为每 ...

  10. loj #6515. 「雅礼集训 2018 Day10」贪玩蓝月

    \(\color{#0066ff}{输入样例}\) 0 11 10 QU 0 0 QU 1 9 IG 14 7 IF 3 5 QU 0 9 IG 1 8 DF QU 0 4 IF 1 2 DG QU ...