1
0
mirror of https://github.com/arsenetar/dupeguru.git synced 2025-05-08 17:59:50 +00:00

Compare commits

..

No commits in common. "809116c764a922ca9cf806eaece105f7b2c250cf" and "47dbe805bb563f3f4ffb90730fc8b1349448789e" have entirely different histories.

10 changed files with 441 additions and 398 deletions

View File

@ -1,6 +1,3 @@
recursive-include core *.h recursive-include core *.h
recursive-include core *.m recursive-include core *.m
include run.py include run.py
graft locale
graft help
graft qtlib/locale

View File

@ -4,16 +4,19 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.gnu.org/licenses/gpl-3.0.html # http://www.gnu.org/licenses/gpl-3.0.html
from pathlib import Path import os
import os.path as op
import sys import sys
from optparse import OptionParser from optparse import OptionParser
import shutil import shutil
from setuptools import sandbox from setuptools import setup, Extension
from hscommon import sphinxgen from hscommon import sphinxgen
from hscommon.build import ( from hscommon.build import (
add_to_pythonpath, add_to_pythonpath,
print_and_do, print_and_do,
move_all,
fix_qt_resource_file, fix_qt_resource_file,
) )
from hscommon import loc from hscommon import loc
@ -60,14 +63,14 @@ def parse_args():
def build_help(): def build_help():
print("Generating Help") print("Generating Help")
current_path = Path(".").absolute() current_path = op.abspath(".")
help_basepath = current_path.joinpath("help", "en") help_basepath = op.join(current_path, "help", "en")
help_destpath = current_path.joinpath("build", "help") help_destpath = op.join(current_path, "build", "help")
changelog_path = current_path.joinpath("help", "changelog") changelog_path = op.join(current_path, "help", "changelog")
tixurl = "https://github.com/arsenetar/dupeguru/issues/{}" tixurl = "https://github.com/arsenetar/dupeguru/issues/{}"
confrepl = {"language": "en"} confrepl = {"language": "en"}
changelogtmpl = current_path.joinpath("help", "changelog.tmpl") changelogtmpl = op.join(current_path, "help", "changelog.tmpl")
conftmpl = current_path.joinpath("help", "conf.tmpl") conftmpl = op.join(current_path, "help", "conf.tmpl")
sphinxgen.gen( sphinxgen.gen(
help_basepath, help_basepath,
help_destpath, help_destpath,
@ -80,15 +83,15 @@ def build_help():
def build_qt_localizations(): def build_qt_localizations():
loc.compile_all_po(Path("qtlib", "locale")) loc.compile_all_po(op.join("qtlib", "locale"))
loc.merge_locale_dir(Path("qtlib", "locale"), "locale") loc.merge_locale_dir(op.join("qtlib", "locale"), "locale")
def build_localizations(): def build_localizations():
loc.compile_all_po("locale") loc.compile_all_po("locale")
build_qt_localizations() build_qt_localizations()
locale_dest = Path("build", "locale") locale_dest = op.join("build", "locale")
if locale_dest.exists(): if op.exists(locale_dest):
shutil.rmtree(locale_dest) shutil.rmtree(locale_dest)
shutil.copytree("locale", locale_dest, ignore=shutil.ignore_patterns("*.po", "*.pot")) shutil.copytree("locale", locale_dest, ignore=shutil.ignore_patterns("*.po", "*.pot"))
@ -96,35 +99,57 @@ def build_localizations():
def build_updatepot(): def build_updatepot():
print("Building .pot files from source files") print("Building .pot files from source files")
print("Building core.pot") print("Building core.pot")
loc.generate_pot(["core"], Path("locale", "core.pot"), ["tr"]) loc.generate_pot(["core"], op.join("locale", "core.pot"), ["tr"])
print("Building columns.pot") print("Building columns.pot")
loc.generate_pot(["core"], Path("locale", "columns.pot"), ["coltr"]) loc.generate_pot(["core"], op.join("locale", "columns.pot"), ["coltr"])
print("Building ui.pot") print("Building ui.pot")
# When we're not under OS X, we don't want to overwrite ui.pot because it contains Cocoa locs # When we're not under OS X, we don't want to overwrite ui.pot because it contains Cocoa locs
# We want to merge the generated pot with the old pot in the most preserving way possible. # We want to merge the generated pot with the old pot in the most preserving way possible.
ui_packages = ["qt", Path("cocoa", "inter")] ui_packages = ["qt", op.join("cocoa", "inter")]
loc.generate_pot(ui_packages, Path("locale", "ui.pot"), ["tr"], merge=True) loc.generate_pot(ui_packages, op.join("locale", "ui.pot"), ["tr"], merge=True)
print("Building qtlib.pot") print("Building qtlib.pot")
loc.generate_pot(["qtlib"], Path("qtlib", "locale", "qtlib.pot"), ["tr"]) loc.generate_pot(["qtlib"], op.join("qtlib", "locale", "qtlib.pot"), ["tr"])
def build_mergepot(): def build_mergepot():
print("Updating .po files using .pot files") print("Updating .po files using .pot files")
loc.merge_pots_into_pos("locale") loc.merge_pots_into_pos("locale")
loc.merge_pots_into_pos(Path("qtlib", "locale")) loc.merge_pots_into_pos(op.join("qtlib", "locale"))
# loc.merge_pots_into_pos(Path("cocoalib", "locale")) # loc.merge_pots_into_pos(op.join("cocoalib", "locale"))
def build_normpo(): def build_normpo():
loc.normalize_all_pos("locale") loc.normalize_all_pos("locale")
loc.normalize_all_pos(Path("qtlib", "locale")) loc.normalize_all_pos(op.join("qtlib", "locale"))
# loc.normalize_all_pos(Path("cocoalib", "locale")) # loc.normalize_all_pos(op.join("cocoalib", "locale"))
def build_pe_modules(): def build_pe_modules():
print("Building PE Modules") print("Building PE Modules")
# Leverage setup.py to build modules exts = [
sandbox.run_setup("setup.py", ["build_ext", "--inplace"]) Extension(
"_block",
[
op.join("core", "pe", "modules", "block.c"),
op.join("core", "pe", "modules", "common.c"),
],
),
Extension(
"_cache",
[
op.join("core", "pe", "modules", "cache.c"),
op.join("core", "pe", "modules", "common.c"),
],
),
]
exts.append(Extension("_block_qt", [op.join("qt", "pe", "modules", "block.c")]))
setup(
script_args=["build_ext", "--inplace"],
ext_modules=exts,
)
move_all("_block_qt*", op.join("qt", "pe"))
move_all("_block*", op.join("core", "pe"))
move_all("_cache*", op.join("core", "pe"))
def build_normal(): def build_normal():
@ -135,8 +160,8 @@ def build_normal():
print("Building localizations") print("Building localizations")
build_localizations() build_localizations()
print("Building Qt stuff") print("Building Qt stuff")
print_and_do("pyrcc5 {0} > {1}".format(Path("qt", "dg.qrc"), Path("qt", "dg_rc.py"))) print_and_do("pyrcc5 {0} > {1}".format(op.join("qt", "dg.qrc"), op.join("qt", "dg_rc.py")))
fix_qt_resource_file(Path("qt", "dg_rc.py")) fix_qt_resource_file(op.join("qt", "dg_rc.py"))
build_help() build_help()
@ -144,10 +169,10 @@ def main():
if sys.version_info < (3, 6): if sys.version_info < (3, 6):
sys.exit("Python < 3.6 is unsupported.") sys.exit("Python < 3.6 is unsupported.")
options = parse_args() options = parse_args()
if options.clean and Path("build").exists(): if options.clean and op.exists("build"):
shutil.rmtree("build") shutil.rmtree("build")
if not Path("build").exists(): if not op.exists("build"):
Path("build").mkdir() os.mkdir("build")
if options.doc: if options.doc:
build_help() build_help()
elif options.loc: elif options.loc:

View File

@ -2,9 +2,9 @@
* Created On: 2010-01-30 * Created On: 2010-01-30
* Copyright 2014 Hardcoded Software (http://www.hardcoded.net) * Copyright 2014 Hardcoded Software (http://www.hardcoded.net)
* *
* This software is licensed under the "BSD" License as described in the * This software is licensed under the "BSD" License as described in the "LICENSE" file,
* "LICENSE" file, which should be included with this package. The terms are * which should be included with this package. The terms are also available at
* also available at http://www.hardcoded.net/licenses/bsd_license * http://www.hardcoded.net/licenses/bsd_license
*/ */
#include "common.h" #include "common.h"
@ -14,84 +14,86 @@ static PyObject *NoBlocksError;
/* avgdiff/maxdiff has been called with 2 block lists of different size. */ /* avgdiff/maxdiff has been called with 2 block lists of different size. */
static PyObject *DifferentBlockCountError; static PyObject *DifferentBlockCountError;
/* Returns a 3 sized tuple containing the mean color of 'image'. /* Returns a 3 sized tuple containing the mean color of 'image'.
* image: a PIL image or crop. * image: a PIL image or crop.
*/ */
static PyObject *getblock(PyObject *image) { static PyObject* getblock(PyObject *image)
int i, totr, totg, totb; {
Py_ssize_t pixel_count; int i, totr, totg, totb;
PyObject *ppixels; Py_ssize_t pixel_count;
PyObject *ppixels;
totr = totg = totb = 0;
ppixels = PyObject_CallMethod(image, "getdata", NULL); totr = totg = totb = 0;
if (ppixels == NULL) { ppixels = PyObject_CallMethod(image, "getdata", NULL);
return NULL; if (ppixels == NULL) {
} return NULL;
}
pixel_count = PySequence_Length(ppixels);
for (i = 0; i < pixel_count; i++) { pixel_count = PySequence_Length(ppixels);
PyObject *ppixel, *pr, *pg, *pb; for (i=0; i<pixel_count; i++) {
int r, g, b; PyObject *ppixel, *pr, *pg, *pb;
int r, g, b;
ppixel = PySequence_ITEM(ppixels, i);
pr = PySequence_ITEM(ppixel, 0); ppixel = PySequence_ITEM(ppixels, i);
pg = PySequence_ITEM(ppixel, 1); pr = PySequence_ITEM(ppixel, 0);
pb = PySequence_ITEM(ppixel, 2); pg = PySequence_ITEM(ppixel, 1);
Py_DECREF(ppixel); pb = PySequence_ITEM(ppixel, 2);
r = PyLong_AsLong(pr); Py_DECREF(ppixel);
g = PyLong_AsLong(pg); r = PyLong_AsLong(pr);
b = PyLong_AsLong(pb); g = PyLong_AsLong(pg);
Py_DECREF(pr); b = PyLong_AsLong(pb);
Py_DECREF(pg); Py_DECREF(pr);
Py_DECREF(pb); Py_DECREF(pg);
Py_DECREF(pb);
totr += r;
totg += g; totr += r;
totb += b; totg += g;
} totb += b;
}
Py_DECREF(ppixels);
Py_DECREF(ppixels);
if (pixel_count) {
totr /= pixel_count; if (pixel_count) {
totg /= pixel_count; totr /= pixel_count;
totb /= pixel_count; totg /= pixel_count;
} totb /= pixel_count;
}
return inttuple(3, totr, totg, totb);
return inttuple(3, totr, totg, totb);
} }
/* Returns the difference between the first block and the second. /* Returns the difference between the first block and the second.
* It returns an absolute sum of the 3 differences (RGB). * It returns an absolute sum of the 3 differences (RGB).
*/ */
static int diff(PyObject *first, PyObject *second) { static int diff(PyObject *first, PyObject *second)
int r1, g1, b1, r2, b2, g2; {
PyObject *pr, *pg, *pb; int r1, g1, b1, r2, b2, g2;
pr = PySequence_ITEM(first, 0); PyObject *pr, *pg, *pb;
pg = PySequence_ITEM(first, 1); pr = PySequence_ITEM(first, 0);
pb = PySequence_ITEM(first, 2); pg = PySequence_ITEM(first, 1);
r1 = PyLong_AsLong(pr); pb = PySequence_ITEM(first, 2);
g1 = PyLong_AsLong(pg); r1 = PyLong_AsLong(pr);
b1 = PyLong_AsLong(pb); g1 = PyLong_AsLong(pg);
Py_DECREF(pr); b1 = PyLong_AsLong(pb);
Py_DECREF(pg); Py_DECREF(pr);
Py_DECREF(pb); Py_DECREF(pg);
Py_DECREF(pb);
pr = PySequence_ITEM(second, 0);
pg = PySequence_ITEM(second, 1); pr = PySequence_ITEM(second, 0);
pb = PySequence_ITEM(second, 2); pg = PySequence_ITEM(second, 1);
r2 = PyLong_AsLong(pr); pb = PySequence_ITEM(second, 2);
g2 = PyLong_AsLong(pg); r2 = PyLong_AsLong(pr);
b2 = PyLong_AsLong(pb); g2 = PyLong_AsLong(pg);
Py_DECREF(pr); b2 = PyLong_AsLong(pb);
Py_DECREF(pg); Py_DECREF(pr);
Py_DECREF(pb); Py_DECREF(pg);
Py_DECREF(pb);
return abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2);
return abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2);
} }
PyDoc_STRVAR(block_getblocks2_doc, PyDoc_STRVAR(block_getblocks2_doc,
"Returns a list of blocks (3 sized tuples).\n\ "Returns a list of blocks (3 sized tuples).\n\
\n\ \n\
image: A PIL image to base the blocks on.\n\ image: A PIL image to base the blocks on.\n\
block_count_per_side: This integer determine the number of blocks the function will return.\n\ block_count_per_side: This integer determine the number of blocks the function will return.\n\
@ -99,150 +101,153 @@ If it is 10, for example, 100 blocks will be returns (10 width, 10 height). The
necessarely cover square areas. The area covered by each block will be proportional to the image\n\ necessarely cover square areas. The area covered by each block will be proportional to the image\n\
itself.\n"); itself.\n");
static PyObject *block_getblocks2(PyObject *self, PyObject *args) { static PyObject* block_getblocks2(PyObject *self, PyObject *args)
int block_count_per_side, width, height, block_width, block_height, ih; {
PyObject *image; int block_count_per_side, width, height, block_width, block_height, ih;
PyObject *pimage_size, *pwidth, *pheight; PyObject *image;
PyObject *result; PyObject *pimage_size, *pwidth, *pheight;
PyObject *result;
if (!PyArg_ParseTuple(args, "Oi", &image, &block_count_per_side)) {
return NULL; if (!PyArg_ParseTuple(args, "Oi", &image, &block_count_per_side)) {
}
pimage_size = PyObject_GetAttrString(image, "size");
pwidth = PySequence_ITEM(pimage_size, 0);
pheight = PySequence_ITEM(pimage_size, 1);
width = PyLong_AsLong(pwidth);
height = PyLong_AsLong(pheight);
Py_DECREF(pimage_size);
Py_DECREF(pwidth);
Py_DECREF(pheight);
if (!(width && height)) {
return PyList_New(0);
}
block_width = max(width / block_count_per_side, 1);
block_height = max(height / block_count_per_side, 1);
result = PyList_New((Py_ssize_t)block_count_per_side * block_count_per_side);
if (result == NULL) {
return NULL;
}
for (ih = 0; ih < block_count_per_side; ih++) {
int top, bottom, iw;
top = min(ih * block_height, height - block_height);
bottom = top + block_height;
for (iw = 0; iw < block_count_per_side; iw++) {
int left, right;
PyObject *pbox;
PyObject *pmethodname;
PyObject *pcrop;
PyObject *pblock;
left = min(iw * block_width, width - block_width);
right = left + block_width;
pbox = inttuple(4, left, top, right, bottom);
pmethodname = PyUnicode_FromString("crop");
pcrop = PyObject_CallMethodObjArgs(image, pmethodname, pbox, NULL);
Py_DECREF(pmethodname);
Py_DECREF(pbox);
if (pcrop == NULL) {
Py_DECREF(result);
return NULL; return NULL;
}
pblock = getblock(pcrop);
Py_DECREF(pcrop);
if (pblock == NULL) {
Py_DECREF(result);
return NULL;
}
PyList_SET_ITEM(result, ih * block_count_per_side + iw, pblock);
} }
}
pimage_size = PyObject_GetAttrString(image, "size");
return result; pwidth = PySequence_ITEM(pimage_size, 0);
pheight = PySequence_ITEM(pimage_size, 1);
width = PyLong_AsLong(pwidth);
height = PyLong_AsLong(pheight);
Py_DECREF(pimage_size);
Py_DECREF(pwidth);
Py_DECREF(pheight);
if (!(width && height)) {
return PyList_New(0);
}
block_width = max(width / block_count_per_side, 1);
block_height = max(height / block_count_per_side, 1);
result = PyList_New(block_count_per_side * block_count_per_side);
if (result == NULL) {
return NULL;
}
for (ih=0; ih<block_count_per_side; ih++) {
int top, bottom, iw;
top = min(ih*block_height, height-block_height);
bottom = top + block_height;
for (iw=0; iw<block_count_per_side; iw++) {
int left, right;
PyObject *pbox;
PyObject *pmethodname;
PyObject *pcrop;
PyObject *pblock;
left = min(iw*block_width, width-block_width);
right = left + block_width;
pbox = inttuple(4, left, top, right, bottom);
pmethodname = PyUnicode_FromString("crop");
pcrop = PyObject_CallMethodObjArgs(image, pmethodname, pbox, NULL);
Py_DECREF(pmethodname);
Py_DECREF(pbox);
if (pcrop == NULL) {
Py_DECREF(result);
return NULL;
}
pblock = getblock(pcrop);
Py_DECREF(pcrop);
if (pblock == NULL) {
Py_DECREF(result);
return NULL;
}
PyList_SET_ITEM(result, ih*block_count_per_side+iw, pblock);
}
}
return result;
} }
PyDoc_STRVAR(block_avgdiff_doc, PyDoc_STRVAR(block_avgdiff_doc,
"Returns the average diff between first blocks and seconds.\n\ "Returns the average diff between first blocks and seconds.\n\
\n\ \n\
If the result surpasses limit, limit + 1 is returned, except if less than min_iterations\n\ If the result surpasses limit, limit + 1 is returned, except if less than min_iterations\n\
iterations have been made in the blocks.\n"); iterations have been made in the blocks.\n");
static PyObject *block_avgdiff(PyObject *self, PyObject *args) { static PyObject* block_avgdiff(PyObject *self, PyObject *args)
PyObject *first, *second; {
int limit, min_iterations; PyObject *first, *second;
Py_ssize_t count; int limit, min_iterations;
int sum, i, result; Py_ssize_t count;
int sum, i, result;
if (!PyArg_ParseTuple(args, "OOii", &first, &second, &limit,
&min_iterations)) { if (!PyArg_ParseTuple(args, "OOii", &first, &second, &limit, &min_iterations)) {
return NULL; return NULL;
}
count = PySequence_Length(first);
if (count != PySequence_Length(second)) {
PyErr_SetString(DifferentBlockCountError, "");
return NULL;
}
if (!count) {
PyErr_SetString(NoBlocksError, "");
return NULL;
}
sum = 0;
for (i = 0; i < count; i++) {
int iteration_count;
PyObject *item1, *item2;
iteration_count = i + 1;
item1 = PySequence_ITEM(first, i);
item2 = PySequence_ITEM(second, i);
sum += diff(item1, item2);
Py_DECREF(item1);
Py_DECREF(item2);
if ((sum > limit * iteration_count) &&
(iteration_count >= min_iterations)) {
return PyLong_FromLong(limit + 1);
} }
}
count = PySequence_Length(first);
result = sum / count; if (count != PySequence_Length(second)) {
if (!result && sum) { PyErr_SetString(DifferentBlockCountError, "");
result = 1; return NULL;
} }
return PyLong_FromLong(result); if (!count) {
PyErr_SetString(NoBlocksError, "");
return NULL;
}
sum = 0;
for (i=0; i<count; i++) {
int iteration_count;
PyObject *item1, *item2;
iteration_count = i + 1;
item1 = PySequence_ITEM(first, i);
item2 = PySequence_ITEM(second, i);
sum += diff(item1, item2);
Py_DECREF(item1);
Py_DECREF(item2);
if ((sum > limit*iteration_count) && (iteration_count >= min_iterations)) {
return PyLong_FromLong(limit + 1);
}
}
result = sum / count;
if (!result && sum) {
result = 1;
}
return PyLong_FromLong(result);
} }
static PyMethodDef BlockMethods[] = { static PyMethodDef BlockMethods[] = {
{"getblocks2", block_getblocks2, METH_VARARGS, block_getblocks2_doc}, {"getblocks2", block_getblocks2, METH_VARARGS, block_getblocks2_doc},
{"avgdiff", block_avgdiff, METH_VARARGS, block_avgdiff_doc}, {"avgdiff", block_avgdiff, METH_VARARGS, block_avgdiff_doc},
{NULL, NULL, 0, NULL} /* Sentinel */ {NULL, NULL, 0, NULL} /* Sentinel */
}; };
static struct PyModuleDef BlockDef = {PyModuleDef_HEAD_INIT, static struct PyModuleDef BlockDef = {
"_block", PyModuleDef_HEAD_INIT,
NULL, "_block",
-1, NULL,
BlockMethods, -1,
NULL, BlockMethods,
NULL, NULL,
NULL, NULL,
NULL}; NULL,
NULL
};
PyObject *PyInit__block(void) { PyObject *
PyObject *m = PyModule_Create(&BlockDef); PyInit__block(void)
if (m == NULL) { {
return NULL; PyObject *m = PyModule_Create(&BlockDef);
} if (m == NULL) {
return NULL;
}
NoBlocksError = PyErr_NewException("_block.NoBlocksError", NULL, NULL);
PyModule_AddObject(m, "NoBlocksError", NoBlocksError);
DifferentBlockCountError = PyErr_NewException("_block.DifferentBlockCountError", NULL, NULL);
PyModule_AddObject(m, "DifferentBlockCountError", DifferentBlockCountError);
NoBlocksError = PyErr_NewException("_block.NoBlocksError", NULL, NULL); return m;
PyModule_AddObject(m, "NoBlocksError", NoBlocksError);
DifferentBlockCountError =
PyErr_NewException("_block.DifferentBlockCountError", NULL, NULL);
PyModule_AddObject(m, "DifferentBlockCountError", DifferentBlockCountError);
return m;
} }

View File

@ -4,7 +4,7 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.gnu.org/licenses/gpl-3.0.html # http://www.gnu.org/licenses/gpl-3.0.html
from pathlib import Path import os.path as op
import re import re
from .build import read_changelog_file, filereplace from .build import read_changelog_file, filereplace
@ -48,9 +48,9 @@ def gen(
if confrepl is None: if confrepl is None:
confrepl = {} confrepl = {}
if confpath is None: if confpath is None:
confpath = Path(basepath, "conf.tmpl") confpath = op.join(basepath, "conf.tmpl")
if changelogtmpl is None: if changelogtmpl is None:
changelogtmpl = Path(basepath, "changelog.tmpl") changelogtmpl = op.join(basepath, "changelog.tmpl")
changelog = read_changelog_file(changelogpath) changelog = read_changelog_file(changelogpath)
tix = tixgen(tixurl) tix = tixgen(tixurl)
rendered_logs = [] rendered_logs = []
@ -62,13 +62,13 @@ def gen(
rendered = CHANGELOG_FORMAT.format(version=log["version"], date=log["date_str"], description=description) rendered = CHANGELOG_FORMAT.format(version=log["version"], date=log["date_str"], description=description)
rendered_logs.append(rendered) rendered_logs.append(rendered)
confrepl["version"] = changelog[0]["version"] confrepl["version"] = changelog[0]["version"]
changelog_out = Path(basepath, "changelog.rst") changelog_out = op.join(basepath, "changelog.rst")
filereplace(changelogtmpl, changelog_out, changelog="\n".join(rendered_logs)) filereplace(changelogtmpl, changelog_out, changelog="\n".join(rendered_logs))
if Path(confpath).exists(): if op.exists(confpath):
conf_out = Path(basepath, "conf.py") conf_out = op.join(basepath, "conf.py")
filereplace(confpath, conf_out, **confrepl) filereplace(confpath, conf_out, **confrepl)
# Call the sphinx_build function, which is the same as doing sphinx-build from cli # Call the sphinx_build function, which is the same as doing sphinx-build from cli
try: try:
sphinx_build([str(basepath), str(destpath)]) sphinx_build([basepath, destpath])
except SystemExit: except SystemExit:
print("Sphinx called sys.exit(), but we're cancelling it because we don't actually want to exit") print("Sphinx called sys.exit(), but we're cancelling it because we don't actually want to exit")

View File

@ -71,7 +71,7 @@ def package_debian_distribution(distribution):
version = "{}~{}".format(app_version, distribution) version = "{}~{}".format(app_version, distribution)
destpath = op.join("build", "dupeguru-{}".format(version)) destpath = op.join("build", "dupeguru-{}".format(version))
srcpath = op.join(destpath, "src") srcpath = op.join(destpath, "src")
packages = ["hscommon", "core", "qtlib", "qt", "send2trash"] packages = ["hscommon", "core", "qtlib", "qt", "send2trash", "hsaudiotag"]
copy_files_to_package(srcpath, packages, with_so=False) copy_files_to_package(srcpath, packages, with_so=False)
os.mkdir(op.join(destpath, "modules")) os.mkdir(op.join(destpath, "modules"))
copy_all(op.join("core", "pe", "modules", "*.*"), op.join(destpath, "modules")) copy_all(op.join("core", "pe", "modules", "*.*"), op.join(destpath, "modules"))
@ -122,7 +122,14 @@ def package_arch():
# need to include them). # need to include them).
print("Packaging for Arch") print("Packaging for Arch")
srcpath = op.join("build", "dupeguru-arch") srcpath = op.join("build", "dupeguru-arch")
packages = ["hscommon", "core", "qtlib", "qt", "send2trash"] packages = [
"hscommon",
"core",
"qtlib",
"qt",
"send2trash",
"hsaudiotag",
]
copy_files_to_package(srcpath, packages, with_so=True) copy_files_to_package(srcpath, packages, with_so=True)
shutil.copy(op.join("images", "dgse_logo_128.png"), srcpath) shutil.copy(op.join("images", "dgse_logo_128.png"), srcpath)
debopts = json.load(open(op.join("pkg", "arch", "dupeguru.json"))) debopts = json.load(open(op.join("pkg", "arch", "dupeguru.json")))
@ -226,8 +233,7 @@ def main():
return return
print("Packaging dupeGuru with UI qt") print("Packaging dupeGuru with UI qt")
if sys.platform == "win32": if sys.platform == "win32":
package_debian() package_windows()
# package_windows()
elif sys.platform == "darwin": elif sys.platform == "darwin":
package_macos() package_macos()
else: else:

View File

@ -2,165 +2,178 @@
* Created On: 2010-01-31 * Created On: 2010-01-31
* Copyright 2014 Hardcoded Software (http://www.hardcoded.net) * Copyright 2014 Hardcoded Software (http://www.hardcoded.net)
* *
* This software is licensed under the "BSD" License as described in the * This software is licensed under the "BSD" License as described in the "LICENSE" file,
*"LICENSE" file, which should be included with this package. The terms are also * which should be included with this package. The terms are also available at
*available at http://www.hardcoded.net/licenses/bsd_license * http://www.hardcoded.net/licenses/bsd_license
**/ **/
#define PY_SSIZE_T_CLEAN #define PY_SSIZE_T_CLEAN
#include "Python.h" #include "Python.h"
/* It seems like MS VC defines min/max already */ /* It seems like MS VC defines min/max already */
#ifndef _MSC_VER #ifndef _MSC_VER
static int max(int a, int b) { return b > a ? b : a; } static int
max(int a, int b)
static int min(int a, int b) { return b < a ? b : a; } {
#endif return b > a ? b : a;
static PyObject *getblock(PyObject *image, int width, int height) {
int pixel_count, red, green, blue, bytes_per_line;
PyObject *pred, *pgreen, *pblue;
PyObject *result;
red = green = blue = 0;
pixel_count = width * height;
if (pixel_count) {
PyObject *sipptr, *bits_capsule, *pi;
char *s;
int i;
pi = PyObject_CallMethod(image, "bytesPerLine", NULL);
bytes_per_line = PyLong_AsLong(pi);
Py_DECREF(pi);
sipptr = PyObject_CallMethod(image, "bits", NULL);
bits_capsule = PyObject_CallMethod(sipptr, "ascapsule", NULL);
Py_DECREF(sipptr);
s = (char *)PyCapsule_GetPointer(bits_capsule, NULL);
Py_DECREF(bits_capsule);
/* Qt aligns all its lines on 32bit, which means that if the number of bytes
*per line for image is not divisible by 4, there's going to be crap
*inserted in "s" We have to take this into account when calculating offsets
**/
for (i = 0; i < height; i++) {
int j;
for (j = 0; j < width; j++) {
int offset;
unsigned char r, g, b;
offset = i * bytes_per_line + j * 3;
r = s[offset];
g = s[offset + 1];
b = s[offset + 2];
red += r;
green += g;
blue += b;
}
}
red /= pixel_count;
green /= pixel_count;
blue /= pixel_count;
}
pred = PyLong_FromLong(red);
pgreen = PyLong_FromLong(green);
pblue = PyLong_FromLong(blue);
result = PyTuple_Pack(3, pred, pgreen, pblue);
Py_DECREF(pred);
Py_DECREF(pgreen);
Py_DECREF(pblue);
return result;
} }
/* block_getblocks(QImage image, int block_count_per_side) -> [(int r, int g, static int
*int b), ...] min(int a, int b)
* {
* Compute blocks out of `image`. Note the use of min/max when compes the time return b < a ? b : a;
*of computing widths and heights and positions. This is to cover the case where }
*the width or height of the image is smaller than `block_count_per_side`. In #endif
*these cases, blocks will be, of course, 1 pixel big. But also, because all
*compared block lists are required to be of the same size, any block that has
* no pixel to be assigned to will simply be assigned the last pixel. This is
*why we have min(..., height-block_height-1) and stuff like that.
**/
static PyObject *block_getblocks(PyObject *self, PyObject *args) {
int block_count_per_side, width, height, block_width, block_height, ih;
PyObject *image;
PyObject *pi;
PyObject *result;
if (!PyArg_ParseTuple(args, "Oi", &image, &block_count_per_side)) { static PyObject*
return NULL; getblock(PyObject *image, int width, int height)
} {
int pixel_count, red, green, blue, bytes_per_line;
pi = PyObject_CallMethod(image, "width", NULL); PyObject *pred, *pgreen, *pblue;
width = PyLong_AsLong(pi); PyObject *result;
Py_DECREF(pi);
pi = PyObject_CallMethod(image, "height", NULL); red = green = blue = 0;
height = PyLong_AsLong(pi); pixel_count = width * height;
Py_DECREF(pi); if (pixel_count) {
PyObject *sipptr, *bits_capsule, *pi;
if (!(width && height)) { char *s;
return PyList_New(0); int i;
}
pi = PyObject_CallMethod(image, "bytesPerLine", NULL);
block_width = max(width / block_count_per_side, 1); bytes_per_line = PyLong_AsLong(pi);
block_height = max(height / block_count_per_side, 1); Py_DECREF(pi);
result = PyList_New((Py_ssize_t)block_count_per_side * block_count_per_side); sipptr = PyObject_CallMethod(image, "bits", NULL);
if (result == NULL) { bits_capsule = PyObject_CallMethod(sipptr, "ascapsule", NULL);
return NULL; Py_DECREF(sipptr);
} s = (char *)PyCapsule_GetPointer(bits_capsule, NULL);
Py_DECREF(bits_capsule);
for (ih = 0; ih < block_count_per_side; ih++) { /* Qt aligns all its lines on 32bit, which means that if the number of bytes per
int top, iw; * line for image is not divisible by 4, there's going to be crap inserted in "s"
top = min(ih * block_height, height - block_height - 1); * We have to take this into account when calculating offsets
for (iw = 0; iw < block_count_per_side; iw++) { **/
int left; for (i=0; i<height; i++) {
PyObject *pcrop; int j;
PyObject *pblock; for (j=0; j<width; j++) {
int offset;
left = min(iw * block_width, width - block_width - 1); unsigned char r, g, b;
pcrop = PyObject_CallMethod(image, "copy", "iiii", left, top, block_width,
block_height); offset = i * bytes_per_line + j * 3;
if (pcrop == NULL) { r = s[offset];
Py_DECREF(result); g = s[offset + 1];
return NULL; b = s[offset + 2];
} red += r;
pblock = getblock(pcrop, block_width, block_height); green += g;
Py_DECREF(pcrop); blue += b;
if (pblock == NULL) { }
Py_DECREF(result); }
return NULL;
} red /= pixel_count;
PyList_SET_ITEM(result, ih * block_count_per_side + iw, pblock); green /= pixel_count;
blue /= pixel_count;
} }
}
pred = PyLong_FromLong(red);
pgreen = PyLong_FromLong(green);
pblue = PyLong_FromLong(blue);
result = PyTuple_Pack(3, pred, pgreen, pblue);
Py_DECREF(pred);
Py_DECREF(pgreen);
Py_DECREF(pblue);
return result;
}
return result; /* block_getblocks(QImage image, int block_count_per_side) -> [(int r, int g, int b), ...]
*
* Compute blocks out of `image`. Note the use of min/max when compes the time of computing widths
* and heights and positions. This is to cover the case where the width or height of the image is
* smaller than `block_count_per_side`. In these cases, blocks will be, of course, 1 pixel big. But
* also, because all compared block lists are required to be of the same size, any block that has
* no pixel to be assigned to will simply be assigned the last pixel. This is why we have
* min(..., height-block_height-1) and stuff like that.
**/
static PyObject*
block_getblocks(PyObject *self, PyObject *args)
{
int block_count_per_side, width, height, block_width, block_height, ih;
PyObject *image;
PyObject *pi;
PyObject *result;
if (!PyArg_ParseTuple(args, "Oi", &image, &block_count_per_side)) {
return NULL;
}
pi = PyObject_CallMethod(image, "width", NULL);
width = PyLong_AsLong(pi);
Py_DECREF(pi);
pi = PyObject_CallMethod(image, "height", NULL);
height = PyLong_AsLong(pi);
Py_DECREF(pi);
if (!(width && height)) {
return PyList_New(0);
}
block_width = max(width / block_count_per_side, 1);
block_height = max(height / block_count_per_side, 1);
result = PyList_New(block_count_per_side * block_count_per_side);
if (result == NULL) {
return NULL;
}
for (ih=0; ih<block_count_per_side; ih++) {
int top, iw;
top = min(ih*block_height, height-block_height-1);
for (iw=0; iw<block_count_per_side; iw++) {
int left;
PyObject *pcrop;
PyObject *pblock;
left = min(iw*block_width, width-block_width-1);
pcrop = PyObject_CallMethod(image, "copy", "iiii", left, top, block_width, block_height);
if (pcrop == NULL) {
Py_DECREF(result);
return NULL;
}
pblock = getblock(pcrop, block_width, block_height);
Py_DECREF(pcrop);
if (pblock == NULL) {
Py_DECREF(result);
return NULL;
}
PyList_SET_ITEM(result, ih*block_count_per_side+iw, pblock);
}
}
return result;
} }
static PyMethodDef BlockMethods[] = { static PyMethodDef BlockMethods[] = {
{"getblocks", block_getblocks, METH_VARARGS, ""}, {"getblocks", block_getblocks, METH_VARARGS, ""},
{NULL, NULL, 0, NULL} /* Sentinel */ {NULL, NULL, 0, NULL} /* Sentinel */
}; };
static struct PyModuleDef BlockDef = {PyModuleDef_HEAD_INIT, static struct PyModuleDef BlockDef = {
"_block_qt", PyModuleDef_HEAD_INIT,
NULL, "_block_qt",
-1, NULL,
BlockMethods, -1,
NULL, BlockMethods,
NULL, NULL,
NULL, NULL,
NULL}; NULL,
NULL
};
PyObject *PyInit__block_qt(void) { PyObject *
PyObject *m = PyModule_Create(&BlockDef); PyInit__block_qt(void)
if (m == NULL) { {
return NULL; PyObject *m = PyModule_Create(&BlockDef);
} if (m == NULL) {
return m; return NULL;
}
return m;
} }

View File

@ -9,7 +9,6 @@
import traceback import traceback
import sys import sys
import os import os
import platform
from PyQt5.QtCore import Qt, QCoreApplication, QSize from PyQt5.QtCore import Qt, QCoreApplication, QSize
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
@ -35,9 +34,7 @@ class ErrorReportDialog(QDialog):
self._setupUi() self._setupUi()
name = QCoreApplication.applicationName() name = QCoreApplication.applicationName()
version = QCoreApplication.applicationVersion() version = QCoreApplication.applicationVersion()
error_text = "Application Name: {}\nVersion: {}\nPython: {}\nOperating System: {}\n\n{}".format( error_text = "Application Name: {}\nVersion: {}\n\n{}".format(name, version, error)
name, version, platform.python_version(), platform.platform(), error
)
# Under windows, we end up with an error report without linesep if we don't mangle it # Under windows, we end up with an error report without linesep if we don't mangle it
error_text = error_text.replace("\n", os.linesep) error_text = error_text.replace("\n", os.linesep)
self.errorTextEdit.setPlainText(error_text) self.errorTextEdit.setPlainText(error_text)

View File

@ -1,7 +1,7 @@
Send2Trash>=1.3.0 Send2Trash>=1.3.0
sphinx>=3.0.0 sphinx>=3.0.0
polib>=1.1.0 polib>=1.1.0
mutagen>=1.44.0 mutagen>=1.45.1
distro>=1.5.0 distro>=1.5.0
PyQt5 >=5.14.1,<6.0; sys_platform != 'linux' PyQt5 >=5.14.1,<6.0; sys_platform != 'linux'
pywin32>=228; sys_platform == 'win32' pywin32>=228; sys_platform == 'win32'

View File

@ -31,15 +31,12 @@ python_requires = >=3.6
install_requires = install_requires =
Send2Trash>=1.3.0 Send2Trash>=1.3.0
polib>=1.1.0 polib>=1.1.0
mutagen>=1.45.1 hsaudiotag3k>=1.1.3*
distro>=1.5.0 distro>=1.5.0
PyQt5 >=5.14.1,<6.0; sys_platform != 'linux' PyQt5 >=5.14.1,<6.0; sys_platform != 'linux'
pywin32>=228; sys_platform == 'win32' pywin32>=228; sys_platform == 'win32'
setup_requires =
sphinx>=3.0.0
tests_require = tests_require =
pytest >=6,<7 pytest >=6,<7
include_package_data = true
[options.entry_points] [options.entry_points]
console_scripts = console_scripts =

View File

@ -3,7 +3,7 @@ from pathlib import Path
exts = [ exts = [
Extension( Extension(
"core.pe._block", "_block",
[ [
str(Path("core", "pe", "modules", "block.c")), str(Path("core", "pe", "modules", "block.c")),
str(Path("core", "pe", "modules", "common.c")), str(Path("core", "pe", "modules", "common.c")),
@ -11,16 +11,19 @@ exts = [
include_dirs=[str(Path("core", "pe", "modules"))], include_dirs=[str(Path("core", "pe", "modules"))],
), ),
Extension( Extension(
"core.pe._cache", "_cache",
[ [
str(Path("core", "pe", "modules", "cache.c")), str(Path("core", "pe", "modules", "cache.c")),
str(Path("core", "pe", "modules", "common.c")), str(Path("core", "pe", "modules", "common.c")),
], ],
include_dirs=[str(Path("core", "pe", "modules"))], include_dirs=[str(Path("core", "pe", "modules"))],
), ),
Extension("qt.pe._block_qt", [str(Path("qt", "pe", "modules", "block.c"))]), Extension("_block_qt", [str(Path("qt", "pe", "modules", "block.c"))]),
] ]
headers = [str(Path("core", "pe", "modules", "common.h"))] headers = [str(Path("core", "pe", "modules", "common.h"))]
setup(ext_modules=exts, headers=headers) setup(
ext_modules=exts,
headers=headers,
)