mirror of
https://github.com/arsenetar/dupeguru.git
synced 2025-05-07 17:29:50 +00:00
Remove Cocoa specific and other unused code
This commit is contained in:
parent
ebb81d9f03
commit
143147cb8e
7
build.py
7
build.py
@ -109,10 +109,7 @@ def build_updatepot():
|
|||||||
print("Building columns.pot")
|
print("Building columns.pot")
|
||||||
loc.generate_pot(["core"], Path("locale", "columns.pot"), ["coltr"])
|
loc.generate_pot(["core"], Path("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
|
loc.generate_pot(["qt"], Path("locale", "ui.pot"), ["tr"], merge=True)
|
||||||
# We want to merge the generated pot with the old pot in the most preserving way possible.
|
|
||||||
ui_packages = ["qt", Path("cocoa", "inter")]
|
|
||||||
loc.generate_pot(ui_packages, Path("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"], Path("qtlib", "locale", "qtlib.pot"), ["tr"])
|
||||||
|
|
||||||
@ -121,13 +118,11 @@ 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(Path("qtlib", "locale"))
|
||||||
# loc.merge_pots_into_pos(Path("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(Path("qtlib", "locale"))
|
||||||
# loc.normalize_all_pos(Path("cocoalib", "locale"))
|
|
||||||
|
|
||||||
|
|
||||||
def build_pe_modules():
|
def build_pe_modules():
|
||||||
|
@ -20,13 +20,8 @@ import re
|
|||||||
import importlib
|
import importlib
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import glob
|
import glob
|
||||||
import sysconfig
|
|
||||||
import modulefinder
|
|
||||||
|
|
||||||
from setuptools import setup, Extension
|
|
||||||
|
|
||||||
from .plat import ISWINDOWS
|
from .plat import ISWINDOWS
|
||||||
from .util import ensure_folder, delete_files_with_pattern
|
|
||||||
|
|
||||||
|
|
||||||
def print_and_do(cmd):
|
def print_and_do(cmd):
|
||||||
@ -181,23 +176,6 @@ def build_dmg(app_path, destfolder):
|
|||||||
print("Build Complete")
|
print("Build Complete")
|
||||||
|
|
||||||
|
|
||||||
def copy_sysconfig_files_for_embed(destpath):
|
|
||||||
# This normally shouldn't be needed for Python 3.3+.
|
|
||||||
makefile = sysconfig.get_makefile_filename()
|
|
||||||
configh = sysconfig.get_config_h_filename()
|
|
||||||
shutil.copy(makefile, destpath)
|
|
||||||
shutil.copy(configh, destpath)
|
|
||||||
with open(op.join(destpath, "site.py"), "w") as fp:
|
|
||||||
fp.write(
|
|
||||||
"""
|
|
||||||
import os.path as op
|
|
||||||
from distutils import sysconfig
|
|
||||||
sysconfig.get_makefile_filename = lambda: op.join(op.dirname(__file__), 'Makefile')
|
|
||||||
sysconfig.get_config_h_filename = lambda: op.join(op.dirname(__file__), 'pyconfig.h')
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def add_to_pythonpath(path):
|
def add_to_pythonpath(path):
|
||||||
"""Adds ``path`` to both ``PYTHONPATH`` env and ``sys.path``."""
|
"""Adds ``path`` to both ``PYTHONPATH`` env and ``sys.path``."""
|
||||||
abspath = op.abspath(path)
|
abspath = op.abspath(path)
|
||||||
@ -248,20 +226,6 @@ def copy_packages(packages_names, dest, create_links=False, extra_ignores=None):
|
|||||||
shutil.copy(source_path, dest_path)
|
shutil.copy(source_path, dest_path)
|
||||||
|
|
||||||
|
|
||||||
def copy_qt_plugins(folder_names, dest): # This is only for Windows
|
|
||||||
from PyQt5.QtCore import QLibraryInfo
|
|
||||||
|
|
||||||
qt_plugin_dir = QLibraryInfo.location(QLibraryInfo.PluginsPath)
|
|
||||||
|
|
||||||
def ignore(path, names):
|
|
||||||
if path == qt_plugin_dir:
|
|
||||||
return [n for n in names if n not in folder_names]
|
|
||||||
else:
|
|
||||||
return [n for n in names if not n.endswith(".dll")]
|
|
||||||
|
|
||||||
shutil.copytree(qt_plugin_dir, dest, ignore=ignore)
|
|
||||||
|
|
||||||
|
|
||||||
def build_debian_changelog(
|
def build_debian_changelog(
|
||||||
changelogpath,
|
changelogpath,
|
||||||
destfile,
|
destfile,
|
||||||
@ -349,183 +313,6 @@ def read_changelog_file(filename):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
class OSXAppStructure:
|
|
||||||
def __init__(self, dest):
|
|
||||||
self.dest = dest
|
|
||||||
self.contents = op.join(dest, "Contents")
|
|
||||||
self.macos = op.join(self.contents, "MacOS")
|
|
||||||
self.resources = op.join(self.contents, "Resources")
|
|
||||||
self.frameworks = op.join(self.contents, "Frameworks")
|
|
||||||
self.infoplist = op.join(self.contents, "Info.plist")
|
|
||||||
|
|
||||||
def create(self, infoplist):
|
|
||||||
ensure_empty_folder(self.dest)
|
|
||||||
os.makedirs(self.macos)
|
|
||||||
os.mkdir(self.resources)
|
|
||||||
os.mkdir(self.frameworks)
|
|
||||||
copy(infoplist, self.infoplist)
|
|
||||||
open(op.join(self.contents, "PkgInfo"), "wt").write("APPLxxxx")
|
|
||||||
|
|
||||||
def copy_executable(self, executable):
|
|
||||||
info = plistlib.readPlist(self.infoplist)
|
|
||||||
self.executablename = info["CFBundleExecutable"]
|
|
||||||
self.executablepath = op.join(self.macos, self.executablename)
|
|
||||||
copy(executable, self.executablepath)
|
|
||||||
|
|
||||||
def copy_resources(self, *resources, use_symlinks=False):
|
|
||||||
for path in resources:
|
|
||||||
resource_dest = op.join(self.resources, op.basename(path))
|
|
||||||
action = symlink if use_symlinks else copy
|
|
||||||
action(op.abspath(path), resource_dest)
|
|
||||||
|
|
||||||
def copy_frameworks(self, *frameworks):
|
|
||||||
for path in frameworks:
|
|
||||||
framework_dest = op.join(self.frameworks, op.basename(path))
|
|
||||||
copy(path, framework_dest)
|
|
||||||
|
|
||||||
|
|
||||||
def create_osx_app_structure(
|
|
||||||
dest,
|
|
||||||
executable,
|
|
||||||
infoplist,
|
|
||||||
resources=None,
|
|
||||||
frameworks=None,
|
|
||||||
symlink_resources=False,
|
|
||||||
):
|
|
||||||
# `dest`: A path to the destination .app folder
|
|
||||||
# `executable`: the path of the executable file that goes in "MacOS"
|
|
||||||
# `infoplist`: The path to your Info.plist file.
|
|
||||||
# `resources`: A list of paths of files or folders going in the "Resources" folder.
|
|
||||||
# `frameworks`: Same as above for "Frameworks".
|
|
||||||
# `symlink_resources`: If True, will symlink resources into the structure instead of copying them.
|
|
||||||
app = OSXAppStructure(dest)
|
|
||||||
app.create(infoplist)
|
|
||||||
app.copy_executable(executable)
|
|
||||||
app.copy_resources(*resources, use_symlinks=symlink_resources)
|
|
||||||
app.copy_frameworks(*frameworks)
|
|
||||||
|
|
||||||
|
|
||||||
class OSXFrameworkStructure:
|
|
||||||
def __init__(self, dest):
|
|
||||||
self.dest = dest
|
|
||||||
self.contents = op.join(dest, "Versions", "A")
|
|
||||||
self.resources = op.join(self.contents, "Resources")
|
|
||||||
self.headers = op.join(self.contents, "Headers")
|
|
||||||
self.infoplist = op.join(self.resources, "Info.plist")
|
|
||||||
self._update_executable_path()
|
|
||||||
|
|
||||||
def _update_executable_path(self):
|
|
||||||
if not op.exists(self.infoplist):
|
|
||||||
self.executablename = self.executablepath = None
|
|
||||||
return
|
|
||||||
info = plistlib.readPlist(self.infoplist)
|
|
||||||
self.executablename = info["CFBundleExecutable"]
|
|
||||||
self.executablepath = op.join(self.contents, self.executablename)
|
|
||||||
|
|
||||||
def create(self, infoplist):
|
|
||||||
ensure_empty_folder(self.dest)
|
|
||||||
os.makedirs(self.contents)
|
|
||||||
os.mkdir(self.resources)
|
|
||||||
os.mkdir(self.headers)
|
|
||||||
copy(infoplist, self.infoplist)
|
|
||||||
self._update_executable_path()
|
|
||||||
|
|
||||||
def create_symlinks(self):
|
|
||||||
# Only call this after create() and copy_executable()
|
|
||||||
os.symlink("A", op.join(self.dest, "Versions", "Current"))
|
|
||||||
os.symlink(op.relpath(self.executablepath, self.dest), op.join(self.dest, self.executablename))
|
|
||||||
os.symlink(op.relpath(self.headers, self.dest), op.join(self.dest, "Headers"))
|
|
||||||
os.symlink(op.relpath(self.resources, self.dest), op.join(self.dest, "Resources"))
|
|
||||||
|
|
||||||
def copy_executable(self, executable):
|
|
||||||
copy(executable, self.executablepath)
|
|
||||||
|
|
||||||
def copy_resources(self, *resources, use_symlinks=False):
|
|
||||||
for path in resources:
|
|
||||||
resource_dest = op.join(self.resources, op.basename(path))
|
|
||||||
action = symlink if use_symlinks else copy
|
|
||||||
action(op.abspath(path), resource_dest)
|
|
||||||
|
|
||||||
def copy_headers(self, *headers, use_symlinks=False):
|
|
||||||
for path in headers:
|
|
||||||
header_dest = op.join(self.headers, op.basename(path))
|
|
||||||
action = symlink if use_symlinks else copy
|
|
||||||
action(op.abspath(path), header_dest)
|
|
||||||
|
|
||||||
|
|
||||||
def copy_embeddable_python_dylib(dst):
|
|
||||||
runtime = op.join(
|
|
||||||
sysconfig.get_config_var("PYTHONFRAMEWORKPREFIX"),
|
|
||||||
sysconfig.get_config_var("LDLIBRARY"),
|
|
||||||
)
|
|
||||||
filedest = op.join(dst, "Python")
|
|
||||||
shutil.copy(runtime, filedest)
|
|
||||||
os.chmod(filedest, 0o774) # We need write permission to use install_name_tool
|
|
||||||
cmd = "install_name_tool -id @rpath/Python %s" % filedest
|
|
||||||
print_and_do(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
def collect_stdlib_dependencies(script, dest_folder, extra_deps=None):
|
|
||||||
sysprefix = sys.prefix # could be a virtualenv
|
|
||||||
basesysprefix = sys.base_prefix # seems to be path to non-virtual sys
|
|
||||||
real_lib_prefix = sysconfig.get_config_var("LIBDEST") # leaving this in case it is neede
|
|
||||||
|
|
||||||
def is_stdlib_path(path):
|
|
||||||
# A module path is only a stdlib path if it's in either sys.prefix or
|
|
||||||
# sysconfig.get_config_var('prefix') (the 2 are different if we are in a virtualenv) and if
|
|
||||||
# there's no "site-package in the path.
|
|
||||||
if not path:
|
|
||||||
return False
|
|
||||||
if "site-package" in path:
|
|
||||||
return False
|
|
||||||
if not (path.startswith(sysprefix) or path.startswith(basesysprefix) or path.startswith(real_lib_prefix)):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
ensure_folder(dest_folder)
|
|
||||||
mf = modulefinder.ModuleFinder()
|
|
||||||
mf.run_script(script)
|
|
||||||
modpaths = [mod.__file__ for mod in mf.modules.values()]
|
|
||||||
modpaths = filter(is_stdlib_path, modpaths)
|
|
||||||
for p in modpaths:
|
|
||||||
if p.startswith(real_lib_prefix):
|
|
||||||
relpath = op.relpath(p, real_lib_prefix)
|
|
||||||
elif p.startswith(sysprefix):
|
|
||||||
relpath = op.relpath(p, sysprefix)
|
|
||||||
assert relpath.startswith("lib/python3.") # we want to get rid of that lib/python3.x part
|
|
||||||
relpath = relpath[len("lib/python3.X/") :]
|
|
||||||
elif p.startswith(basesysprefix):
|
|
||||||
relpath = op.relpath(p, basesysprefix)
|
|
||||||
assert relpath.startswith("lib/python3.")
|
|
||||||
relpath = relpath[len("lib/python3.X/") :]
|
|
||||||
else:
|
|
||||||
raise AssertionError()
|
|
||||||
if relpath.startswith("lib-dynload"): # We copy .so files in lib-dynload directly in our dest
|
|
||||||
relpath = relpath[len("lib-dynload/") :]
|
|
||||||
if relpath.startswith("encodings") or relpath.startswith("distutils"):
|
|
||||||
# We force their inclusion later.
|
|
||||||
continue
|
|
||||||
dest_path = op.join(dest_folder, relpath)
|
|
||||||
ensure_folder(op.dirname(dest_path))
|
|
||||||
copy(p, dest_path)
|
|
||||||
# stringprep is used by encodings.
|
|
||||||
# We use real_lib_prefix with distutils because virtualenv messes with it and we need to refer
|
|
||||||
# to the original distutils folder.
|
|
||||||
FORCED_INCLUSION = [
|
|
||||||
"encodings",
|
|
||||||
"stringprep",
|
|
||||||
op.join(real_lib_prefix, "distutils"),
|
|
||||||
]
|
|
||||||
if extra_deps:
|
|
||||||
FORCED_INCLUSION += extra_deps
|
|
||||||
copy_packages(FORCED_INCLUSION, dest_folder)
|
|
||||||
# There's a couple of rather big exe files in the distutils folder that we absolutely don't
|
|
||||||
# need. Remove them.
|
|
||||||
delete_files_with_pattern(op.join(dest_folder, "distutils"), "*.exe")
|
|
||||||
# And, finally, create an empty "site.py" that Python needs around on startup.
|
|
||||||
open(op.join(dest_folder, "site.py"), "w").close()
|
|
||||||
|
|
||||||
|
|
||||||
def fix_qt_resource_file(path):
|
def fix_qt_resource_file(path):
|
||||||
# pyrcc5 under Windows, if the locale is non-english, can produce a source file with a date
|
# pyrcc5 under Windows, if the locale is non-english, can produce a source file with a date
|
||||||
# containing accented characters. If it does, the encoding is wrong and it prevents the file
|
# containing accented characters. If it does, the encoding is wrong and it prevents the file
|
||||||
@ -537,21 +324,3 @@ def fix_qt_resource_file(path):
|
|||||||
lines = [line for line in lines if not line.startswith(b"#")]
|
lines = [line for line in lines if not line.startswith(b"#")]
|
||||||
with open(path, "wb") as fp:
|
with open(path, "wb") as fp:
|
||||||
fp.write(b"\n".join(lines))
|
fp.write(b"\n".join(lines))
|
||||||
|
|
||||||
|
|
||||||
def build_cocoa_ext(extname, dest, source_files, extra_frameworks=(), extra_includes=()):
|
|
||||||
extra_link_args = ["-framework", "CoreFoundation", "-framework", "Foundation"]
|
|
||||||
for extra in extra_frameworks:
|
|
||||||
extra_link_args += ["-framework", extra]
|
|
||||||
ext = Extension(
|
|
||||||
extname,
|
|
||||||
source_files,
|
|
||||||
extra_link_args=extra_link_args,
|
|
||||||
include_dirs=extra_includes,
|
|
||||||
)
|
|
||||||
setup(script_args=["build_ext", "--inplace"], ext_modules=[ext])
|
|
||||||
# Our problem here is to get the fully qualified filename of the resulting .so but I couldn't
|
|
||||||
# find a documented way to do so. The only thing I could find is this below :(
|
|
||||||
fn = ext._file_name
|
|
||||||
assert op.exists(fn)
|
|
||||||
move(fn, op.join(dest, fn))
|
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
# Created By: Virgil Dupras
|
|
||||||
# Created On: 2011-04-19
|
|
||||||
# Copyright 2015 Hardcoded Software (http://www.hardcoded.net)
|
|
||||||
#
|
|
||||||
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
|
|
||||||
# which should be included with this package. The terms are also available at
|
|
||||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
|
|
||||||
# Taken from http://bzimmer.ziclix.com/2008/12/17/python-thread-dumps/
|
|
||||||
def stacktraces():
|
|
||||||
code = []
|
|
||||||
for thread_id, stack in sys._current_frames().items():
|
|
||||||
code.append("\n# ThreadID: %s" % thread_id)
|
|
||||||
for filename, lineno, name, line in traceback.extract_stack(stack):
|
|
||||||
code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
|
|
||||||
if line:
|
|
||||||
code.append(" %s" % (line.strip()))
|
|
||||||
|
|
||||||
return "\n".join(code)
|
|
@ -42,29 +42,6 @@ def special_folder_path(special_folder, appname=None, portable=False):
|
|||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Normally, we would simply do "from cocoa import proxy", but due to a bug in pytest (currently
|
|
||||||
# at v2.4.2), our test suite is broken when we do that. This below is a workaround until that
|
|
||||||
# bug is fixed.
|
|
||||||
import cocoa
|
|
||||||
|
|
||||||
if not hasattr(cocoa, "proxy"):
|
|
||||||
raise ImportError()
|
|
||||||
proxy = cocoa.proxy
|
|
||||||
_open_url = proxy.openURL_
|
|
||||||
_open_path = proxy.openPath_
|
|
||||||
_reveal_path = proxy.revealPath_
|
|
||||||
|
|
||||||
def _special_folder_path(special_folder, appname=None, portable=False):
|
|
||||||
if special_folder == SpecialFolder.CACHE:
|
|
||||||
base = proxy.getCachePath()
|
|
||||||
else:
|
|
||||||
base = proxy.getAppdataPath()
|
|
||||||
if not appname:
|
|
||||||
appname = proxy.bundleInfo_("CFBundleName")
|
|
||||||
return op.join(base, appname)
|
|
||||||
|
|
||||||
except ImportError:
|
|
||||||
try:
|
|
||||||
from PyQt5.QtCore import QUrl, QStandardPaths
|
from PyQt5.QtCore import QUrl, QStandardPaths
|
||||||
from PyQt5.QtGui import QDesktopServices
|
from PyQt5.QtGui import QDesktopServices
|
||||||
from qtlib.util import get_appdata
|
from qtlib.util import get_appdata
|
||||||
@ -97,7 +74,7 @@ except ImportError:
|
|||||||
folder = get_appdata(portable)
|
folder = get_appdata(portable)
|
||||||
return folder
|
return folder
|
||||||
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# We're either running tests, and these functions don't matter much or we're in a really
|
# We're either running tests, and these functions don't matter much or we're in a really
|
||||||
# weird situation. Let's just have dummy fallbacks.
|
# weird situation. Let's just have dummy fallbacks.
|
||||||
logging.warning("Can't setup desktop functions!")
|
logging.warning("Can't setup desktop functions!")
|
||||||
|
@ -1,216 +0,0 @@
|
|||||||
# Created By: Virgil Dupras
|
|
||||||
# Created On: 2011-08-05
|
|
||||||
# Copyright 2015 Hardcoded Software (http://www.hardcoded.net)
|
|
||||||
#
|
|
||||||
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
|
|
||||||
# which should be included with this package. The terms are also available at
|
|
||||||
# http://www.gnu.org/licenses/gpl-3.0.html
|
|
||||||
|
|
||||||
from sys import maxsize as INF
|
|
||||||
from math import sqrt
|
|
||||||
|
|
||||||
VERY_SMALL = 0.0000001
|
|
||||||
|
|
||||||
|
|
||||||
class Point:
|
|
||||||
def __init__(self, x, y):
|
|
||||||
self.x = x
|
|
||||||
self.y = y
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<Point {:2.2f}, {:2.2f}>".format(*self)
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
yield self.x
|
|
||||||
yield self.y
|
|
||||||
|
|
||||||
def distance_to(self, other):
|
|
||||||
return Line(self, other).length()
|
|
||||||
|
|
||||||
|
|
||||||
class Line:
|
|
||||||
def __init__(self, p1, p2):
|
|
||||||
self.p1 = p1
|
|
||||||
self.p2 = p2
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<Line {}, {}>".format(*self)
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
yield self.p1
|
|
||||||
yield self.p2
|
|
||||||
|
|
||||||
def dx(self):
|
|
||||||
return self.p2.x - self.p1.x
|
|
||||||
|
|
||||||
def dy(self):
|
|
||||||
return self.p2.y - self.p1.y
|
|
||||||
|
|
||||||
def length(self):
|
|
||||||
return sqrt(self.dx() ** 2 + self.dy() ** 2)
|
|
||||||
|
|
||||||
def slope(self):
|
|
||||||
if self.dx() == 0:
|
|
||||||
return INF if self.dy() > 0 else -INF
|
|
||||||
else:
|
|
||||||
return self.dy() / self.dx()
|
|
||||||
|
|
||||||
def intersection_point(self, other):
|
|
||||||
# with help from http://paulbourke.net/geometry/lineline2d/
|
|
||||||
if abs(self.slope() - other.slope()) < VERY_SMALL:
|
|
||||||
# parallel. Even if coincident, we return nothing
|
|
||||||
return None
|
|
||||||
|
|
||||||
A, B = self
|
|
||||||
C, D = other
|
|
||||||
|
|
||||||
denom = (D.y - C.y) * (B.x - A.x) - (D.x - C.x) * (B.y - A.y)
|
|
||||||
if denom == 0:
|
|
||||||
return None
|
|
||||||
numera = (D.x - C.x) * (A.y - C.y) - (D.y - C.y) * (A.x - C.x)
|
|
||||||
numerb = (B.x - A.x) * (A.y - C.y) - (B.y - A.y) * (A.x - C.x)
|
|
||||||
|
|
||||||
mua = numera / denom
|
|
||||||
mub = numerb / denom
|
|
||||||
if (0 <= mua <= 1) and (0 <= mub <= 1):
|
|
||||||
x = A.x + mua * (B.x - A.x)
|
|
||||||
y = A.y + mua * (B.y - A.y)
|
|
||||||
return Point(x, y)
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class Rect:
|
|
||||||
def __init__(self, x, y, w, h):
|
|
||||||
self.x = x
|
|
||||||
self.y = y
|
|
||||||
self.w = w
|
|
||||||
self.h = h
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
yield self.x
|
|
||||||
yield self.y
|
|
||||||
yield self.w
|
|
||||||
yield self.h
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<Rect {:2.2f}, {:2.2f}, {:2.2f}, {:2.2f}>".format(*self)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_center(cls, center, width, height):
|
|
||||||
x = center.x - width / 2
|
|
||||||
y = center.y - height / 2
|
|
||||||
return cls(x, y, width, height)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_corners(cls, pt1, pt2):
|
|
||||||
x1, y1 = pt1
|
|
||||||
x2, y2 = pt2
|
|
||||||
return cls(min(x1, x2), min(y1, y2), abs(x1 - x2), abs(y1 - y2))
|
|
||||||
|
|
||||||
def center(self):
|
|
||||||
return Point(self.x + self.w / 2, self.y + self.h / 2)
|
|
||||||
|
|
||||||
def contains_point(self, point):
|
|
||||||
x, y = point
|
|
||||||
(x1, y1), (x2, y2) = self.corners()
|
|
||||||
return (x1 <= x <= x2) and (y1 <= y <= y2)
|
|
||||||
|
|
||||||
def contains_rect(self, rect):
|
|
||||||
pt1, pt2 = rect.corners()
|
|
||||||
return self.contains_point(pt1) and self.contains_point(pt2)
|
|
||||||
|
|
||||||
def corners(self):
|
|
||||||
return Point(self.x, self.y), Point(self.x + self.w, self.y + self.h)
|
|
||||||
|
|
||||||
def intersects(self, other):
|
|
||||||
r1pt1, r1pt2 = self.corners()
|
|
||||||
r2pt1, r2pt2 = other.corners()
|
|
||||||
if r1pt1.x < r2pt1.x:
|
|
||||||
xinter = r1pt2.x >= r2pt1.x
|
|
||||||
else:
|
|
||||||
xinter = r2pt2.x >= r1pt1.x
|
|
||||||
if not xinter:
|
|
||||||
return False
|
|
||||||
if r1pt1.y < r2pt1.y:
|
|
||||||
yinter = r1pt2.y >= r2pt1.y
|
|
||||||
else:
|
|
||||||
yinter = r2pt2.y >= r1pt1.y
|
|
||||||
return yinter
|
|
||||||
|
|
||||||
def lines(self):
|
|
||||||
pt1, pt4 = self.corners()
|
|
||||||
pt2 = Point(pt4.x, pt1.y)
|
|
||||||
pt3 = Point(pt1.x, pt4.y)
|
|
||||||
l1 = Line(pt1, pt2)
|
|
||||||
l2 = Line(pt2, pt4)
|
|
||||||
l3 = Line(pt3, pt4)
|
|
||||||
l4 = Line(pt1, pt3)
|
|
||||||
return l1, l2, l3, l4
|
|
||||||
|
|
||||||
def scaled_rect(self, dx, dy):
|
|
||||||
"""Returns a rect that has the same borders at self, but grown/shrunk by dx/dy on each side."""
|
|
||||||
x, y, w, h = self
|
|
||||||
x -= dx
|
|
||||||
y -= dy
|
|
||||||
w += dx * 2
|
|
||||||
h += dy * 2
|
|
||||||
return Rect(x, y, w, h)
|
|
||||||
|
|
||||||
def united(self, other):
|
|
||||||
"""Returns the bounding rectangle of this rectangle and `other`."""
|
|
||||||
# ul=upper left lr=lower right
|
|
||||||
ulcorner1, lrcorner1 = self.corners()
|
|
||||||
ulcorner2, lrcorner2 = other.corners()
|
|
||||||
corner1 = Point(min(ulcorner1.x, ulcorner2.x), min(ulcorner1.y, ulcorner2.y))
|
|
||||||
corner2 = Point(max(lrcorner1.x, lrcorner2.x), max(lrcorner1.y, lrcorner2.y))
|
|
||||||
return Rect.from_corners(corner1, corner2)
|
|
||||||
|
|
||||||
# --- Properties
|
|
||||||
@property
|
|
||||||
def top(self):
|
|
||||||
return self.y
|
|
||||||
|
|
||||||
@top.setter
|
|
||||||
def top(self, value):
|
|
||||||
self.y = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def bottom(self):
|
|
||||||
return self.y + self.h
|
|
||||||
|
|
||||||
@bottom.setter
|
|
||||||
def bottom(self, value):
|
|
||||||
self.y = value - self.h
|
|
||||||
|
|
||||||
@property
|
|
||||||
def left(self):
|
|
||||||
return self.x
|
|
||||||
|
|
||||||
@left.setter
|
|
||||||
def left(self, value):
|
|
||||||
self.x = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def right(self):
|
|
||||||
return self.x + self.w
|
|
||||||
|
|
||||||
@right.setter
|
|
||||||
def right(self, value):
|
|
||||||
self.x = value - self.w
|
|
||||||
|
|
||||||
@property
|
|
||||||
def width(self):
|
|
||||||
return self.w
|
|
||||||
|
|
||||||
@width.setter
|
|
||||||
def width(self, value):
|
|
||||||
self.w = value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def height(self):
|
|
||||||
return self.h
|
|
||||||
|
|
||||||
@height.setter
|
|
||||||
def height(self, value):
|
|
||||||
self.h = value
|
|
118
hscommon/loc.py
118
hscommon/loc.py
@ -1,14 +1,11 @@
|
|||||||
import os
|
import os
|
||||||
import os.path as op
|
import os.path as op
|
||||||
import shutil
|
import shutil
|
||||||
import re
|
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
import polib
|
import polib
|
||||||
|
|
||||||
from . import pygettext
|
from . import pygettext
|
||||||
from .util import modified_after, dedupe, ensure_folder
|
|
||||||
from .build import print_and_do, ensure_empty_folder
|
|
||||||
|
|
||||||
LC_MESSAGES = "LC_MESSAGES"
|
LC_MESSAGES = "LC_MESSAGES"
|
||||||
|
|
||||||
@ -116,118 +113,3 @@ def normalize_all_pos(base_folder):
|
|||||||
for pofile in pofiles:
|
for pofile in pofiles:
|
||||||
p = polib.pofile(pofile)
|
p = polib.pofile(pofile)
|
||||||
p.save()
|
p.save()
|
||||||
|
|
||||||
|
|
||||||
# --- Cocoa
|
|
||||||
def all_lproj_paths(folder):
|
|
||||||
return files_with_ext(folder, ".lproj")
|
|
||||||
|
|
||||||
|
|
||||||
def escape_cocoa_strings(s):
|
|
||||||
return s.replace("\\", "\\\\").replace('"', '\\"').replace("\n", "\\n")
|
|
||||||
|
|
||||||
|
|
||||||
def unescape_cocoa_strings(s):
|
|
||||||
return s.replace("\\\\", "\\").replace('\\"', '"').replace("\\n", "\n")
|
|
||||||
|
|
||||||
|
|
||||||
def strings2pot(target, dest):
|
|
||||||
with open(target, "rt", encoding="utf-8") as fp:
|
|
||||||
contents = fp.read()
|
|
||||||
# We're reading an en.lproj file. We only care about the righthand part of the translation.
|
|
||||||
re_trans = re.compile(r'".*" = "(.*)";')
|
|
||||||
strings = re_trans.findall(contents)
|
|
||||||
if op.exists(dest):
|
|
||||||
po = polib.pofile(dest)
|
|
||||||
else:
|
|
||||||
po = polib.POFile()
|
|
||||||
for s in dedupe(strings):
|
|
||||||
s = unescape_cocoa_strings(s)
|
|
||||||
entry = po.find(s)
|
|
||||||
if entry is None:
|
|
||||||
entry = polib.POEntry(msgid=s)
|
|
||||||
po.append(entry)
|
|
||||||
# we don't know or care about a line number so we put 0
|
|
||||||
entry.occurrences.append((target, "0"))
|
|
||||||
entry.occurrences = dedupe(entry.occurrences)
|
|
||||||
po.save(dest)
|
|
||||||
|
|
||||||
|
|
||||||
def allstrings2pot(lprojpath, dest, excludes=None):
|
|
||||||
allstrings = files_with_ext(lprojpath, STRING_EXT)
|
|
||||||
if excludes:
|
|
||||||
allstrings = [p for p in allstrings if op.splitext(op.basename(p))[0] not in excludes]
|
|
||||||
for strings_path in allstrings:
|
|
||||||
strings2pot(strings_path, dest)
|
|
||||||
|
|
||||||
|
|
||||||
def po2strings(pofile, en_strings, dest):
|
|
||||||
# Takes en_strings and replace all righthand parts of "foo" = "bar"; entries with translations
|
|
||||||
# in pofile, then puts the result in dest.
|
|
||||||
po = polib.pofile(pofile)
|
|
||||||
if not modified_after(pofile, dest):
|
|
||||||
return
|
|
||||||
ensure_folder(op.dirname(dest))
|
|
||||||
print("Creating {} from {}".format(dest, pofile))
|
|
||||||
with open(en_strings, "rt", encoding="utf-8") as fp:
|
|
||||||
contents = fp.read()
|
|
||||||
re_trans = re.compile(r'(?<= = ").*(?=";\n)')
|
|
||||||
|
|
||||||
def repl(match):
|
|
||||||
s = match.group(0)
|
|
||||||
unescaped = unescape_cocoa_strings(s)
|
|
||||||
entry = po.find(unescaped)
|
|
||||||
if entry is None:
|
|
||||||
print("WARNING: Could not find entry '{}' in .po file".format(s))
|
|
||||||
return s
|
|
||||||
trans = entry.msgstr
|
|
||||||
return escape_cocoa_strings(trans) if trans else s
|
|
||||||
|
|
||||||
contents = re_trans.sub(repl, contents)
|
|
||||||
with open(dest, "wt", encoding="utf-8") as fp:
|
|
||||||
fp.write(contents)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_cocoa_strings_from_code(code_folder, dest_folder):
|
|
||||||
# Uses the "genstrings" command to generate strings file from all .m files in "code_folder".
|
|
||||||
# The strings file (their name depends on the localization table used in the source) will be
|
|
||||||
# placed in "dest_folder".
|
|
||||||
# genstrings produces utf-16 files with comments. After having generated the files, we convert
|
|
||||||
# them to utf-8 and remove the comments.
|
|
||||||
ensure_empty_folder(dest_folder)
|
|
||||||
print_and_do('genstrings -o "{}" `find "{}" -name *.m | xargs`'.format(dest_folder, code_folder))
|
|
||||||
for stringsfile in os.listdir(dest_folder):
|
|
||||||
stringspath = op.join(dest_folder, stringsfile)
|
|
||||||
with open(stringspath, "rt", encoding="utf-16") as fp:
|
|
||||||
content = fp.read()
|
|
||||||
content = re.sub(r"/\*.*?\*/", "", content)
|
|
||||||
content = re.sub(r"\n{2,}", "\n", content)
|
|
||||||
# I have no idea why, but genstrings seems to have problems with "%" character in strings
|
|
||||||
# and inserts (number)$ after it. Find these bogus inserts and remove them.
|
|
||||||
content = re.sub(r"%\d\$", "%", content)
|
|
||||||
with open(stringspath, "wt", encoding="utf-8") as fp:
|
|
||||||
fp.write(content)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_cocoa_strings_from_xib(xib_folder):
|
|
||||||
xibs = [op.join(xib_folder, fn) for fn in os.listdir(xib_folder) if fn.endswith(".xib")]
|
|
||||||
for xib in xibs:
|
|
||||||
dest = xib.replace(".xib", STRING_EXT)
|
|
||||||
print_and_do("ibtool {} --generate-strings-file {}".format(xib, dest))
|
|
||||||
print_and_do("iconv -f utf-16 -t utf-8 {0} | tee {0}".format(dest))
|
|
||||||
|
|
||||||
|
|
||||||
def localize_stringsfile(stringsfile, dest_root_folder):
|
|
||||||
stringsfile_name = op.basename(stringsfile)
|
|
||||||
for lang in get_langs("locale"):
|
|
||||||
pofile = op.join("locale", lang, "LC_MESSAGES", "ui.po")
|
|
||||||
cocoa_lang = PO2COCOA.get(lang, lang)
|
|
||||||
dest_lproj = op.join(dest_root_folder, cocoa_lang + ".lproj")
|
|
||||||
ensure_folder(dest_lproj)
|
|
||||||
po2strings(pofile, stringsfile, op.join(dest_lproj, stringsfile_name))
|
|
||||||
|
|
||||||
|
|
||||||
def localize_all_stringsfiles(src_folder, dest_root_folder):
|
|
||||||
stringsfiles = [op.join(src_folder, fn) for fn in os.listdir(src_folder) if fn.endswith(STRING_EXT)]
|
|
||||||
for path in stringsfiles:
|
|
||||||
localize_stringsfile(path, dest_root_folder)
|
|
||||||
|
@ -129,18 +129,6 @@ def install_gettext_trans(base_folder, lang):
|
|||||||
installed_lang = lang
|
installed_lang = lang
|
||||||
|
|
||||||
|
|
||||||
def install_gettext_trans_under_cocoa():
|
|
||||||
from cocoa import proxy
|
|
||||||
|
|
||||||
res_folder = proxy.getResourcePath()
|
|
||||||
base_folder = op.join(res_folder, "locale")
|
|
||||||
current_lang = proxy.systemLang()
|
|
||||||
install_gettext_trans(base_folder, current_lang)
|
|
||||||
localename = get_locale_name(current_lang)
|
|
||||||
if localename is not None:
|
|
||||||
locale.setlocale(locale.LC_ALL, localename)
|
|
||||||
|
|
||||||
|
|
||||||
def install_gettext_trans_under_qt(base_folder, lang=None):
|
def install_gettext_trans_under_qt(base_folder, lang=None):
|
||||||
# So, we install the gettext locale, great, but we also should try to install qt_*.qm if
|
# So, we install the gettext locale, great, but we also should try to install qt_*.qm if
|
||||||
# available so that strings that are inside Qt itself over which I have no control are in the
|
# available so that strings that are inside Qt itself over which I have no control are in the
|
||||||
|
Loading…
x
Reference in New Issue
Block a user