diff --git a/.gitignore b/.gitignore index 9f409db..812f985 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,763 @@ +# Created by https://www.toptal.com/developers/gitignore/api/python,qt,qtcreator,visualstudiocode,visualstudio,macos,linux,windows,cmake +# Edit at https://www.toptal.com/developers/gitignore?templates=python,qt,qtcreator,visualstudiocode,visualstudio,macos,linux,windows,cmake + +### CMake ### +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +### CMake Patch ### +# External projects +*-prefix/ + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +### Qt ### +# C++ objects and libs +*.slo +*.lo +*.o +*.a +*.la +*.lai +*.so.* +*.dll +*.dylib + +# Qt-es +object_script.*.Release +object_script.*.Debug +*_plugin_import.cpp +/.qmake.cache +/.qmake.stash +*.pro.user +*.pro.user.* +*.qbs.user +*.qbs.user.* +*.moc +moc_*.cpp +moc_*.h +qrc_*.cpp +ui_*.h +*.qmlc +*.jsc +Makefile* +*build-* +*.qm +*.prl + +# Qt unit tests +target_wrapper.* + +# QtCreator +*.autosave + +# QtCreator Qml +*.qmlproject.user +*.qmlproject.user.* + +# QtCreator CMake +CMakeLists.txt.user* + +# QtCreator 4.8< compilation database + +# QtCreator local machine specific files for imported projects +*creator.user* + +*_qmlcache.qrc + +### QtCreator ### +# gitignore for Qt Creator like IDE for pure C/C++ project without Qt +# +# Reference: http://doc.qt.io/qtcreator/creator-project-generic.html + + + +# Qt Creator autogenerated files + + +# A listing of all the files included in the project +*.files + +# Include directories +*.includes + +# Project configuration settings like predefined Macros +*.config + +# Qt Creator settings +*.creator + +# User project settings +*.creator.user* + +# Qt Creator backups + +# Flags for Clang Code Model +*.cxxflags +*.cflags + + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### VisualStudio ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +*.code-workspace + +# Local History for Visual Studio Code + +# Windows Installer files from build outputs + +# JetBrains Rider +*.sln.iml + +### VisualStudio Patch ### +# Additional files built by Visual Studio + +# End of https://www.toptal.com/developers/gitignore/api/python,qt,qtcreator,visualstudiocode,visualstudio,macos,linux,windows,cmake + # Byte-compiled / optimized / DLL files __pycache__/ *.py[co] diff --git a/Demo/WeltHideWindow.py b/Demo/WeltHideWindow.py old mode 100644 new mode 100755 index 5faa20b..91a49f9 --- a/Demo/WeltHideWindow.py +++ b/Demo/WeltHideWindow.py @@ -10,26 +10,77 @@ @description: 简单的窗口贴边隐藏 """ +import os +import platform +from subprocess import getoutput + try: from PyQt5.QtCore import Qt - from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QApplication + from PyQt5.QtWidgets import ( + QApplication, + QGridLayout, + QMessageBox, + QPushButton, + QSizePolicy, + QSpacerItem, + QWidget, + ) except ImportError: from PySide2.QtCore import Qt - from PySide2.QtWidgets import QWidget, QVBoxLayout, QPushButton, QApplication + from PySide2.QtWidgets import ( + QApplication, + QGridLayout, + QMessageBox, + QPushButton, + QSizePolicy, + QSpacerItem, + QWidget, + ) -class WeltHideWindow(QWidget): +def IsSupport(): + """判断是否支持""" + if platform.system() == "Linux": + name = os.environ.get("XDG_SESSION_DESKTOP", "") + os.environ.get( + "XDG_CURRENT_DESKTOP", "" + ) + if name.lower().find("gnome") != -1: + print("gnome desktop") + return False + + wid = getoutput("xprop -root _NET_SUPPORTING_WM_CHECK").split(" # ")[-1] + print("wid:", wid) + if wid: + name = getoutput("xprop -id %s _NET_WM_NAME" % wid) + print("name:", name) + if name.lower().find("gnome") != -1: + print("gnome desktop") + return False + return True + + +class WeltHideWindow(QWidget): def __init__(self, *args, **kwargs): super(WeltHideWindow, self).__init__(*args, **kwargs) self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint) - self.resize(800, 600) + self.resize(400, 300) self._width = QApplication.desktop().availableGeometry(self).width() - layout = QVBoxLayout(self) - layout.addWidget(QPushButton("关闭窗口", self, clicked=self.close)) + layout = QGridLayout(self) + layout.addItem( + QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum), 0, 0 + ) + self.closeBtn = QPushButton("X", self) + layout.addWidget(self.closeBtn, 0, 1) + layout.addItem( + QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding), 1, 0 + ) + self.closeBtn.clicked.connect(self.close) + self.closeBtn.setMinimumSize(24, 24) + self.closeBtn.setMaximumSize(24, 24) def mousePressEvent(self, event): - '''鼠标按下事件,需要记录下坐标self._pos 和 是否可移动self._canMove''' + """鼠标按下事件,需要记录下坐标self._pos 和 是否可移动self._canMove""" super(WeltHideWindow, self).mousePressEvent(event) if event.button() == Qt.LeftButton: self._pos = event.globalPos() - self.pos() @@ -37,13 +88,13 @@ def mousePressEvent(self, event): self._canMove = not self.isMaximized() or not self.isFullScreen() def mouseMoveEvent(self, event): - '''鼠标移动事件,动态调整窗口位置''' + """鼠标移动事件,动态调整窗口位置""" super(WeltHideWindow, self).mouseMoveEvent(event) if event.buttons() == Qt.LeftButton and self._canMove: self.move(event.globalPos() - self._pos) def mouseReleaseEvent(self, event): - '''鼠标弹起事件,这个时候需要判断窗口的左边是否符合贴到左边,顶部,右边一半''' + """鼠标弹起事件,这个时候需要判断窗口的左边是否符合贴到左边,顶部,右边一半""" super(WeltHideWindow, self).mouseReleaseEvent(event) self._canMove = False pos = self.pos() @@ -60,7 +111,7 @@ def mouseReleaseEvent(self, event): return self.move(self._width - 1, y) def enterEvent(self, event): - '''鼠标进入窗口事件,用于弹出显示窗口''' + """鼠标进入窗口事件,用于弹出显示窗口""" super(WeltHideWindow, self).enterEvent(event) pos = self.pos() x = pos.x() @@ -73,7 +124,7 @@ def enterEvent(self, event): return self.move(self._width - self.width(), y) def leaveEvent(self, event): - '''鼠标离开事件,如果原先窗口已经隐藏,并暂时显示,此时离开后需要再次隐藏''' + """鼠标离开事件,如果原先窗口已经隐藏,并暂时显示,此时离开后需要再次隐藏""" super(WeltHideWindow, self).leaveEvent(event) pos = self.pos() x = pos.x() @@ -86,10 +137,18 @@ def leaveEvent(self, event): return self.move(self._width - 1, y) -if __name__ == '__main__': +if __name__ == "__main__": import sys app = QApplication(sys.argv) - w = WeltHideWindow() - w.show() - sys.exit(app.exec_()) + if not IsSupport(): + QMessageBox.warning( + None, + "Warning", + "当前桌面不支持此功能", + ) + app.quit() + else: + w = WeltHideWindow() + w.show() + sys.exit(app.exec_()) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8000a6f --- /dev/null +++ b/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/QColumnView/FileManager.py b/QColumnView/FileManager.py new file mode 100644 index 0000000..810e489 --- /dev/null +++ b/QColumnView/FileManager.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Created on 2025/04/08 +@author: Irony +@site: https://pyqt.site | https://github.com/PyQt5 +@email: 892768447@qq.com +@file: FileManager.py +@description: +""" + +try: + from PyQt5.QtWidgets import ( + QApplication, + QColumnView, + QFileSystemModel, + QGridLayout, + QMessageBox, + QPushButton, + QSizePolicy, + QSpacerItem, + QWidget, + ) +except Exception: + from PySide2.QtWidgets import ( + QApplication, + QColumnView, + QFileSystemModel, + QGridLayout, + QMessageBox, + QPushButton, + QSizePolicy, + QSpacerItem, + QWidget, + ) + + +class FileManager(QWidget): # type: ignore + def __init__(self, *args, **kwargs): + super(FileManager, self).__init__(*args, **kwargs) + self.resize(800, 600) + self._view = QColumnView(self) + self._btn = QPushButton("确定", self) + layout = QGridLayout(self) + layout.addWidget(self._view, 0, 0, 1, 2) + layout.addItem( + QSpacerItem( + 40, + 20, + QSizePolicy.Expanding, + QSizePolicy.Minimum, + ), + 1, + 0, + ) + layout.addWidget(self._btn, 1, 1, 1, 1) + layout.setRowStretch(1, 0) + self._model = QFileSystemModel(self) + self._model.setRootPath("") # 设置根路径 + self._view.setModel(self._model) # type: ignore + self._btn.clicked.connect(self.onAccept) # type: ignore + + def onAccept(self): + path = self._model.filePath(self._view.currentIndex()) + print("path", path) + if path: + QMessageBox.information(self, "提示", "路径:%s" % path) + + +if __name__ == "__main__": + import cgitb + import sys + + cgitb.enable(format="text") + app = QApplication(sys.argv) + w = FileManager() + w.show() + sys.exit(app.exec_()) diff --git a/QColumnView/README.md b/QColumnView/README.md index e69de29..bea28a7 100644 --- a/QColumnView/README.md +++ b/QColumnView/README.md @@ -0,0 +1,16 @@ +# QColumnView + +- 目录 + - [文件系统浏览器](#1文件系统浏览器) + +## 1、文件系统浏览器 + +[运行 FileManager.py](FileManager.py) + +一个省市区关联的三级联动,数据源在data.json中 + +1. 通过`QFileSystemModel`模型来显示文件系统 +2. 结合`QColumnView`的`setMode`函数显示文件系统模型 +3. 通过`QFileSystemModel.filePath(QColumnView.currentIndex())`获取当前选中的路径 + +![FileManager](ScreenShot/FileManager.png) diff --git a/QColumnView/ScreenShot/FileManager.png b/QColumnView/ScreenShot/FileManager.png new file mode 100644 index 0000000..c69afdc Binary files /dev/null and b/QColumnView/ScreenShot/FileManager.png differ diff --git a/QPushButton/Data/Images/avatar.jpg b/QPushButton/Data/Images/avatar.jpg new file mode 100644 index 0000000..32856da Binary files /dev/null and b/QPushButton/Data/Images/avatar.jpg differ diff --git a/QPushButton/README.md b/QPushButton/README.md index 2328a51..1d9556c 100644 --- a/QPushButton/README.md +++ b/QPushButton/README.md @@ -5,6 +5,8 @@ - [按钮底部线条进度](#2按钮底部线条进度) - [按钮文字旋转进度](#3按钮文字旋转进度) - [按钮常用信号](#4按钮常用信号) + - [旋转动画按钮](#5旋转动画按钮) + - [弹性动画按钮](#6弹性动画按钮) ## 1、普通样式 [运行 NormalStyle.py](NormalStyle.py) @@ -33,4 +35,14 @@ 根据官网文档 https://doc.qt.io/qt-5/qabstractbutton.html#signals 中的信号介绍编写 按钮的点击、按下、释放、选中信号演示 -![SignalsExample](ScreenShot/SignalsExample.gif) \ No newline at end of file +![SignalsExample](ScreenShot/SignalsExample.gif) + +## 5、旋转动画按钮 +[运行 RotateButton.py](RotateButton.py) + +![RotateButton](ScreenShot/RotateButton.gif) + +## 6、弹性动画按钮 +[运行 RubberBandButton.py](RubberBandButton.py) + +![RubberBandButton](ScreenShot/RubberBandButton.gif) \ No newline at end of file diff --git a/QPushButton/RotateButton.py b/QPushButton/RotateButton.py new file mode 100644 index 0000000..e9b9f08 --- /dev/null +++ b/QPushButton/RotateButton.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Created on 2019年1月2日 +@author: Irony +@site: https://pyqt.site https://github.com/PyQt5 +@email: 892768447@qq.com +@file: Widgets.RotateButton +@description: +""" + +import os +import sys + +try: + from PyQt5.QtCore import QPointF, QPropertyAnimation, QRectF, Qt, pyqtProperty + from PyQt5.QtGui import QColor, QImage, QPainter, QPainterPath, QPixmap + from PyQt5.QtWidgets import ( + QGraphicsDropShadowEffect, + QPushButton, + QStyle, + QStyleOptionButton, + QStylePainter, + QApplication, + QWidget, + QHBoxLayout, + ) +except ImportError: + from PySide2.QtCore import QPointF, QPropertyAnimation, QRectF, Qt, pyqtProperty + from PySide2.QtGui import QColor, QImage, QPainter, QPainterPath, QPixmap + from PySide2.QtWidgets import ( + QGraphicsDropShadowEffect, + QPushButton, + QStyle, + QStyleOptionButton, + QStylePainter, + QApplication, + QWidget, + QHBoxLayout, + ) + + +class RotateButton(QPushButton): + + STARTVALUE = 0 # 起始旋转角度 + ENDVALUE = 360 # 结束旋转角度 + DURATION = 540 # 动画完成总时间 + + def __init__(self, *args, **kwargs): + super(RotateButton, self).__init__(*args, **kwargs) + self.setCursor(Qt.PointingHandCursor) + self._angle = 0 # 角度 + self._padding = 10 # 阴影边距 + self._image = "" # 图片路径 + self._shadowColor = QColor(33, 33, 33) # 阴影颜色 + self._pixmap = None # 图片对象 + # 属性动画 + self._animation = QPropertyAnimation(self, b"angle", self) + self._animation.setLoopCount(1) # 只循环一次 + + def paintEvent(self, event): + """绘制事件""" + text = self.text() + option = QStyleOptionButton() + self.initStyleOption(option) + option.text = "" # 不绘制文字 + painter = QStylePainter(self) + painter.setRenderHint(QStylePainter.Antialiasing) + painter.setRenderHint(QStylePainter.HighQualityAntialiasing) + painter.setRenderHint(QStylePainter.SmoothPixmapTransform) + painter.drawControl(QStyle.CE_PushButton, option) + # 变换坐标为正中间 + painter.translate(self.rect().center()) + painter.rotate(self._angle) # 旋转 + + # 绘制图片 + if self._pixmap and not self._pixmap.isNull(): + w = self.width() + h = self.height() + pos = QPointF(-self._pixmap.width() / 2, -self._pixmap.height() / 2) + painter.drawPixmap(pos, self._pixmap) + elif text: + # 在变换坐标后的正中间画文字 + fm = self.fontMetrics() + w = fm.width(text) + h = fm.height() + rect = QRectF(0 - w * 2, 0 - h, w * 2 * 2, h * 2) + painter.drawText(rect, Qt.AlignCenter, text) + else: + super(RotateButton, self).paintEvent(event) + + def enterEvent(self, _): + """鼠标进入事件""" + # 设置阴影 + # 边框阴影效果 + effect = QGraphicsDropShadowEffect(self) + effect.setBlurRadius(self._padding * 2) + effect.setOffset(0, 0) + effect.setColor(self._shadowColor) + self.setGraphicsEffect(effect) + + # 开启旋转动画 + self._animation.stop() + cv = self._animation.currentValue() or self.STARTVALUE + self._animation.setDuration( + self.DURATION if cv == 0 else int(cv / self.ENDVALUE * self.DURATION) + ) + self._animation.setStartValue(cv) + self._animation.setEndValue(self.ENDVALUE) + self._animation.start() + + def leaveEvent(self, _): + """鼠标离开事件""" + # 取消阴影 + self.setGraphicsEffect(None) + + # 旋转动画 + self._animation.stop() + cv = self._animation.currentValue() or self.ENDVALUE + self._animation.setDuration(int(cv / self.ENDVALUE * self.DURATION)) + self._animation.setStartValue(cv) + self._animation.setEndValue(self.STARTVALUE) + self._animation.start() + + def setPixmap(self, path): + if not os.path.exists(path): + self._image = "" + self._pixmap = None + return + self._image = path + size = ( + max( + min(self.width(), self.height()), + min(self.minimumWidth(), self.minimumHeight()), + ) + - self._padding + ) # 需要边距的边框 + radius = int(size / 2) + image = QImage(size, size, QImage.Format_ARGB32_Premultiplied) + image.fill(Qt.transparent) # 填充背景为透明 + pixmap = QPixmap(path).scaled( + size, size, Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation + ) + # QPainter + painter = QPainter() + painter.begin(image) + painter.setRenderHint(QPainter.Antialiasing, True) + painter.setRenderHint(QPainter.SmoothPixmapTransform, True) + # QPainterPath + path = QPainterPath() + path.addRoundedRect(0, 0, size, size, radius, radius) + # 切割圆 + painter.setClipPath(path) + painter.drawPixmap(0, 0, pixmap) + painter.end() + self._pixmap = QPixmap.fromImage(image) + self.update() + + def pixmap(self): + return self._pixmap + + @pyqtProperty(str) + def image(self): + return self._image + + @image.setter + def image(self, path): + self.setPixmap(path) + + @pyqtProperty(int) + def angle(self): + return self._angle + + @angle.setter + def angle(self, value): + self._angle = value + self.update() + + @pyqtProperty(int) + def padding(self): + return self._padding + + @padding.setter + def padding(self, value): + self._padding = value + + @pyqtProperty(QColor) + def shadowColor(self): + return self._shadowColor + + @shadowColor.setter + def shadowColor(self, color): + self._shadowColor = QColor(color) + + +class TestWindow(QWidget): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + layout = QHBoxLayout(self) + + btn = RotateButton("pyqt.site", self) + btn.setMinimumHeight(96) + btn.setToolTip("旋转按钮") + layout.addWidget(btn) + + btn = RotateButton("", self) + btn.setMinimumHeight(96) + btn.setObjectName("imageLabel1") + btn.setPixmap("./Data/Images/avatar.jpg") + layout.addWidget(btn) + + btn = RotateButton("", self) + btn.setMinimumHeight(96) + btn.setObjectName("imageLabel2") + layout.addWidget(btn) + + +if __name__ == "__main__": + import cgitb + + cgitb.enable(1, None, 5, "text") + + # cd to current dir + os.chdir(os.path.dirname(os.path.abspath(sys.argv[0]))) + + app = QApplication(sys.argv) + app.setStyleSheet( + """ + RotateButton { + font-size: 48px; + } + #imageLabel1, #imageLabel2 { + background: transparent; + } + #imageLabel2 { + qproperty-image: "./Data/Images/avatar.jpg"; + } + """ + ) + w = TestWindow() + w.show() + sys.exit(app.exec_()) diff --git a/QPushButton/RubberBandButton.py b/QPushButton/RubberBandButton.py new file mode 100644 index 0000000..75af92e --- /dev/null +++ b/QPushButton/RubberBandButton.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Created on 2019年1月3日 +@author: Irony +@site: https://pyqt.site https://github.com/PyQt5 +@email: 892768447@qq.com +@file: Widgets.RubberBandButton +@description: +""" + +try: + from PyQt5.QtCore import ( + QEasingCurve, + QParallelAnimationGroup, + QPropertyAnimation, + QRectF, + Qt, + pyqtProperty, + ) + from PyQt5.QtGui import QColor, QPainter + from PyQt5.QtWidgets import ( + QPushButton, + QStyle, + QStyleOptionButton, + QStylePainter, + QWidget, + QApplication, + QHBoxLayout, + ) +except ImportError: + from PySide2.QtCore import ( + QEasingCurve, + QParallelAnimationGroup, + QPropertyAnimation, + QRectF, + Qt, + Property as pyqtProperty, + ) + from PySide2.QtGui import QColor, QPainter + from PySide2.QtWidgets import ( + QPushButton, + QStyle, + QStyleOptionButton, + QApplication, + QStylePainter, + QWidget, + QHBoxLayout, + ) + + +class RubberBandButton(QPushButton): + + def __init__(self, *args, **kwargs): + self._bgcolor = QColor(kwargs.pop("bgcolor", Qt.green)) + super(RubberBandButton, self).__init__(*args, **kwargs) + self.setFlat(True) + self.setCursor(Qt.PointingHandCursor) + self._width = 0 + self._height = 0 + + def paintEvent(self, event): + self._initAnimate() + painter = QStylePainter(self) + painter.setRenderHint(QPainter.Antialiasing, True) + painter.setRenderHint(QPainter.HighQualityAntialiasing, True) + painter.setRenderHint(QPainter.SmoothPixmapTransform, True) + painter.setBrush(QColor(self._bgcolor)) + painter.setPen(QColor(self._bgcolor)) + painter.drawEllipse( + QRectF( + (self.minimumWidth() - self._width) / 2, + (self.minimumHeight() - self._height) / 2, + self._width, + self._height, + ) + ) + # 绘制本身的文字和图标 + options = QStyleOptionButton() + options.initFrom(self) + size = options.rect.size() + size.transpose() + options.rect.setSize(size) + options.features = QStyleOptionButton.Flat + options.text = self.text() + options.icon = self.icon() + options.iconSize = self.iconSize() + painter.drawControl(QStyle.CE_PushButton, options) + event.accept() + + def _initAnimate(self): + if hasattr(self, "_animate"): + return + self._width = self.minimumWidth() * 7 / 8 + self._height = self.minimumHeight() * 7 / 8 + # self._width=175 + # self._height=175 + + # 宽度动画 + wanimate = QPropertyAnimation(self, b"rWidth") + wanimate.setEasingCurve(QEasingCurve.OutElastic) + wanimate.setDuration(700) + wanimate.valueChanged.connect(self.update) + + # 插入宽度线性值 + wanimate.setKeyValueAt(0, self._width) + # wanimate.setKeyValueAt(0.1, 180) + # wanimate.setKeyValueAt(0.2, 185) + # wanimate.setKeyValueAt(0.3, 190) + # wanimate.setKeyValueAt(0.4, 195) + wanimate.setKeyValueAt(0.5, self._width + 6) + # wanimate.setKeyValueAt(0.6, 195) + # wanimate.setKeyValueAt(0.7, 190) + # wanimate.setKeyValueAt(0.8, 185) + # wanimate.setKeyValueAt(0.9, 180) + wanimate.setKeyValueAt(1, self._width) + + # 高度动画 + hanimate = QPropertyAnimation(self, b"rHeight") + hanimate.setEasingCurve(QEasingCurve.OutElastic) + hanimate.setDuration(700) + + # 插入高度线性值 + hanimate.setKeyValueAt(0, self._height) + # hanimate.setKeyValueAt(0.1, 170) + # hanimate.setKeyValueAt(0.3, 165) + hanimate.setKeyValueAt(0.5, self._height - 6) + # hanimate.setKeyValueAt(0.7, 165) + # hanimate.setKeyValueAt(0.9, 170) + hanimate.setKeyValueAt(1, self._height) + + # 设置动画组 + self._animate = QParallelAnimationGroup(self) + self._animate.addAnimation(wanimate) + self._animate.addAnimation(hanimate) + + def enterEvent(self, event): + super(RubberBandButton, self).enterEvent(event) + self._animate.stop() + self._animate.start() + + @pyqtProperty(int) + def rWidth(self): + return self._width + + @rWidth.setter + def rWidth(self, value): + self._width = value + + @pyqtProperty(int) + def rHeight(self): + return self._height + + @rHeight.setter + def rHeight(self, value): + self._height = value + + @pyqtProperty(QColor) + def bgColor(self): + return self._bgcolor + + @bgColor.setter + def bgColor(self, color): + self._bgcolor = QColor(color) + + +class TestWindow(QWidget): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + layout = QHBoxLayout(self) + layout.addWidget(RubberBandButton("d", self, bgcolor="#01847E")) + layout.addWidget(RubberBandButton("v", self, bgcolor="#7FA91F")) + layout.addWidget(RubberBandButton("N", self, bgcolor="#FC8416")) + layout.addWidget(RubberBandButton("U", self, bgcolor="#66D3c0")) + layout.addWidget(RubberBandButton("a", self, bgcolor="#F28195")) + + +if __name__ == "__main__": + import cgitb, sys + + cgitb.enable(1, None, 5, "text") + + app = QApplication(sys.argv) + app.setStyleSheet( + """ + RubberBandButton { + min-width: 100px; + max-width: 100px; + min-height: 100px; + max-height: 100px; + border: none; + color: white; + outline: none; + margin: 4px; + font-family: webdings; + font-size: 60px; + } + """ + ) + w = TestWindow() + w.show() + sys.exit(app.exec_()) diff --git a/QPushButton/ScreenShot/RotateButton.gif b/QPushButton/ScreenShot/RotateButton.gif new file mode 100644 index 0000000..6b1c3aa Binary files /dev/null and b/QPushButton/ScreenShot/RotateButton.gif differ diff --git a/QPushButton/ScreenShot/RubberBandButton.gif b/QPushButton/ScreenShot/RubberBandButton.gif new file mode 100644 index 0000000..dcf1e81 Binary files /dev/null and b/QPushButton/ScreenShot/RubberBandButton.gif differ diff --git a/README.md b/README.md index 2c6817b..1057411 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,8 @@ - [按钮底部线条进度](QPushButton/BottomLineProgress.py) - [按钮文字旋转进度](QPushButton/FontRotate.py) - [按钮常用信号](QPushButton/SignalsExample.py) + - [旋转动画按钮](QPushButton/RotateButton.py) + - [弹性动画按钮](QPushButton/RubberBandButton.py) - [QToolButton](QToolButton) - [QRadioButton](QRadioButton) - [QCheckBox](QCheckBox) @@ -66,6 +68,7 @@ - [QTableView](QTableView) - [表格内容复制](QTableView/CopyContent.py) - [QColumnView](QColumnView) + - [文件系统浏览器](QColumnView/FileManager.py) - [QUndoView](QUndoView) - Item Widgets diff --git a/Test/WigglyWidget/CMakeLists.txt b/Test/WigglyWidget/CMakeLists.txt new file mode 100644 index 0000000..41beb60 --- /dev/null +++ b/Test/WigglyWidget/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.14) + +project(WigglyWidget LANGUAGES CXX) + +add_subdirectory(LibWigglyWidget) +add_subdirectory(TestWigglyWidget) +add_subdirectory(PyQtWrapper) +add_subdirectory(PySideWrapper) diff --git a/Test/WigglyWidget/LibWigglyWidget/CMakeLists.txt b/Test/WigglyWidget/LibWigglyWidget/CMakeLists.txt new file mode 100644 index 0000000..d647827 --- /dev/null +++ b/Test/WigglyWidget/LibWigglyWidget/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.14) + +project(LibWigglyWidget LANGUAGES CXX) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 配置安装目录 +set(CMAKE_INSTALL_DIR "${CMAKE_SOURCE_DIR}/dist") + +# 静态库 +set(BUILD_SHARED_LIBS OFF) + +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets) + +add_library(LibWigglyWidget wigglywidget.cpp wigglywidget.h) + +target_link_libraries(LibWigglyWidget PRIVATE Qt${QT_VERSION_MAJOR}::Widgets) + +if(BUILD_SHARED_LIBS) + target_compile_definitions(LibWigglyWidget PRIVATE LIBWIGGLYWIDGET_LIBRARY) +endif() + +# 配置生成的文件安装目录 +install( + TARGETS ${PROJECT_NAME} + RUNTIME DESTINATION "${CMAKE_INSTALL_DIR}/bin" + LIBRARY DESTINATION "${CMAKE_INSTALL_DIR}/lib" + ARCHIVE DESTINATION "${CMAKE_INSTALL_DIR}/lib") + +install(FILES wigglywidget.h DESTINATION "${CMAKE_INSTALL_DIR}/include") diff --git a/Test/WigglyWidget/LibWigglyWidget/wigglywidget.cpp b/Test/WigglyWidget/LibWigglyWidget/wigglywidget.cpp new file mode 100644 index 0000000..0433fc1 --- /dev/null +++ b/Test/WigglyWidget/LibWigglyWidget/wigglywidget.cpp @@ -0,0 +1,50 @@ +#include "wigglywidget.h" + +#include +#include +#include + +WigglyWidget::WigglyWidget(QWidget *parent) : QWidget(parent), step(0) { + setBackgroundRole(QPalette::Midlight); + setAutoFillBackground(true); + + QFont newFont = font(); + newFont.setPointSize(newFont.pointSize() + 20); + setFont(newFont); + + timer.start(60, this); +} + +void WigglyWidget::setText(const QString &newText) { text = newText; } + +void WigglyWidget::paintEvent(QPaintEvent * /* event */) + +{ + static constexpr int sineTable[16] = {0, 38, 71, 92, 100, 92, 71, 38, + 0, -38, -71, -92, -100, -92, -71, -38}; + + QFontMetrics metrics(font()); + int x = (width() - metrics.horizontalAdvance(text)) / 2; + int y = (height() + metrics.ascent() - metrics.descent()) / 2; + QColor color; + + QPainter painter(this); + + for (int i = 0; i < text.size(); ++i) { + int index = (step + i) % 16; + color.setHsv((15 - index) * 16, 255, 191); + painter.setPen(color); + painter.drawText(x, y - ((sineTable[index] * metrics.height()) / 400), + QString(text[i])); + x += metrics.horizontalAdvance(text[i]); + } +} + +void WigglyWidget::timerEvent(QTimerEvent *event) { + if (event->timerId() == timer.timerId()) { + ++step; + update(); + } else { + QWidget::timerEvent(event); + } +} diff --git a/Test/WigglyWidget/LibWigglyWidget/wigglywidget.h b/Test/WigglyWidget/LibWigglyWidget/wigglywidget.h new file mode 100644 index 0000000..3df215e --- /dev/null +++ b/Test/WigglyWidget/LibWigglyWidget/wigglywidget.h @@ -0,0 +1,36 @@ +#ifndef WIGGLYWIDGET_H +#define WIGGLYWIDGET_H + +#include +#include + +#ifdef Q_OS_WIN +#include + +#if defined(WIGGLYWIDGET_LIBRARY) +#define WIGGLYWIDGET_EXPORT Q_DECL_EXPORT +#else +#define WIGGLYWIDGET_EXPORT +#endif +#endif + +class WIGGLYWIDGET_EXPORT WigglyWidget : public QWidget { + Q_OBJECT + +public: + WigglyWidget(QWidget *parent = nullptr); + +public slots: + void setText(const QString &newText); + +protected: + virtual void paintEvent(QPaintEvent *event) override; + virtual void timerEvent(QTimerEvent *event) override; + +private: + QBasicTimer timer; + QString text; + int step; +}; + +#endif // WIGGLYWIDGET_H diff --git a/Test/WigglyWidget/PyQtWrapper/CMakeLists.txt b/Test/WigglyWidget/PyQtWrapper/CMakeLists.txt new file mode 100644 index 0000000..8218c32 --- /dev/null +++ b/Test/WigglyWidget/PyQtWrapper/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.14) + +project(PyQtWrapper) diff --git a/Test/WigglyWidget/PyQtWrapper/TestWigglyWidget.py b/Test/WigglyWidget/PyQtWrapper/TestWigglyWidget.py new file mode 100644 index 0000000..ac1177a --- /dev/null +++ b/Test/WigglyWidget/PyQtWrapper/TestWigglyWidget.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Created on 2024/04/26 +@author: Irony +@site: https://pyqt.site | https://github.com/PyQt5 +@email: 892768447@qq.com +@file: TestWigglyWidget.py +@description: +""" + +import os +import sys + +sys.path.append( + os.path.join(os.path.dirname(os.path.abspath(__file__)), "build/WigglyWidget") +) + +from PyQt5.QtWidgets import QApplication, QLineEdit, QVBoxLayout, QWidget +from WigglyWidget import WigglyWidget + + +class TestWigglyWidget(QWidget): + def __init__(self, *args, **kwargs): + super(TestWigglyWidget, self).__init__(*args, **kwargs) + self._layout = QVBoxLayout(self) + self._lineEdit = QLineEdit(self) + self._wigglyWidget = WigglyWidget(self) + self._layout.addWidget(self._lineEdit) + self._layout.addWidget(self._wigglyWidget) + + self._lineEdit.textChanged.connect(self._wigglyWidget.setText) + self._lineEdit.setText("pyqt.site") + + +if __name__ == "__main__": + import cgitb + import sys + + cgitb.enable(format="text") + app = QApplication(sys.argv) + w = TestWigglyWidget() + w.show() + w.resize(800, 600) + if not hasattr(app, "exec_"): + app.exec_ = app.exec + sys.exit(app.exec_()) diff --git a/Test/WigglyWidget/PyQtWrapper/pyproject.toml b/Test/WigglyWidget/PyQtWrapper/pyproject.toml new file mode 100644 index 0000000..fb83751 --- /dev/null +++ b/Test/WigglyWidget/PyQtWrapper/pyproject.toml @@ -0,0 +1,47 @@ +# Specify sip v6 as the build system for the package. +[build-system] +requires = ["sip >=5.3, <7", "PyQt-builder >=1.9, <2"] +build-backend = "sipbuild.api" + +# Specify the PEP 621 metadata for the project. +[project] +name = "WigglyWidget" +version = "0.1.0" +description = "Python bindings for the WigglyWidget library" +urls.homepage = "https://github.com/PyQt5/PyQt" +dependencies = ["PyQt5 (>=5.15.0, <6.0.0)"] + +[[project.authors]] +name = "Irony" +email = "892768447@qq.com" + +# Specify a PyQt-based project. +[tool.sip] +project-factory = "pyqtbuild:PyQtProject" + +# Specify the PEP 566 metadata for the project. +[tool.sip.metadata] +name = "WigglyWidget" +summary = "Python bindings for the WigglyWidget library" +home-page = "https://github.com/PyQt5/PyQt" +author = "Irony" +author-email = "892768447@qq.com" +requires-dist = "PyQt5 (>=5.15.0, <6.0.0)" + +# Configure the project. +[tool.sip.project] +tag-prefix = "WigglyWidget" +sip-include-dirs = [ + "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/PyQt5/bindings", + "/usr/local/lib64/python3.6/site-packages/PyQt5/bindings", + "C:/soft/Python311/Lib/site-packages/PyQt5/bindings", + "C:/soft/Python/lib/site-packages/PyQt6/bindings" +] + +# Configure the building of the fib bindings. +[tool.sip.bindings.WigglyWidget] +qmake-QT = ["core", "gui", "widgets"] +headers = ["wigglywidget.h"] +include-dirs = ["../dist/include"] +libraries = ["LibWigglyWidget"] +library-dirs = ["../dist/lib"] diff --git a/Test/WigglyWidget/PyQtWrapper/requirements.txt b/Test/WigglyWidget/PyQtWrapper/requirements.txt new file mode 100644 index 0000000..4d4e9f1 --- /dev/null +++ b/Test/WigglyWidget/PyQtWrapper/requirements.txt @@ -0,0 +1,6 @@ +PyQt5 +PyQt6 +sip +PyQt5-sip +PyQt6-sip +PyQt-builder diff --git a/Test/WigglyWidget/PyQtWrapper/sip/WigglyWidget/WigglyWidget.sip b/Test/WigglyWidget/PyQtWrapper/sip/WigglyWidget/WigglyWidget.sip new file mode 100644 index 0000000..6fe0006 --- /dev/null +++ b/Test/WigglyWidget/PyQtWrapper/sip/WigglyWidget/WigglyWidget.sip @@ -0,0 +1,16 @@ +class WigglyWidget : QWidget +{ +%TypeHeaderCode +#include "wigglywidget.h" +%End + +public: + WigglyWidget(QWidget *parent /TransferThis/ = 0); + +public slots: + void setText(const QString &newText); + +protected: + virtual void paintEvent(QPaintEvent *); + virtual void timerEvent(QTimerEvent *); +}; diff --git a/Test/WigglyWidget/PyQtWrapper/sip/WigglyWidget/WigglyWidgetmod.sip b/Test/WigglyWidget/PyQtWrapper/sip/WigglyWidget/WigglyWidgetmod.sip new file mode 100644 index 0000000..927d003 --- /dev/null +++ b/Test/WigglyWidget/PyQtWrapper/sip/WigglyWidget/WigglyWidgetmod.sip @@ -0,0 +1,9 @@ +%Module(name=WigglyWidget, keyword_arguments="Optional", use_limited_api=True) + + +%Import QtCore/QtCoremod.sip +%Import QtWidgets/QtWidgetsmod.sip + +%DefaultSupertype sip.simplewrapper + +%Include WigglyWidget.sip diff --git a/Test/WigglyWidget/PySideWrapper/CMakeLists.txt b/Test/WigglyWidget/PySideWrapper/CMakeLists.txt new file mode 100644 index 0000000..9912205 --- /dev/null +++ b/Test/WigglyWidget/PySideWrapper/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.14) +cmake_policy(VERSION 3.14) + +# Enable policy to not use RPATH settings for install_name on macOS. +if(POLICY CMP0068) + cmake_policy(SET CMP0068 NEW) +endif() + +# Enable policy to run automoc on generated files. +if(POLICY CMP0071) + cmake_policy(SET CMP0071 NEW) +endif() + +project(PySideWrapper) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(BINDINGS_HEADER_FILE "${CMAKE_CURRENT_SOURCE_DIR}/bindings.h") +set(BINDINGS_TYPESYSTEM_FILE "${CMAKE_CURRENT_SOURCE_DIR}/bindings.xml") +set(BINDINGS_OUTPUT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/dist") +set(BINDINGS_INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../dist/include/") + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/bindings.txt.in" + "${CMAKE_CURRENT_SOURCE_DIR}/bindings.txt") diff --git a/Test/WigglyWidget/PySideWrapper/TestWigglyWidget.py b/Test/WigglyWidget/PySideWrapper/TestWigglyWidget.py new file mode 100644 index 0000000..d2a851c --- /dev/null +++ b/Test/WigglyWidget/PySideWrapper/TestWigglyWidget.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Created on 2024/04/26 +@author: Irony +@site: https://pyqt.site | https://github.com/PyQt5 +@email: 892768447@qq.com +@file: TestWigglyWidget.py +@description: +""" + +import sys + +from PySide2.QtWidgets import QApplication, QLineEdit, QVBoxLayout, QWidget +from WigglyWidget import WigglyWidget + + +class TestWigglyWidget(QWidget): + def __init__(self, *args, **kwargs): + super(TestWigglyWidget, self).__init__(*args, **kwargs) + self._layout = QVBoxLayout(self) + self._lineEdit = QLineEdit(self) + self._wigglyWidget = WigglyWidget(self) + self._layout.addWidget(self._lineEdit) + self._layout.addWidget(self._wigglyWidget) + + self._lineEdit.textChanged.connect(self._wigglyWidget.setText) + self._lineEdit.setText("pyqt.site") + + +if __name__ == "__main__": + import cgitb + import sys + + cgitb.enable(format="text") + app = QApplication(sys.argv) + w = TestWigglyWidget() + w.show() + sys.exit(app.exec_()) diff --git a/Test/WigglyWidget/PySideWrapper/bindings.h b/Test/WigglyWidget/PySideWrapper/bindings.h new file mode 100644 index 0000000..0634a4b --- /dev/null +++ b/Test/WigglyWidget/PySideWrapper/bindings.h @@ -0,0 +1,4 @@ +#ifndef BINDINGS_H +#define BINDINGS_H +#include "wigglywidget.h" +#endif // BINDINGS_H diff --git a/Test/WigglyWidget/PySideWrapper/bindings.txt.in b/Test/WigglyWidget/PySideWrapper/bindings.txt.in new file mode 100644 index 0000000..d1bb8a4 --- /dev/null +++ b/Test/WigglyWidget/PySideWrapper/bindings.txt.in @@ -0,0 +1,13 @@ +[generator-project] +generator-set = shiboken +header-file = ${BINDINGS_HEADER_FILE} +typesystem-file = ${BINDINGS_TYPESYSTEM_FILE} +output-directory = ${BINDINGS_OUTPUT_DIR} +include-path = ${BINDINGS_INCLUDE_PATH} +framework-include-paths = +typesystem-paths = ${BINDINGS_TYPESYSTEM_PATH} + +enable-parent-ctor-heuristic +enable-pyside-extensions +enable-return-value-heuristic +use-isnull-as-nb_nonzero diff --git a/Test/WigglyWidget/PySideWrapper/bindings.xml b/Test/WigglyWidget/PySideWrapper/bindings.xml new file mode 100644 index 0000000..c3a3129 --- /dev/null +++ b/Test/WigglyWidget/PySideWrapper/bindings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/Test/WigglyWidget/PySideWrapper/generated.bat b/Test/WigglyWidget/PySideWrapper/generated.bat new file mode 100644 index 0000000..e69de29 diff --git a/Test/WigglyWidget/PySideWrapper/generated.sh b/Test/WigglyWidget/PySideWrapper/generated.sh new file mode 100644 index 0000000..e69de29 diff --git a/Test/WigglyWidget/PySideWrapper/requirements.txt b/Test/WigglyWidget/PySideWrapper/requirements.txt new file mode 100644 index 0000000..fa259e9 --- /dev/null +++ b/Test/WigglyWidget/PySideWrapper/requirements.txt @@ -0,0 +1,9 @@ +PySide2==5.15.2.1 + +https://download.qt.io/official_releases/QtForPython/shiboken2-generator/shiboken2_generator-5.15.2.1-5.15.2-cp35.cp36.cp37.cp38.cp39.cp310-none-win_amd64.whl; platform_machine == "x86_64" and platform_system == "Windows" + +https://download.qt.io/official_releases/QtForPython/shiboken2-generator/shiboken2_generator-5.15.2.1-5.15.2-cp35.cp36.cp37.cp38.cp39.cp310-none-win32.whl; platform_machine == "i686" and platform_system == "Windows" + +https://download.qt.io/official_releases/QtForPython/shiboken2-generator/shiboken2_generator-5.15.2.1-5.15.2-cp35.cp36.cp37.cp38.cp39.cp310-abi3-manylinux1_x86_64.whl; platform_machine == "x86_64" and platform_system == "Linux" + +https://download.qt.io/official_releases/QtForPython/shiboken2-generator/shiboken2_generator-5.15.2.1-5.15.2-cp35.cp36.cp37.cp38.cp39.cp310-abi3-macosx_10_13_intel.whl; platform_machine == "x86_64" and platform_system == "Darwin" diff --git a/Test/WigglyWidget/README.md b/Test/WigglyWidget/README.md new file mode 100644 index 0000000..817eaf4 --- /dev/null +++ b/Test/WigglyWidget/README.md @@ -0,0 +1,11 @@ +# WigglyWidget + +## Build + +Windows + +1. 使用 `QtCreator` 打开项目 `CMakeLists.txt`,勾选对应的Qt版本。 +2. 在 `QtCreator` 中通过 `项目`->`构建`->`构建步骤`->`详情` 里勾选 `all` 和 `install` +3. 进入 `PyQtWrapper` 目录,打开`vs cmd`,运行 `python -m pip install -r requirements.txt` +4. `sip-build --verbose --tracing --qmake=你的Qt目录下的qmake.exe路径`,等待编译完成 +5. `python TestWigglyWidget.py` 进行测试 \ No newline at end of file diff --git a/Test/WigglyWidget/TestWigglyWidget/CMakeLists.txt b/Test/WigglyWidget/TestWigglyWidget/CMakeLists.txt new file mode 100644 index 0000000..9ab36f9 --- /dev/null +++ b/Test/WigglyWidget/TestWigglyWidget/CMakeLists.txt @@ -0,0 +1,61 @@ +cmake_minimum_required(VERSION 3.5) + +project( + TestWigglyWidget + VERSION 0.1 + LANGUAGES CXX) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 配置安装目录 +set(CMAKE_INSTALL_DIR "${CMAKE_SOURCE_DIR}/dist") + +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets) + +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../LibWigglyWidget") + +set(PROJECT_SOURCES main.cpp) + +if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) + qt_add_executable(${PROJECT_NAME} MANUAL_FINALIZATION ${PROJECT_SOURCES}) + # Define target properties for Android with Qt 6 as: set_property(TARGET + # TestWigglyWidget APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR + # ${CMAKE_CURRENT_SOURCE_DIR}/android) For more information, see + # https://doc.qt.io/qt-6/qt-add-executable.html#target-creation +else() + if(ANDROID) + add_library(${PROJECT_NAME} SHARED ${PROJECT_SOURCES}) + # Define properties for Android with Qt 5 after find_package() calls as: + # set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android") + else() + add_executable(${PROJECT_NAME} ${PROJECT_SOURCES}) + endif() +endif() + +target_link_libraries(${PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Widgets + LibWigglyWidget) + +# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. If +# you are developing for iOS or macOS you should consider setting an explicit, +# fixed bundle identifier manually though. +if(${QT_VERSION} VERSION_LESS 6.1.0) + set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER + com.example.TestWigglyWidget) +endif() +set_target_properties( + ${PROJECT_NAME} + PROPERTIES ${BUNDLE_ID_OPTION} MACOSX_BUNDLE_BUNDLE_VERSION + ${PROJECT_VERSION} MACOSX_BUNDLE_SHORT_VERSION_STRING + ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} MACOSX_BUNDLE + TRUE WIN32_EXECUTABLE + TRUE) + +if(QT_VERSION_MAJOR EQUAL 6) + qt_finalize_executable(${PROJECT_NAME}) +endif() diff --git a/Test/WigglyWidget/TestWigglyWidget/main.cpp b/Test/WigglyWidget/TestWigglyWidget/main.cpp new file mode 100644 index 0000000..a74498e --- /dev/null +++ b/Test/WigglyWidget/TestWigglyWidget/main.cpp @@ -0,0 +1,11 @@ +#include + +#include "wigglywidget.h" + +int main(int argc, char *argv[]) { + QApplication a(argc, argv); + WigglyWidget w; + w.setText("pyqt.site"); + w.show(); + return a.exec(); +}