显示.9格式图片

This commit is contained in:
Irony 2018-10-26 01:03:24 +08:00
parent 762e6b9af2
commit 0478eb0019
20 changed files with 1229 additions and 8 deletions

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?><pydev_project>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Python34</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 3.0</pydev_property>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/${PROJECT_DIR_NAME}</path>

View file

@ -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

View file

@ -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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

View file

@ -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<Part> horz;
std::vector<Part> 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);
}
}

View file

@ -0,0 +1,26 @@
#ifndef QTNINEPATCH_H
#define QTNINEPATCH_H
#include <QtCore/qglobal.h>
#if defined(QTNINEPATCHLIB_LIBRARY)
# define QTNINEPATCHLIBSHARED_EXPORT Q_DECL_EXPORT
#else
# define QTNINEPATCHLIBSHARED_EXPORT Q_DECL_IMPORT
#endif
#include <QImage>
#include <QPixmap>
#include <QPainter>
#include <vector>
class QtNinePatch;
class QTNINEPATCHLIBSHARED_EXPORT QtNinePatch
{
public:
static QPixmap createPixmapFromNinePatchImage(const QImage &, int, int);
};
#endif // QTNINEPATCH_H

View file

@ -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]

View file

@ -0,0 +1,202 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 3.5.1, 2018-10-26T00:09:02. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{52a31565-4664-45f3-a98d-2ef2ad3b26ae}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap"/>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop Qt 5.5.1 MSVC2010 32bit</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop Qt 5.5.1 MSVC2010 32bit</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">qt.55.win32_msvc2010_kit</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">F:/Python/PyhtonTest/PyQt/QtNinePatch/build</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibraryAuto">true</value>
<value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.SeparateDebugInfo">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.UseQtQuickCompiler">false</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">构建</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">清理</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Release</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
<value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">部署</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">在本地部署</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Arguments"></value>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Executable"></value>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.WorkingDirectory">%{buildDir}</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">自定义执行档</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">18</value>
</data>
<data>
<variable>Version</variable>
<value type="int">18</value>
</data>
</qtcreator>

View file

@ -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);
};

View file

@ -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')

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View file

@ -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_())

View file

@ -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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View file

@ -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_())

View file

@ -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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View file

@ -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_())