manuskript/manuskript/functions.py
2017-11-14 15:01:20 +01:00

356 lines
10 KiB
Python

#!/usr/bin/env python
#--!-- coding: utf8 --!--
import os
import re
from random import *
from PyQt5.QtCore import Qt, QRect, QStandardPaths, QObject, QRegExp, QDir
from PyQt5.QtGui import QBrush, QIcon, QPainter, QColor, QImage, QPixmap
from PyQt5.QtWidgets import qApp, QTextEdit
from manuskript.enums import Outline
# Used to detect multiple connections
AUC = Qt.AutoConnection | Qt.UniqueConnection
MW = None
def wordCount(text):
t = text.strip().replace(" ", "\n").split("\n")
t = [l for l in t if l]
return len(t)
def toInt(text):
if text:
try:
return int(text)
except ValueError:
pass
return 0
def toFloat(text):
if text:
return float(text)
else:
return 0.
def toString(text):
if text in [None, "None"]:
return ""
else:
return str(text)
def drawProgress(painter, rect, progress, radius=0):
from manuskript.ui import style as S
painter.setPen(Qt.NoPen)
painter.setBrush(QColor(S.base)) # "#dddddd"
painter.drawRoundedRect(rect, radius, radius)
painter.setBrush(QBrush(colorFromProgress(progress)))
r2 = QRect(rect)
r2.setWidth(r2.width() * min(progress, 1))
painter.drawRoundedRect(r2, radius, radius)
def colorFromProgress(progress):
progress = toFloat(progress)
c1 = QColor(Qt.red)
c2 = QColor(Qt.blue)
c3 = QColor(Qt.darkGreen)
c4 = QColor("#FFA500")
if progress < 0.3:
return c1
elif progress < 0.8:
return c2
elif progress > 1.2:
return c4
else:
return c3
def mainWindow():
global MW
if not MW:
for i in qApp.topLevelWidgets():
if i.objectName() == "MainWindow":
MW = i
return MW
return None
else:
return MW
def iconColor(icon):
"""Returns a QRgb from a QIcon, assuming its all the same color"""
px = icon.pixmap(5, 5)
if px.width() != 0:
return QColor(QImage(px).pixel(2, 2))
else:
return QColor(Qt.transparent)
def iconFromColor(color):
px = QPixmap(32, 32)
px.fill(color)
return QIcon(px)
def iconFromColorString(string):
return iconFromColor(QColor(string))
def themeIcon(name):
"Returns an icon for the given name."
db = {
"character": "stock_people",
"characters": "stock_people",
"plot": "stock_shuffle",
"plots": "stock_shuffle",
"world": "emblem-web", #stock_timezone applications-internet
"outline": "gtk-index", #applications-versioncontrol
"label": "folder_color_picker",
"status": "applications-development",
"text": "view-text",
"card": "view-card",
"outline": "view-outline",
"tree": "view-list-tree",
"spelling": "tools-check-spelling"
}
if name in db:
return QIcon.fromTheme(db[name])
else:
return QIcon()
def randomColor(mix=None):
"""Generates a random color. If mix (QColor) is given, mixes the random color and mix."""
r = randint(0, 255)
g = randint(0, 255)
b = randint(0, 255)
if mix:
r = (r + mix.red()) / 2
g = (g + mix.green()) / 2
b = (b + mix.blue()) / 2
return QColor(r, g, b)
def mixColors(col1, col2, f=.5):
fromString = False
if type(col1) == str:
fromString = True
col1 = QColor(col1)
if type(col2) == str:
col2 = QColor(col2)
f2 = 1-f
r = col1.red() * f + col2.red() * f2
g = col1.green() * f + col2.green() * f2
b = col1.blue() * f + col2.blue() * f2
return QColor(r, g, b) if not fromString else QColor(r, g, b).name()
def outlineItemColors(item):
from manuskript.ui import style as S
"""Takes an OutlineItem and returns a dict of colors."""
colors = {}
mw = mainWindow()
# POV
colors["POV"] = QColor(Qt.transparent)
POV = item.data(Outline.POV.value)
if POV == "":
col = QColor(Qt.transparent)
else:
for i in range(mw.mdlCharacter.rowCount()):
if mw.mdlCharacter.ID(i) == POV:
colors["POV"] = iconColor(mw.mdlCharacter.icon(i))
# Label
lbl = item.data(Outline.label.value)
if lbl == "":
col = QColor(Qt.transparent)
else:
col = iconColor(mw.mdlLabels.item(toInt(lbl)).icon())
# if col == Qt.black:
# # Don't know why, but transparent is rendered as black
# col = QColor(Qt.transparent)
colors["Label"] = col
# Progress
pg = item.data(Outline.goalPercentage.value)
colors["Progress"] = colorFromProgress(pg)
# Compile
if item.compile() in [0, "0"]:
colors["Compile"] = mixColors(QColor(S.text), QColor(S.window))
else:
colors["Compile"] = QColor(Qt.transparent) # will use default
return colors
def colorifyPixmap(pixmap, color):
# FIXME: ugly
p = QPainter(pixmap)
p.setCompositionMode(p.CompositionMode_Overlay)
p.fillRect(pixmap.rect(), color)
return pixmap
def appPath(suffix=None):
p = os.path.realpath(os.path.join(os.path.split(__file__)[0], ".."))
if suffix:
p = os.path.join(p, suffix)
return p
def writablePath(suffix=None):
if hasattr(QStandardPaths, "AppLocalDataLocation"):
p = QStandardPaths.writableLocation(QStandardPaths.AppLocalDataLocation)
else:
# Qt < 5.4
p = QStandardPaths.writableLocation(QStandardPaths.DataLocation)
if suffix:
p = os.path.join(p, suffix)
if not os.path.exists(p):
os.makedirs(p)
return p
def allPaths(suffix=None):
paths = []
# src directory
paths.append(appPath(suffix))
# user writable directory
paths.append(writablePath(suffix))
return paths
def tempFile(name):
"Returns a temp file."
return os.path.join(QDir.tempPath(), name)
def totalObjects():
return len(mainWindow().findChildren(QObject))
def printObjects():
print("Objects:", str(totalObjects()))
def findWidgetsOfClass(cls):
"""
Returns all widgets, children of MainWindow, whose class is cls.
@param cls: a class
@return: list of QWidgets
"""
return mainWindow().findChildren(cls, QRegExp())
def findBackground(filename):
"""
Returns the full path to a background file of name filename within resources folders.
"""
return findFirstFile(re.escape(filename), "resources/backgrounds")
def findFirstFile(regex, path="resources"):
"""
Returns full path of first file matching regular expression regex within folder path,
otherwise returns full path of last file in folder path.
"""
paths = allPaths(path)
for p in paths:
lst = os.listdir(p)
for l in lst:
if re.match(regex, l):
return os.path.join(p, l)
def customIcons():
"""
Returns a list of possible customIcons. String from theme.
"""
r = [
"text-plain",
"gnome-settings",
"applications-internet",
"applications-debugging",
"applications-development",
"system-help",
"info",
"dialog-question",
"dialog-warning",
"stock_timezone",
"stock_people",
"stock_shuffle",
"gtk-index",
"folder_color_picker",
"applications-versioncontrol",
"stock_home",
"stock_trash_empty",
"stock_trash_full",
"stock_yes",
"stock_no",
"stock_notes",
"stock_calendar",
"stock_mic",
'stock_score-lowest', 'stock_score-lower', 'stock_score-low', 'stock_score-normal', 'stock_score-high', 'stock_score-higher', 'stock_score-highest',
"stock_task",
"stock_refresh",
"application-community",
"applications-chat",
"application-menu",
"applications-education",
"applications-science",
"applications-puzzles",
"applications-roleplaying",
"applications-sports",
"applications-libraries",
"applications-publishing",
"applications-development",
"applications-games",
"applications-boardgames",
"applications-geography",
"applications-physics",
"package_multimedia",
"media-flash",
"media-optical",
"media-floppy",
"media-playback-start",
"media-playback-pause",
"media-playback-stop",
"media-playback-record",
"media-playback-start-rtl",
"media-eject",
"document-save",
"gohome",
'purple-folder', 'yellow-folder', 'red-folder', 'custom-folder', 'grey-folder', 'blue-folder', 'default-folder', 'pink-folder', 'orange-folder', 'green-folder', 'brown-folder',
'folder-home', 'folder-remote', 'folder-music', 'folder-saved-search', 'folder-projects', 'folder-sound', 'folder-publicshare', 'folder-pictures', 'folder-saved-search-alt', 'folder-tag',
'calendar-01', 'calendar-02', 'calendar-03', 'calendar-04', 'calendar-05', 'calendar-06', 'calendar-07', 'calendar-08', 'calendar-09', 'calendar-10',
'arrow-down', 'arrow-left', 'arrow-right', 'arrow-up', 'arrow-down-double', 'arrow-left-double', 'arrow-right-double', 'arrow-up-double',
'emblem-added', 'emblem-checked', 'emblem-downloads', 'emblem-dropbox-syncing', 'emblem-danger', 'emblem-development', 'emblem-dropbox-app', 'emblem-art', 'emblem-camera', 'emblem-dropbox-selsync', 'emblem-insync-des-error', 'emblem-insync-error', 'emblem-generic', 'emblem-favorites', 'emblem-error', 'emblem-dropbox-uptodate', 'emblem-marketing', 'emblem-money', 'emblem-music', 'emblem-noread', 'emblem-people', 'emblem-personal', 'emblem-sound', 'emblem-shared', 'emblem-sales', 'emblem-presentation', 'emblem-plan', 'emblem-system', 'emblem-urgent', 'emblem-videos', 'emblem-web',
'face-angel', 'face-clown', 'face-angry', 'face-cool', 'face-devilish', 'face-sick', 'face-sleeping', 'face-uncertain', 'face-monkey', 'face-ninja', 'face-pirate', 'face-glasses', 'face-in-love', 'face-confused',
'feed-marked-symbolic', 'feed-non-starred', 'feed-starred', 'feed-unmarked-symbolic',
'notification-new-symbolic',
]
return sorted(r)
def statusMessage(message, duration=5000):
mainWindow().statusBar().showMessage(message, duration)