diff --git a/.pydevproject b/.pydevproject index 0f52659..a5ee231 100644 --- a/.pydevproject +++ b/.pydevproject @@ -1,8 +1,8 @@ - - -Default -python 3.0 - -/${PROJECT_DIR_NAME} - - + + +Python34 +python 3.0 + +/${PROJECT_DIR_NAME} + + diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs index bed5cda..29a0b31 100644 --- a/.settings/org.eclipse.core.resources.prefs +++ b/.settings/org.eclipse.core.resources.prefs @@ -35,6 +35,12 @@ encoding//\u5206\u5272\u7A97\u53E3\u7684\u5206\u5272\u6761\u91CD\u5199/Splitter. encoding//\u52A8\u753B\u7279\u6548/\u53F3\u952E\u83DC\u5355\u52A8\u753B.py=utf-8 encoding//\u52A8\u753B\u7279\u6548/\u6DE1\u5165\u6DE1\u51FA.py=utf-8 encoding//\u53F3\u4E0B\u89D2\u5F39\u51FA\u6846/WindowNotify.py=utf-8 +encoding//\u56FE\u7247/\u663E\u793A.9\u683C\u5F0F\u56FE\u7247/pyd\u7248\u672C/QtNinePatch/sip/configure.py=utf-8 +encoding//\u56FE\u7247/\u663E\u793A.9\u683C\u5F0F\u56FE\u7247/pyd\u7248\u672C/QtNinePatch/sip/testQtNinePatch.py=utf-8 +encoding//\u56FE\u7247/\u663E\u793A.9\u683C\u5F0F\u56FE\u7247/\u7EAFpython\u7248\u672C1/NinePatch.py=utf-8 +encoding//\u56FE\u7247/\u663E\u793A.9\u683C\u5F0F\u56FE\u7247/\u7EAFpython\u7248\u672C1/testNinePatch.py=utf-8 +encoding//\u56FE\u7247/\u663E\u793A.9\u683C\u5F0F\u56FE\u7247/\u7EAFpython\u7248\u672C2/QtNinePatch.py=utf-8 +encoding//\u56FE\u7247/\u663E\u793A.9\u683C\u5F0F\u56FE\u7247/\u7EAFpython\u7248\u672C2/testQtNinePatch.py=utf-8 encoding//\u56FE\u7247\u52A0\u8F7D/LoadImage.py=utf-8 encoding//\u56FE\u7247\u52A0\u8F7D/SlippedImgWidget.py=utf-8 encoding//\u56FE\u7247\u52A0\u8F7D/res_rc.py=utf-8 diff --git a/图片/显示.9格式图片/README.md b/图片/显示.9格式图片/README.md new file mode 100644 index 0000000..2d5b461 --- /dev/null +++ b/图片/显示.9格式图片/README.md @@ -0,0 +1,53 @@ +# .9格式图片显示(气泡) + +什么叫.9.PNG呢,这是安卓开发里面的一种特殊的图片 +这种格式的图片在android 环境下具有自适应调节大小的能力。 + + +(1)允许开发人员定义可扩展区域,当需要延伸图片以填充比图片本身更大区域时,可扩展区的内容被延展。 + +(2)允许开发人员定义内容显示区,用于显示文字或其他内容 + +目前手机QQ中很多漂亮的的聊天气泡就是.9格式的png图片 + +在Github开源库中搜索到两个C++版本的 + +1.一个是NinePatchQt https://github.com/Roninsc2/NinePatchQt + +2.一个是QtNinePatch https://github.com/soramimi/QtNinePatch + +### For PyQt +1、目前针对第一个库在2年前用[纯python版本1](纯python版本1/)参考源码重新写一个见 + +2、这次针对第二个写了[纯python版本2](纯python版本2/)的和[pyd编译好的](pyd版本)两个版本 + +### 说明 +1、建议优先使用pyd版本的(后续提供Python3.4 3.5 3.6 3.7 编译好的32为库文件),也可以自行编译,编译步骤见下文。 + +2、其次可以使用纯python版本2的(个人觉得方便调用) + +3、最后再考虑纯python版本1的吧 + +4、以上为个人意见,两个C++版本的写法不一样,但是核心算法应该是类似的。 + +### 自行编译 + +1、首先要安装好PyQt5、编译安装对应的sip、对应的VC++编译工具 + +2、用Qt Creator 打开pro文件进行编译 + +3、进入源码中的sip文件夹 + +4、修改configure.py文件 + +```python +# 这里是你的VC版本和对应的Qt目录中的文件夹 +config.platform = "win32-msvc2010" +qt_path = 'D:/soft/Qt/Qt5.5.1/5.5/msvc2010' +``` + +5、python configure.py + +# 截图 + +![截图1](ScreenShot/1.gif) diff --git a/图片/显示.9格式图片/ScreenShot/1.gif b/图片/显示.9格式图片/ScreenShot/1.gif new file mode 100644 index 0000000..0c3a73d Binary files /dev/null and b/图片/显示.9格式图片/ScreenShot/1.gif differ diff --git a/图片/显示.9格式图片/pyd版本/QtNinePatch/QtNinePatch.cpp b/图片/显示.9格式图片/pyd版本/QtNinePatch/QtNinePatch.cpp new file mode 100644 index 0000000..6bda869 --- /dev/null +++ b/图片/显示.9格式图片/pyd版本/QtNinePatch/QtNinePatch.cpp @@ -0,0 +1,136 @@ +#include "QtNinePatch.h" + +struct Part { + bool stretchable; + int pos; + int len; + Part() + : stretchable(false) + , pos(0) + , len(0) + { + } + Part(int pos, int len, bool stretchable) + : stretchable(stretchable) + , pos(pos) + , len(len) + { + } +}; + +static inline bool isStretchableMarker(QRgb pixel) +{ + return (qAlpha(pixel) >> 7) & 1; +} + +static QPixmap resize9patch(QImage const &image, int dw, int dh) +{ + int sw = image.width(); + int sh = image.height(); + if (sw > 2 && sh > 2 && dw > 0 && dh > 0) { + QPixmap pixmap(dw, dh); + pixmap.fill(Qt::transparent); + QPainter pr(&pixmap); + pr.setRenderHint(QPainter::SmoothPixmapTransform); + + std::vector horz; + std::vector vert; + int horz_stretch = 0; + int vert_stretch = 0; + { + int pos; + QRgb last; + QRgb next; + pos = 0; + last = image.pixel(1, 0); + for (int x = 1; x < sw - 1; x++) { + next = image.pixel(x + 1, 0); + if (isStretchableMarker(last) != isStretchableMarker(next) || x == sw - 2) { + bool stretchable = isStretchableMarker(last); + int len = x - pos; + horz.push_back(Part(pos, len, stretchable)); + if (stretchable) horz_stretch += len; + last = next; + pos = x; + } + } + pos = 0; + last = image.pixel(0, 1); + for (int y = 1; y < sh - 1; y++) { + next = image.pixel(0, y + 1); + if (isStretchableMarker(last) != isStretchableMarker(next) || y == sh - 2) { + bool stretchable = isStretchableMarker(last); + int len = y - pos; + vert.push_back(Part(pos, len, stretchable)); + if (stretchable) vert_stretch += len; + last = next; + pos = y; + } + } + } + double horz_mul = 0; + double vert_mul = 0; + if (horz_stretch > 0) horz_mul = (double)(dw - (sw - 2 - horz_stretch)) / horz_stretch; + if (vert_stretch > 0) vert_mul = (double)(dh - (sh - 2 - vert_stretch)) / vert_stretch; + int dy0 = 0; + int dy1 = 0; + double vstretch = 0; + for (int i = 0; i < (int)vert.size(); i++) { + int sy0 = vert[i].pos; + int sy1 = vert[i].pos + vert[i].len; + if (i + 1 == (int)vert.size()) { + dy1 = dh; + } else if (vert[i].stretchable) { + vstretch += (double)vert[i].len * vert_mul; + double s = floor(vstretch); + vstretch -= s; + dy1 += (int)s; + } else { + dy1 += vert[i].len; + } + int dx0 = 0; + int dx1 = 0; + double hstretch = 0; + for (int j = 0; j < (int)horz.size(); j++) { + int sx0 = horz[j].pos; + int sx1 = horz[j].pos + horz[j].len; + if (j + 1 == (int)horz.size()) { + dx1 = dw; + } else if (horz[j].stretchable) { + hstretch += (double)horz[j].len * horz_mul; + double s = floor(hstretch); + hstretch -= s; + dx1 += (int)s; + } else { + dx1 += horz[j].len; + } + pr.drawImage(QRect(dx0, dy0, dx1 - dx0, dy1 - dy0), image, QRect(sx0 + 1, sy0 + 1, sx1 - sx0, sy1 - sy0)); + dx0 = dx1; + } + dy0 = dy1; + } + + return pixmap; + } + return QPixmap(); +} + +QPixmap QtNinePatch::createPixmapFromNinePatchImage(const QImage &image, int dw, int dh) +{ + int w = dw; + int h = dh; + if (w < image.width() || h < image.height()) { // shrink + if (w < image.width()) w = image.width(); + if (h < image.height()) h = image.height(); + QPixmap pm1 = resize9patch(image, w, h); + if (pm1.isNull()) return QPixmap(); + QPixmap pm2(dw, dh); + pm2.fill(Qt::transparent); + QPainter pr(&pm2); + pr.setRenderHint(QPainter::SmoothPixmapTransform); + pr.drawPixmap(0, 0, dw, dh, pm1, 0, 0, w, h); + return pm2; + } else { + return resize9patch(image, w, h); + } +} diff --git a/图片/显示.9格式图片/pyd版本/QtNinePatch/QtNinePatch.h b/图片/显示.9格式图片/pyd版本/QtNinePatch/QtNinePatch.h new file mode 100644 index 0000000..782e4fa --- /dev/null +++ b/图片/显示.9格式图片/pyd版本/QtNinePatch/QtNinePatch.h @@ -0,0 +1,26 @@ +#ifndef QTNINEPATCH_H +#define QTNINEPATCH_H + +#include + +#if defined(QTNINEPATCHLIB_LIBRARY) +# define QTNINEPATCHLIBSHARED_EXPORT Q_DECL_EXPORT +#else +# define QTNINEPATCHLIBSHARED_EXPORT Q_DECL_IMPORT +#endif + +#include +#include +#include +#include + +class QtNinePatch; + +class QTNINEPATCHLIBSHARED_EXPORT QtNinePatch +{ + +public: + static QPixmap createPixmapFromNinePatchImage(const QImage &, int, int); +}; + +#endif // QTNINEPATCH_H diff --git a/图片/显示.9格式图片/pyd版本/QtNinePatch/QtNinePatch.pro b/图片/显示.9格式图片/pyd版本/QtNinePatch/QtNinePatch.pro new file mode 100644 index 0000000..2910b39 --- /dev/null +++ b/图片/显示.9格式图片/pyd版本/QtNinePatch/QtNinePatch.pro @@ -0,0 +1,38 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2018-10-25T14:12:10 +# +#------------------------------------------------- + +QT += core axcontainer gui + +#greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = QtNinePatch +TEMPLATE = lib +CONFIG += release +CONFIG += warn_off exceptions_off hide_symbols + +#CONFIG += staticlib +#CONFIG += dll static release + +DESTDIR = build +DLLDESTDIR = build + +DEFINES += QTNINEPATCHLIB_LIBRARY + +SOURCES += QtNinePatch.cpp + +HEADERS += QtNinePatch.h + +unix { + target.path = /usr/lib + INSTALLS += target +} + +DEFINES += _XKEYCHECK_H + +INCLUDEPATH += . +INCLUDEPATH += $$[QT_INSTALL_HEADERS] + +LIBS += -L$$[QT_INSTALL_LIBS] diff --git a/图片/显示.9格式图片/pyd版本/QtNinePatch/QtNinePatch.pro.user b/图片/显示.9格式图片/pyd版本/QtNinePatch/QtNinePatch.pro.user new file mode 100644 index 0000000..70ed833 --- /dev/null +++ b/图片/显示.9格式图片/pyd版本/QtNinePatch/QtNinePatch.pro.user @@ -0,0 +1,202 @@ + + + + + + EnvironmentId + {52a31565-4664-45f3-a98d-2ef2ad3b26ae} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + + ProjectExplorer.Project.Target.0 + + Desktop Qt 5.5.1 MSVC2010 32bit + Desktop Qt 5.5.1 MSVC2010 32bit + qt.55.win32_msvc2010_kit + 0 + 0 + 0 + + F:/Python/PyhtonTest/PyQt/QtNinePatch/build + + + true + qmake + + QtProjectManager.QMakeBuildStep + false + true + + false + false + false + + + true + Make + + Qt4ProjectManager.MakeStep + + false + + + + 2 + 构建 + + ProjectExplorer.BuildSteps.Build + + + + true + Make + + Qt4ProjectManager.MakeStep + + true + clean + + + 1 + 清理 + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + + Qt4ProjectManager.Qt4BuildConfiguration + 0 + true + + 1 + + + 0 + 部署 + + ProjectExplorer.BuildSteps.Deploy + + 1 + 在本地部署 + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + + false + false + false + false + true + 0.01 + 10 + true + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + + + %{buildDir} + 自定义执行档 + + ProjectExplorer.CustomExecutableRunConfiguration + 3768 + false + true + false + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 18 + + + Version + 18 + + diff --git a/图片/显示.9格式图片/pyd版本/QtNinePatch/sip/QtNinePatch.dll b/图片/显示.9格式图片/pyd版本/QtNinePatch/sip/QtNinePatch.dll new file mode 100644 index 0000000..4ac6d92 Binary files /dev/null and b/图片/显示.9格式图片/pyd版本/QtNinePatch/sip/QtNinePatch.dll differ diff --git a/图片/显示.9格式图片/pyd版本/QtNinePatch/sip/QtNinePatch.pyd b/图片/显示.9格式图片/pyd版本/QtNinePatch/sip/QtNinePatch.pyd new file mode 100644 index 0000000..73d7dc3 Binary files /dev/null and b/图片/显示.9格式图片/pyd版本/QtNinePatch/sip/QtNinePatch.pyd differ diff --git a/图片/显示.9格式图片/pyd版本/QtNinePatch/sip/QtNinePatch.sip b/图片/显示.9格式图片/pyd版本/QtNinePatch/sip/QtNinePatch.sip new file mode 100644 index 0000000..a5a56d2 --- /dev/null +++ b/图片/显示.9格式图片/pyd版本/QtNinePatch/sip/QtNinePatch.sip @@ -0,0 +1,14 @@ +%Module QtNinePatch +%Import QtCore/QtCoremod.sip +%Import QtGui/QtGuimod.sip + +class QtNinePatch +{ +%TypeHeaderCode +#include "../QtNinePatch.h" +%End + +public: + static QPixmap createPixmapFromNinePatchImage(const QImage &, int, int); + +}; \ No newline at end of file diff --git a/图片/显示.9格式图片/pyd版本/QtNinePatch/sip/configure.py b/图片/显示.9格式图片/pyd版本/QtNinePatch/sip/configure.py new file mode 100644 index 0000000..eae22a5 --- /dev/null +++ b/图片/显示.9格式图片/pyd版本/QtNinePatch/sip/configure.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# -*- encoding:utf-8 -*- + +import os +import shutil + +import PyQt5 +from PyQt5.QtCore import PYQT_CONFIGURATION +import sipconfig + + +# 模块名 +moduleName = 'QtNinePatch' + +# The name of the SIP build file generated by SIP and used by the build +# system. +build_file = '%s.sbf' % moduleName + +# Get the SIP configuration information. +config = sipconfig.Configuration() +# pyqt5.5 + python34 + msvc 2010 +config.platform = "win32-msvc2010" +qt_path = 'D:/soft/Qt/Qt5.5.1/5.5/msvc2010' +print('QT_DIR: %s' % qt_path) + +pyqtpath = os.path.dirname(PyQt5.__file__) +print('PyQt5 path: ', pyqtpath) + +config.sip_bin = os.path.join(pyqtpath, 'sip.exe') +config.default_sip_dir = os.path.join(pyqtpath, 'sip') + +sip_cmd = ' '.join([ + config.sip_bin, + '-c', "build", + '-b', "build/" + build_file, + '-I', config.default_sip_dir + '/PyQt5', + PYQT_CONFIGURATION.get('sip_flags', ''), + '%s.sip' % moduleName, +]) + +os.makedirs('build', exist_ok=True) +print(sip_cmd) +os.system(sip_cmd) + + +# Create the Makefile. +makefile = sipconfig.SIPModuleMakefile( + config, build_file, dir='build' +) + +# Add the library we are wrapping. The name doesn't include any platform +# specific prefixes or extensions (e.g. the 'lib' prefix on UNIX, or the +# '.dll' extension on Windows). + +# 添加头文件路径 +makefile.extra_include_dirs = [ + '../', '.', + os.path.join(qt_path, 'include'), + os.path.join(qt_path, 'include', 'QtCore'), + os.path.join(qt_path, 'include', 'QtGui') +] + +# 添加用C++编译的库文件路径 +makefile.extra_lib_dirs = [ + os.path.abspath('../build'), + os.path.join(qt_path, 'lib'), +] +print(makefile.extra_lib_dirs) + +makefile.extra_libs = [ + moduleName, + 'Qt5Core', + 'Qt5Gui' +] +print(makefile.extra_libs) + +# Generate the Makefile itself. +makefile.generate() + +shutil.copy('../build/%s.dll' % moduleName, '%s.dll' % moduleName) + +os.chdir('build') +os.system('nmake') + +os.chdir('../') +shutil.copy('build/%s.pyd' % moduleName, '%s.pyd' % moduleName) +print('ok') diff --git a/图片/显示.9格式图片/pyd版本/QtNinePatch/sip/skin_aio_friend_bubble_pressed.9.png b/图片/显示.9格式图片/pyd版本/QtNinePatch/sip/skin_aio_friend_bubble_pressed.9.png new file mode 100644 index 0000000..9fc0aeb Binary files /dev/null and b/图片/显示.9格式图片/pyd版本/QtNinePatch/sip/skin_aio_friend_bubble_pressed.9.png differ diff --git a/图片/显示.9格式图片/pyd版本/QtNinePatch/sip/testQtNinePatch.py b/图片/显示.9格式图片/pyd版本/QtNinePatch/sip/testQtNinePatch.py new file mode 100644 index 0000000..35584c5 --- /dev/null +++ b/图片/显示.9格式图片/pyd版本/QtNinePatch/sip/testQtNinePatch.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Created on 2018年10月25日 +@author: Irony +@site: https://pyqt5.com https://github.com/892768447 +@email: 892768447@qq.com +@file: testQtNinePatch +@description: +""" + + +__Author__ = """By: Irony +QQ: 892768447 +Email: 892768447@qq.com""" +__Copyright__ = "Copyright (c) 2018 Irony" +__Version__ = "Version 1.0" + +import sys + +from PyQt5.QtGui import QImage +from PyQt5.QtWidgets import QApplication, QLabel + +from QtNinePatch import QtNinePatch + + +class Label(QLabel): + + def __init__(self, *args, **kwargs): + super(Label, self).__init__(*args, **kwargs) + #.9 格式的图片 + self.image = QImage('skin_aio_friend_bubble_pressed.9.png') + + def showEvent(self, event): + super(Label, self).showEvent(event) + pixmap = QtNinePatch.createPixmapFromNinePatchImage( + self.image, self.width(), self.height()) + self.setPixmap(pixmap) + + def resizeEvent(self, event): + super(Label, self).resizeEvent(event) + pixmap = QtNinePatch.createPixmapFromNinePatchImage( + self.image, self.width(), self.height()) + self.setPixmap(pixmap) + + +app = QApplication(sys.argv) +w = Label() +w.resize(400, 200) +w.show() + +sys.exit(app.exec_()) diff --git a/图片/显示.9格式图片/纯python版本1/NinePatch.py b/图片/显示.9格式图片/纯python版本1/NinePatch.py new file mode 100644 index 0000000..d2703af --- /dev/null +++ b/图片/显示.9格式图片/纯python版本1/NinePatch.py @@ -0,0 +1,361 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Created on 2018年10月25日 +@author: Irony +@site: https://pyqt5.com https://github.com/892768447 +@email: 892768447@qq.com +@file: NinePatch +@description: +""" +from math import fabs + +from PyQt5.QtCore import QRect +from PyQt5.QtGui import QImage, QColor, QPainter, qRed, qGreen, qBlue, qAlpha + + +__Author__ = """By: Irony +QQ: 892768447 +Email: 892768447@qq.com""" +__Copyright__ = "Copyright (c) 2018 Irony" +__Version__ = "Version 1.0" + + +class _Exception(Exception): + + def __init__(self, imgW, imgH): + self.imgW = imgW + self.imgH = imgH + + +class NinePatchException(Exception): + + def __str__(self): + return "Nine patch error" + + +class ExceptionIncorrectWidth(_Exception): + + def __str__(self): + return "Input incorrect width. Mimimum width = :{imgW}".format(imgW=self.imgW) + + +class ExceptionIncorrectWidthAndHeight(_Exception): + + def __str__(self): + return "Input incorrect width width and height. Minimum width = :{imgW} . Minimum height = :{imgH}".format(imgW=self.imgW, imgH=self.imgH) + + +class ExceptionIncorrectHeight(_Exception): + + def __str__(self): + return "Input incorrect height. Minimum height = :{imgW}".format(imgW=self.imgW) + + +class ExceptionNot9Patch(Exception): + + def __str__(self): + return "It is not nine patch image" + + +class NinePatch: + + def __init__(self, fileName): + self.CachedImage = None # 缓存图片 + self.OldWidth = -1 + self.OldHeight = -1 + self.ResizeDistancesX = [] + self.ResizeDistancesY = [] # [(int,int)]数组 + self.setImage(fileName) + + def width(self): + return self.Image.width() + + def height(self): + return self.Image.height() + + def setImage(self, fileName): + self.Image = QImage(fileName) + if self.Image.isNull(): + return + + self.ContentArea = self.GetContentArea() + self.GetResizeArea() + if not self.ResizeDistancesX or not self.ResizeDistancesY: + raise ExceptionNot9Patch + + def __del__(self): + if hasattr(self, "CachedImage"): + del self.CachedImage + if hasattr(self, "Image"): + del self.Image + + def Draw(self, painter, x, y): + painter.drawImage(x, y, self.CachedImage) + + def SetImageSize(self, width, height): + resizeWidth = 0 + resizeHeight = 0 + for i in range(len(self.ResizeDistancesX)): + resizeWidth += self.ResizeDistancesX[i][1] + + for i in range(len(self.ResizeDistancesY)): + resizeHeight += self.ResizeDistancesY[i][1] + + if (width < (self.Image.width() - 2 - resizeWidth) and height < (self.Image.height() - 2 - resizeHeight)): + raise ExceptionIncorrectWidthAndHeight( + self.Image.width() - 2, self.Image.height() - 2) + + if (width < (self.Image.width() - 2 - resizeWidth)): + raise ExceptionIncorrectWidth( + self.Image.width() - 2, self.Image.height() - 2) + + if (height < (self.Image.height() - 2 - resizeHeight)): + raise ExceptionIncorrectHeight( + self.Image.width() - 2, self.Image.height() - 2) + + if (width != self.OldWidth or height != self.OldHeight): + self.OldWidth = width + self.OldHeight = height + self.UpdateCachedImage(width, height) + + @classmethod + def GetContentAreaRect(self, width, height): + # print("GetContentAreaRect : width:%d height:%d" % (width, height)) + return (QRect(self.ContentArea.x(), self.ContentArea.y(), (width - (self.Image.width() - 2 - self.ContentArea.width())), + (height - (self.Image.height() - 2 - self.ContentArea.height())))) + + def DrawScaledPart(self, oldRect, newRect, painter): + if (newRect.width() and newRect.height()): + # print("DrawScaledPart newRect.width:%d newRect.height:%d" % (newRect.width() , newRect.height())) + img = self.Image.copy(oldRect) + img = img.scaled(newRect.width(), newRect.height()) + painter.drawImage(newRect.x(), newRect.y(), img, + 0, 0, newRect.width(), newRect.height()) + + def DrawConstPart(self, oldRect, newRect, painter): + # print("DrawConstPart oldRect:{oldRect} newRect:{newRect}".format(oldRect = oldRect, newRect = newRect)) + img = self.Image.copy(oldRect) + painter.drawImage(newRect.x(), newRect.y(), img, 0, + 0, newRect.width(), newRect.height()) + + def IsColorBlack(self, color): + r = qRed(color) + g = qGreen(color) + b = qBlue(color) + a = qAlpha(color) + if a < 128: + return False + return r < 128 and g < 128 and b < 128 + + def GetContentArea(self): + j = self.Image.height() - 1 + left = 0 + right = 0 + for i in range(self.Image.width()): + if (self.IsColorBlack(self.Image.pixel(i, j)) and left == 0): + left = i + else: + if (left != 0 and self.IsColorBlack(self.Image.pixel(i, j))): + right = i + + if (left and not right): + right = left + + left -= 1 + i = self.Image.width() - 1 + top = 0 + bot = 0 + + for j in range(self.Image.height()): + if (self.IsColorBlack(self.Image.pixel(i, j)) and top == 0): + top = j + else: + if (top and self.IsColorBlack(self.Image.pixel(i, j))): + bot = j + + if (top and not bot): + bot = top + top -= 1 + # print("GetContentArea left: %d top:%d %d %d" % (left, top, right - left, bot - top)) + return (QRect(left, top, right - left, bot - top)) + + def GetResizeArea(self): + j = 0 + left = 0 + right = 0 + + for i in range(self.Image.width()): + if (self.IsColorBlack(self.Image.pixel(i, j)) and left == 0): + left = i + if (left and self.IsColorBlack(self.Image.pixel(i, j)) and not self.IsColorBlack(self.Image.pixel(i + 1, j))): + right = i + left -= 1 + # print("ResizeDistancesX.append ", left, " ", right - left) + self.ResizeDistancesX.append((left, right - left)) + right = 0 + left = 0 + i = 0 + top = 0 + bot = 0 + + for j in range(self.Image.height()): + if (self.IsColorBlack(self.Image.pixel(i, j)) and top == 0): + top = j + + if (top and self.IsColorBlack(self.Image.pixel(i, j)) and not self.IsColorBlack(self.Image.pixel(i, j + 1))): + bot = j + top -= 1 + # print("ResizeDistancesY.append ", top, " ", bot - top) + self.ResizeDistancesY.append((top, bot - top)) + top = 0 + bot = 0 + # print(self.ResizeDistancesX, len(self.ResizeDistancesX)) + # print(self.ResizeDistancesY, len(self.ResizeDistancesY)) + + def GetFactor(self, width, height, factorX, factorY): + topResize = width - (self.Image.width() - 2) + leftResize = height - (self.Image.height() - 2) + for i in range(len(self.ResizeDistancesX)): + topResize += self.ResizeDistancesX[i][1] + factorX += self.ResizeDistancesX[i][1] + + for i in range(len(self.ResizeDistancesY)): + leftResize += self.ResizeDistancesY[i][1] + factorY += self.ResizeDistancesY[i][1] + + factorX = float(topResize) / factorX + factorY = float(leftResize) / factorY + return factorX, factorY + + def UpdateCachedImage(self, width, height): + # print("UpdateCachedImage: ", width, " " , height) + self.CachedImage = QImage( + width, height, QImage.Format_ARGB32_Premultiplied) + self.CachedImage.fill(QColor(0, 0, 0, 0)) + painter = QPainter(self.CachedImage) + factorX = 0.0 + factorY = 0.0 + factorX, factorY = self.GetFactor(width, height, factorX, factorY) + # print("after GetFactor: ", width, height, factorX, factorY) + lostX = 0.0 + lostY = 0.0 + x1 = 0 # for image parts X + y1 = 0 # for image parts Y +# widthResize # width for image parts +# heightResize # height for image parts + resizeX = 0 + resizeY = 0 + offsetX = 0 + offsetY = 0 + for i in range(len(self.ResizeDistancesX)): + y1 = 0 + offsetY = 0 + lostY = 0.0 + for j in range(len(self.ResizeDistancesY)): + widthResize = self.ResizeDistancesX[i][0] - x1 + heightResize = self.ResizeDistancesY[j][0] - y1 + + self.DrawConstPart(QRect(x1 + 1, y1 + 1, widthResize, heightResize), + QRect(x1 + offsetX, y1 + offsetY, widthResize, heightResize), painter) + + y2 = self.ResizeDistancesY[j][0] + heightResize = self.ResizeDistancesY[j][1] + resizeY = round(float(heightResize) * factorY) + lostY += resizeY - (float(heightResize) * factorY) + if (fabs(lostY) >= 1.0): + if (lostY < 0): + resizeY += 1 + lostY += 1.0 + else: + resizeY -= 1 + lostY -= 1.0 + + self.DrawScaledPart(QRect(x1 + 1, y2 + 1, widthResize, heightResize), + QRect(x1 + offsetX, y2 + offsetY, widthResize, resizeY), painter) + + x2 = self.ResizeDistancesX[i][0] + widthResize = self.ResizeDistancesX[i][1] + heightResize = self.ResizeDistancesY[j][0] - y1 + resizeX = round(float(widthResize) * factorX) + lostX += resizeX - (float(widthResize) * factorX) + if (fabs(lostX) >= 1.0): + if (lostX < 0): + resizeX += 1 + lostX += 1.0 + else: + resizeX -= 1 + lostX -= 1.0 + + self.DrawScaledPart(QRect(x2 + 1, y1 + 1, widthResize, heightResize), + QRect(x2 + offsetX, y1 + offsetY, resizeX, heightResize), painter) + + heightResize = self.ResizeDistancesY[j][1] + self.DrawScaledPart(QRect(x2 + 1, y2 + 1, widthResize, heightResize), + QRect(x2 + offsetX, y2 + offsetY, resizeX, resizeY), painter) + + y1 = self.ResizeDistancesY[j][0] + self.ResizeDistancesY[j][1] + offsetY += resizeY - self.ResizeDistancesY[j][1] + + x1 = self.ResizeDistancesX[i][0] + self.ResizeDistancesX[i][1] + offsetX += resizeX - self.ResizeDistancesX[i][1] + + x1 = self.ResizeDistancesX[len( + self.ResizeDistancesX) - 1][0] + self.ResizeDistancesX[len(self.ResizeDistancesX) - 1][1] + widthResize = self.Image.width() - x1 - 2 + y1 = 0 + lostX = 0.0 + lostY = 0.0 + offsetY = 0 + for i in range(len(self.ResizeDistancesY)): + self.DrawConstPart(QRect(x1 + 1, y1 + 1, widthResize, self.ResizeDistancesY[i][0] - y1), + QRect(x1 + offsetX, y1 + offsetY, widthResize, self.ResizeDistancesY[i][0] - y1), painter) + y1 = self.ResizeDistancesY[i][0] + resizeY = round(float(self.ResizeDistancesY[i][1]) * factorY) + lostY += resizeY - (float(self.ResizeDistancesY[i][1]) * factorY) + if (fabs(lostY) >= 1.0): + if (lostY < 0): + resizeY += 1 + lostY += 1.0 + else: + resizeY -= 1 + lostY -= 1.0 + + self.DrawScaledPart(QRect(x1 + 1, y1 + 1, widthResize, self.ResizeDistancesY[i][1]), + QRect(x1 + offsetX, y1 + offsetY, widthResize, resizeY), painter) + y1 = self.ResizeDistancesY[i][0] + self.ResizeDistancesY[i][1] + offsetY += resizeY - self.ResizeDistancesY[i][1] + + y1 = self.ResizeDistancesY[len( + self.ResizeDistancesY) - 1][0] + self.ResizeDistancesY[len(self.ResizeDistancesY) - 1][1] + heightResize = self.Image.height() - y1 - 2 + x1 = 0 + offsetX = 0 + for i in range(len(self.ResizeDistancesX)): + self.DrawConstPart(QRect(x1 + 1, y1 + 1, self.ResizeDistancesX[i][0] - x1, heightResize), + QRect(x1 + offsetX, y1 + offsetY, self.ResizeDistancesX[i][0] - x1, heightResize), painter) + x1 = self.ResizeDistancesX[i][0] + resizeX = round(float(self.ResizeDistancesX[i][1]) * factorX) + lostX += resizeX - (float(self.ResizeDistancesX[i][1]) * factorX) + if (fabs(lostX) >= 1.0): + if (lostX < 0): + resizeX += 1 + lostX += 1.0 + else: + resizeX -= 1 + lostX += 1.0 + + self.DrawScaledPart(QRect(x1 + 1, y1 + 1, self.ResizeDistancesX[i][1], heightResize), + QRect(x1 + offsetX, y1 + offsetY, resizeX, heightResize), painter) + x1 = self.ResizeDistancesX[i][0] + self.ResizeDistancesX[i][1] + offsetX += resizeX - self.ResizeDistancesX[i][1] + + x1 = self.ResizeDistancesX[len( + self.ResizeDistancesX) - 1][0] + self.ResizeDistancesX[len(self.ResizeDistancesX) - 1][1] + widthResize = self.Image.width() - x1 - 2 + y1 = self.ResizeDistancesY[len( + self.ResizeDistancesY) - 1][0] + self.ResizeDistancesY[len(self.ResizeDistancesY) - 1][1] + heightResize = self.Image.height() - y1 - 2 + self.DrawConstPart(QRect(x1 + 1, y1 + 1, widthResize, heightResize), + QRect(x1 + offsetX, y1 + offsetY, widthResize, heightResize), painter) diff --git a/图片/显示.9格式图片/纯python版本1/skin_aio_friend_bubble_pressed.9.png b/图片/显示.9格式图片/纯python版本1/skin_aio_friend_bubble_pressed.9.png new file mode 100644 index 0000000..9fc0aeb Binary files /dev/null and b/图片/显示.9格式图片/纯python版本1/skin_aio_friend_bubble_pressed.9.png differ diff --git a/图片/显示.9格式图片/纯python版本1/testNinePatch.py b/图片/显示.9格式图片/纯python版本1/testNinePatch.py new file mode 100644 index 0000000..016f50c --- /dev/null +++ b/图片/显示.9格式图片/纯python版本1/testNinePatch.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Created on 2018年10月25日 +@author: Irony +@site: https://pyqt5.com https://github.com/892768447 +@email: 892768447@qq.com +@file: testNinePatch +@description: +""" + + +__Author__ = """By: Irony +QQ: 892768447 +Email: 892768447@qq.com""" +__Copyright__ = "Copyright (c) 2018 Irony" +__Version__ = "Version 1.0" + +import sys + +from PyQt5.QtGui import QImage, QPainter +from PyQt5.QtWidgets import QApplication, QLabel, QWidget + +from NinePatch import NinePatch + + +class Label(QWidget): + + def __init__(self, *args, **kwargs): + super(Label, self).__init__(*args, **kwargs) + #.9 格式的图片 + self.image = NinePatch('skin_aio_friend_bubble_pressed.9.png') + + def paintEvent(self, event): + super(Label, self).paintEvent(event) + painter = QPainter(self) + painter.setRenderHint(QPainter.Antialiasing) + painter.setRenderHint(QPainter.SmoothPixmapTransform) + try: + self.image.SetImageSize(self.width(), self.height()) + self.image.Draw(painter, 0, 0) + except Exception as e: + print(e) + + +app = QApplication(sys.argv) +w = Label() +w.resize(400, 200) +w.show() + +sys.exit(app.exec_()) diff --git a/图片/显示.9格式图片/纯python版本2/QtNinePatch.py b/图片/显示.9格式图片/纯python版本2/QtNinePatch.py new file mode 100644 index 0000000..8131b9e --- /dev/null +++ b/图片/显示.9格式图片/纯python版本2/QtNinePatch.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Created on 2018年10月25日 +@author: Irony +@site: https://pyqt5.com https://github.com/892768447 +@email: 892768447@qq.com +@file: QtNinePatch +@description: +""" +from math import floor + +from PyQt5.QtCore import Qt, QRect +from PyQt5.QtGui import qAlpha, QPixmap, QPainter + + +__Author__ = """By: Irony +QQ: 892768447 +Email: 892768447@qq.com""" +__Copyright__ = "Copyright (c) 2018 Irony" +__Version__ = "Version 1.0" + + +class Part: + + def __init__(self, pos=0, length=0, stretchable=False): + self.pos = pos + self.length = length + self.stretchable = stretchable + + +def isStretchableMarker(pixel): + return (qAlpha(pixel) >> 7) & 1 + + +def resize9patch(image, dw, dh): + sw = image.width() + sh = image.height() + if sw > 2 and sh > 2 and dw > 0 and dh > 0: + pixmap = QPixmap(dw, dh) + pixmap.fill(Qt.transparent) + pr = QPainter(pixmap) + pr.setRenderHint(QPainter.Antialiasing) + pr.setRenderHint(QPainter.SmoothPixmapTransform) + + horz = [] + vert = [] + horz_stretch = 0 + vert_stretch = 0 + + pos = 0 + last = image.pixel(1, 0) + for x in range(1, sw - 1): + nextP = image.pixel(x + 1, 0) + if isStretchableMarker(last) != isStretchableMarker(nextP) or x == sw - 2: + stretchable = isStretchableMarker(last) + length = x - pos + horz.append(Part(pos, length, stretchable)) + if stretchable: + horz_stretch += length + last = nextP + pos = x + pos = 0 + last = image.pixel(0, 1) + for y in range(1, sh - 1): + nextP = image.pixel(0, y + 1) + if isStretchableMarker(last) != isStretchableMarker(nextP) or y == sh - 2: + stretchable = isStretchableMarker(last) + length = y - pos + vert.append(Part(pos, length, stretchable)) + if stretchable: + vert_stretch += length + last = nextP + pos = y + + horz_mul = 0 + vert_mul = 0 + if horz_stretch > 0: + horz_mul = float((dw - (sw - 2 - horz_stretch)) / horz_stretch) + if vert_stretch > 0: + vert_mul = float((dh - (sh - 2 - vert_stretch)) / vert_stretch) + dy0 = 0 + dy1 = 0 + vstretch = 0 + len_vert = len(vert) + len_horz = len(horz) + for i in range(len_vert): + sy0 = vert[i].pos + sy1 = vert[i].pos + vert[i].length + if i + 1 == len_vert: + dy1 = dh + elif vert[i].stretchable: + vstretch += float(vert[i].length * vert_mul) + s = floor(vstretch) + vstretch -= s + dy1 += int(s) + else: + dy1 += vert[i].length + dx0 = 0 + dx1 = 0 + hstretch = 0 + for j in range(len_horz): + sx0 = horz[j].pos + sx1 = horz[j].pos + horz[j].length + if j + 1 == len_horz: + dx1 = dw + elif horz[j].stretchable: + hstretch += float(horz[j].length * horz_mul) + s = floor(hstretch) + hstretch -= s + dx1 += int(s) + else: + dx1 += horz[j].length + pr.drawImage(QRect(dx0, dy0, dx1 - dx0, dy1 - dy0), + image, QRect(sx0 + 1, sy0 + 1, sx1 - sx0, sy1 - sy0)) + dx0 = dx1 + dy0 = dy1 + return pixmap + return QPixmap() + + +def createPixmapFromNinePatchImage(image, dw, dh): + w = dw + h = dh + if w < image.width() or h < image.height(): # shrink + w = max(image.width(), w) + h = max(image.height(), h) + pm1 = resize9patch(image, w, h) + if pm1.isNull(): + return QPixmap() + pm2 = QPixmap(dw, dh) + pm2.fill(Qt.transparent) + pr = QPainter(pm2) + pr.setRenderHint(QPainter.Antialiasing) + pr.setRenderHint(QPainter.SmoothPixmapTransform) + pr.drawPixmap(0, 0, dw, dh, pm1, 0, 0, w, h) + return pm2 + else: + return resize9patch(image, dw, dh) diff --git a/图片/显示.9格式图片/纯python版本2/skin_aio_friend_bubble_pressed.9.png b/图片/显示.9格式图片/纯python版本2/skin_aio_friend_bubble_pressed.9.png new file mode 100644 index 0000000..9fc0aeb Binary files /dev/null and b/图片/显示.9格式图片/纯python版本2/skin_aio_friend_bubble_pressed.9.png differ diff --git a/图片/显示.9格式图片/纯python版本2/testQtNinePatch.py b/图片/显示.9格式图片/纯python版本2/testQtNinePatch.py new file mode 100644 index 0000000..3d1622c --- /dev/null +++ b/图片/显示.9格式图片/纯python版本2/testQtNinePatch.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Created on 2018年10月25日 +@author: Irony +@site: https://pyqt5.com https://github.com/892768447 +@email: 892768447@qq.com +@file: testQtNinePatch +@description: +""" + + +__Author__ = """By: Irony +QQ: 892768447 +Email: 892768447@qq.com""" +__Copyright__ = "Copyright (c) 2018 Irony" +__Version__ = "Version 1.0" + +import sys + +from PyQt5.QtGui import QImage +from PyQt5.QtWidgets import QApplication, QLabel + +import QtNinePatch + + +class Label(QLabel): + + def __init__(self, *args, **kwargs): + super(Label, self).__init__(*args, **kwargs) + #.9 格式的图片 + self.image = QImage('skin_aio_friend_bubble_pressed.9.png') + + def showEvent(self, event): + super(Label, self).showEvent(event) + pixmap = QtNinePatch.createPixmapFromNinePatchImage( + self.image, self.width(), self.height()) + self.setPixmap(pixmap) + + def resizeEvent(self, event): + super(Label, self).resizeEvent(event) + pixmap = QtNinePatch.createPixmapFromNinePatchImage( + self.image, self.width(), self.height()) + self.setPixmap(pixmap) + + +app = QApplication(sys.argv) +w = Label() +w.resize(400, 200) +w.show() + +sys.exit(app.exec_())