- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试将代码库从 PyQt5
迁移到 PyQt6
。我在这篇文章中读到(请参阅 https://www.pythonguis.com/faq/pyqt5-vs-pyqt6/ ),所有枚举成员都必须使用其完全限定名称来命名。文章给出了这样的例子:
# PyQt5
widget = QCheckBox("This is a checkbox")
widget.setCheckState(Qt.Checked)
# PyQt6
widget = QCheckBox("This is a checkbox")
widget.setCheckState(Qt.CheckState.Checked)
然后文章继续:
"There are too many updated values to mention them all here. But if you're converting a codebase you can usually just search online for the short-form and the longer form will be in the results."
我明白了。这句话基本上说了一些大意:
"If the Python interpreter runs into an error, and the error turns out to be a short-form enum, you'll likely find the solution online."
我明白了。但这不是我想要迁移代码库的方式。我想要所有短格式枚举的完整列表,然后对每个枚举执行全局搜索和替换。
哪里可以找到这样的列表?
最佳答案
我编写了一个脚本来从 PyQt6 安装中提取所有短格式和相应的完全限定枚举名称。然后它会自动进行转换:
# -*- coding: utf-8 -*-
# ================================================================================================ #
# ENUM CONVERTER TOOL #
# ================================================================================================ #
from typing import *
import os, argparse, inspect, re
q = "'"
help_text = '''
Copyright (c) 2022 Kristof Mulier
MIT licensed, see bottom
ENUM CONVERTER TOOL
===================
The script starts from the toplevel directory (assuming that you put this file in that directory)
and crawls through all the files and folders. In each file, it searches for old-style enums to
convert them into fully qualified names.
HOW TO USE
==========
Fill in the path to your PyQt6 installation folder. See line 57:
pyqt6_folderpath = 'C:/Python39/Lib/site-packages/PyQt6'
Place this script in the toplevel directory of your project. Open a terminal, navigate to the
directory and invoke this script:
$ python enum_converter_tool.py
WARNING
=======
This script modifies the files in your project! Make sure to backup your project before you put this
file inside. Also, you might first want to do a dry run:
$ python enum_converter_tool.py --dry_run
FEATURES
========
You can invoke this script in the following ways:
$ python enum_converter_tool.py No parameters. The script simply goes through
all the files and makes the replacements.
$ python enum_converter_tool.py --dry_run Dry run mode. The script won't do any replace-
ments, but prints out what it could replace.
$ python enum_converter_tool.py --show Print the dictionary this script creates to
convert the old-style enums into new-style.
$ python enum_converter_tool.py --help Show this help info
'''
# IMPORTANT: Point at the folder where PyQt6 stub files are located. This folder will be examined to
# fill the 'enum_dict'.
# pyqt6_folderpath = 'C:/Python39/Lib/site-packages/PyQt6'
# EDIT: @Myridium suggested another way to fill this 'pyqt6_folderpath'
# variable:
import PyQt6
pyqt6_folderpath = PyQt6.__path__[0]
# Figure out where the toplevel directory is located. We assume that this converter tool is located
# in that directory. An os.walk() operation starts from this toplevel directory to find and process
# all files.
toplevel_directory = os.path.realpath(
os.path.dirname(
os.path.realpath(
inspect.getfile(
inspect.currentframe()
)
)
)
).replace('\\', '/')
# Figure out the name of this script. It will be used later on to exclude oneself from the replace-
# ments.
script_name = os.path.realpath(
inspect.getfile(inspect.currentframe())
).replace('\\', '/').split('/')[-1]
# Create the dictionary that will be filled with enums
enum_dict:Dict[str, str] = {}
def fill_enum_dict(filepath:str) -> None:
'''
Parse the given stub file to extract the enums and flags. Each one is inside a class, possibly a
nested one. For example:
---------------------------------------------------------------------
| class Qt(PyQt6.sip.simplewrapper): |
| class HighDpiScaleFactorRoundingPolicy(enum.Enum): |
| Round = ... # type: Qt.HighDpiScaleFactorRoundingPolicy |
---------------------------------------------------------------------
The enum 'Round' is from class 'HighDpiScaleFactorRoundingPolicy' which is in turn from class
'Qt'. The old reference style would then be:
> Qt.Round
The new style (fully qualified name) would be:
> Qt.HighDpiScaleFactorRoundingPolicy.Round
The aim of this function is to fill the 'enum_dict' with an entry like:
enum_dict = {
'Qt.Round' : 'Qt.HighDpiScaleFactorRoundingPolicy.Round'
}
'''
content:str = ''
with open(filepath, 'r', encoding='utf-8', newline='\n', errors='replace') as f:
content = f.read()
p = re.compile(r'(\w+)\s+=\s+\.\.\.\s+#\s*type:\s*([\w.]+)')
for m in p.finditer(content):
# Observe the enum's name, eg. 'Round'
enum_name = m.group(1)
# Figure out in which classes it is
class_list = m.group(2).split('.')
# If it belongs to just one class (no nesting), there is no point in continuing
if len(class_list) == 1:
continue
# Extract the old and new enum's name
old_enum = f'{class_list[0]}.{enum_name}'
new_enum = ''
for class_name in class_list:
new_enum += f'{class_name}.'
continue
new_enum += enum_name
# Add them to the 'enum_dict'
enum_dict[old_enum] = new_enum
continue
return
def show_help() -> None:
'''
Print help info and quit.
'''
print(help_text)
return
def convert_enums_in_file(filepath:str, dry_run:bool) -> None:
'''
Convert the enums in the given file.
'''
filename:str = filepath.split('/')[-1]
# Ignore the file in some cases
if any(filename == fname for fname in (script_name, )):
return
# Read the content
content:str = ''
with open(filepath, 'r', encoding='utf-8', newline='\n', errors='replace') as f:
content = f.read()
# Loop over all the keys in the 'enum_dict'. Perform a replacement in the 'content' for each of
# them.
for k, v in enum_dict.items():
if k not in content:
continue
# Compile a regex pattern that only looks for the old enum (represented by the key of the
# 'enum_dict') if it is surrounded by bounds. What we want to avoid is a situation like
# this:
# k = 'Qt.Window'
# k found in 'qt.Qt.WindowType.Window'
# In the situation above, k is found in 'qt.Qt.WindowType.Window' such that a replacement
# will take place there, messing up the code! By surrounding k with bounds in the regex pat-
# tern, this won't happen.
p = re.compile(fr'\b{k}\b')
# Substitute all occurences of k (key) in 'content' with v (value). The 'subn()' method re-
# turns a tuple (new_string, number_of_subs_made).
new_content, n = p.subn(v, content)
if n == 0:
assert new_content == content
continue
assert new_content != content
print(f'{q}{filename}{q}: Replace {q}{k}{q} => {q}{v}{q} ({n})')
content = new_content
continue
if dry_run:
return
with open(filepath, 'w', encoding='utf-8', newline='\n', errors='replace') as f:
f.write(content)
return
def convert_all(dry_run:bool) -> None:
'''
Search and replace all enums.
'''
for root, dirs, files in os.walk(toplevel_directory):
for f in files:
if not f.endswith('.py'):
continue
filepath = os.path.join(root, f).replace('\\', '/')
convert_enums_in_file(filepath, dry_run)
continue
continue
return
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description = 'Convert enums to fully-qualified names',
add_help = False,
)
parser.add_argument('-h', '--help' , action='store_true')
parser.add_argument('-d', '--dry_run' , action='store_true')
parser.add_argument('-s', '--show' , action='store_true')
args = parser.parse_args()
if args.help:
show_help()
else:
#& Check if 'pyqt6_folderpath' exists
if not os.path.exists(pyqt6_folderpath):
print(
f'\nERROR:\n'
f'Folder {q}{pyqt6_folderpath}{q} could not be found. Make sure that variable '
f'{q}pyqt6_folderpath{q} from line 57 points to the PyQt6 installation folder.\n'
)
else:
#& Fill the 'enum_dict'
type_hint_files = [
os.path.join(pyqt6_folderpath, _filename)
for _filename in os.listdir(pyqt6_folderpath)
if _filename.endswith('.pyi')
]
for _filepath in type_hint_files:
fill_enum_dict(_filepath)
continue
#& Perform requested action
if args.show:
import pprint
pprint.pprint(enum_dict)
elif args.dry_run:
print('\nDRY RUN\n')
convert_all(dry_run=True)
else:
convert_all(dry_run=False)
print('\nQuit enum converter tool\n')
# MIT LICENSE
# ===========
# Copyright (c) 2022 Kristof Mulier
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
# associated documentation files (the "Software"), to deal in the Software without restriction, in-
# cluding without limitation the rights to use, copy, modify, merge, publish, distribute, sublicen-
# se, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to
# do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all copies or substan-
# tial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
# NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRIN-
# GEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
确保备份您的 Python 项目。然后将该文件放在项目的顶层目录中。修改第 57 行(!),使其指向您的 PyQt6 安装文件夹。
首先使用 --dry_run
标志运行脚本,以确保您同意替换。然后在没有任何标志的情况下运行它。
关于qt - 迁移到 Qt6/PyQt6 : what are all the deprecated short-form names in Qt5?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72086632/
我的设置.py: LANGUAGE_CODE = 'de' TIME_ZONE = require_env("TIME_ZONE_IDENTIFIER") USE_I18N = True USE_L1
谁能给我解释一下 Django 的 forms.Form 和 forms.ModelForm 的相同点和不同点? 最佳答案 从 forms.Form 创建的表单由您手动配置。您最好将这些用于不直接与模
我在用 angularjs 构建的表单上遇到此错误。 `错误:[$compile:multidir] 多个指令 [form, form] 请求 'form' Controller :
我是 Spring 的新手,在尝试显示表单错误时遇到了一些麻烦。 我有以下表格: User Name:
我希望在提交表单时找出 spring:bind 和 form:form 标记库之间的区别。 我的 JSP 片段如下: ....
类型‘AbstractControl’上不存在属性‘Controls’。
有一个问题与此非常相似,但我想以不同的方式提出。 我是一个非常自定的人,但有时我确实喜欢走捷径。就这样吧。 我确实发现这两个类非常相似,尽管其中一个“帮助”程序员更快地编写代码或减少代码/重复代码。将
我在控制台中收到此错误。 “表单提交已取消,因为表单未连接” 自从我们将应用程序迁移到更新版本的 React 后,尝试将我的 redux-form 从 v5 迁移到 v6 之后。 我不确定这里出了什么
我想要的是一个表单,在提交时运行验证检查,并突出显示所有无效字段并添加工具提示。 我正在有效地寻找这样的东西: dojo.forEach(dijit.byId('myForm')._invalidWi
我需要设置symfony2表单元素的值。 我在 Controller 操作中使用了doctrine2实体, Symfony\Component\Form\AbstractType 和createFor
这是用于将数据提交到自定义列表的自定义 Editform.aspx。用户完成表单后,他应该能够点击按钮甚至“确定”按钮,并让 sharepoint 将表单数据提交到列表,然后重定向到项目显示表单 (d
我想知道在 spring 标签中编写所有表单是否是一种好习惯,或者我可以将 spring 表单标签与 html 表单标签混合使用吗? 最佳答案 当您需要 Spring 表单提供的功能时使用它们: 绑定
我正在构建动态表单并希望“即时”添加表单组。 这是我的代码,几乎可以工作: import {Component, OnInit} from '@angular/core'; import {FormG
表格 Form.Load 有什么区别? , Form.Shown和 Form.Activated事件?他们被解雇的顺序是什么? 最佳答案 参见 Windows Forms Events Lifecyc
我正在使用具有路线跟踪功能的 Xamarin Forms 开发一些应用程序。尽管我正在使用 AppCenter,即在 App.xaml.cs OnStart 我添加 protected asy
我正在实现一个 gameboy 模拟器,就像我之前的许多人一样。 我正在尝试实现 PPU 并为此使用代表屏幕的类。 // needed because VS can't find it as depe
我是 Orbeon Form 新手,想使用它。不过,我尝试过 Orbeon Form 网站上的 Form 示例,并用泰语输入了一些数据。是的,可以在“泰语”字段中输入数据。但是当我尝试生成“PDF”时
那么让表单一遍又一遍有效地呈现相同表单的最佳方法是什么,并根据实体的属性值有条件地禁用字段? 我有一个发票实体,需要一个用于创建发票的表单,以及在发票流程的各个阶段(生成、发送、支付等)禁用各个字段的
因此,我一直在与我的同事(开发人员和设计人员)就 Web 表单的自动填充工具进行亲切的辩论。这是一个重要的开发问题,因为它会影响表单的构建方式。 问)自动填充工具(例如 Google 工具栏或 Chr
那么让表单一遍又一遍有效地呈现相同表单的最佳方法是什么,并根据实体的属性值有条件地禁用字段? 我有一个发票实体,需要一个用于创建发票的表单,以及在发票流程的各个阶段(生成、发送、支付等)禁用各个字段的
我是一名优秀的程序员,十分优秀!