Package horizons :: Package gui :: Module util
[hide private]
[frames] | no frames]

Source Code for Module horizons.gui.util

  1  # ################################################### 
  2  # Copyright (C) 2008-2017 The Unknown Horizons Team 
  3  # team@unknown-horizons.org 
  4  # This file is part of Unknown Horizons. 
  5  # 
  6  # Unknown Horizons is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, 
 12  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 14  # GNU General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the 
 18  # Free Software Foundation, Inc., 
 19  # 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
 20  # ################################################### 
 21   
 22  import logging 
 23  import os 
 24  from io import BytesIO 
 25   
 26  from fife import fife 
 27  from fife.extensions.pychan import loadXML 
 28  from fife.extensions.pychan.widgets import Container, HBox, Icon 
 29   
 30  from horizons.gui.i18n import translate_widget 
 31  from horizons.gui.widgets.imagebutton import ImageButton 
 32  from horizons.i18n import gettext as T 
 33  from horizons.util.python import decorators 
 34  from horizons.util.python.callback import Callback 
35 36 37 @decorators.cachedfunction 38 -def get_gui_files_map():
39 """Returns a dictionary { basename : full_path } 40 for all xml gui files in content/gui 41 """ 42 xml_files = {} 43 for root, dirs, files in os.walk('content/gui/xml'): 44 for f in files: 45 if not f.endswith('.xml'): 46 continue 47 if f in xml_files: 48 raise Exception('Another file by the name {name} already exists. ' 49 'Please use unique names!'.format(name=f)) 50 xml_files[f] = os.path.join(root, f) 51 return xml_files
52
53 54 -def get_happiness_icon_and_helptext(value, session):
55 happiness_icon_path = "content/gui/icons/templates/happiness/" 56 sad = session.db.get_lower_happiness_limit() 57 happy = session.db.get_upper_happiness_limit() 58 if value <= sad: 59 happiness_icon_path += "sad.png" 60 happiness_helptext = T("sad") 61 elif sad < value < happy: 62 happiness_icon_path += "average.png" 63 happiness_helptext = T("satisfied") 64 elif value >= happy: 65 happiness_icon_path += "happy.png" 66 happiness_helptext = T("happy") 67 68 return happiness_icon_path, happiness_helptext
69
70 71 @decorators.cachedfunction 72 -def get_widget_xml(filename):
73 """ 74 This function reads the given widget file's content and returns the XML. 75 It is cached to avoid useless IO. 76 """ 77 with open(get_gui_files_map()[filename], 'rb') as open_file: 78 return open_file.read()
79
80 81 -def load_uh_widget(filename, style=None, center_widget=False):
82 """Loads a pychan widget from an xml file and applies uh-specific modifications 83 """ 84 # load widget 85 try: 86 widget = loadXML(BytesIO(get_widget_xml(filename))) 87 except (IOError, ValueError) as error: 88 log = logging.getLogger('gui') 89 log.error('PLEASE REPORT: invalid path %s in translation!\n> %s', filename, error) 90 raise 91 92 # translate 93 widget = translate_widget(widget, filename) 94 95 if style: 96 widget.stylize(style) 97 # format headline 98 for w in widget.findChildren(): 99 if w.name.startswith("headline") or w.name == "name": 100 w.stylize('headline') 101 elif w.name.startswith("uni_") or w.comment.startswith("uni_"): 102 w.font = 'unifont' 103 elif w.name.startswith("transparent_"): 104 w.stylize('transparent') 105 if center_widget: 106 widget.position_technique = "center:center" 107 108 return widget
109
110 111 @decorators.cachedfunction 112 -def get_res_icon_path(res, size=32, greyscale=False, full_path=True):
113 """Returns path of a resource icon or placeholder path, if icon does not exist. 114 @param res: resource id. Pass 'placeholder' to get placeholder path. 115 @param full_path: whether to return full icon path or a stub path suitable for ImageButton path= 116 """ 117 icon_path = 'content/gui/icons/resources/{size}/'.format(size=size) 118 if greyscale: 119 icon_path = icon_path + 'greyscale/' 120 if res == 'placeholder': 121 icon_path = icon_path + 'placeholder.png' 122 else: 123 icon_path = icon_path + '{res:03d}.png'.format(res=res) 124 125 try: 126 Icon(image=icon_path).hide() 127 except fife.NotFound: # ImageManager: image not found, use placeholder or die 128 if res == 'placeholder': 129 raise Exception('Image not found: {icon_path}'.format(icon_path=icon_path)) 130 else: 131 log = logging.getLogger('gui') 132 log.warning('Image not found: %s', icon_path) 133 icon_path = get_res_icon_path('placeholder', size) 134 135 if full_path: 136 return icon_path 137 else: 138 # remove 'content/gui/' and '.png' 139 return icon_path[12:][:-4]
140
141 142 -def create_resource_icon(res_id, db):
143 """Creates a pychan Icon for a resource. Helptext is set to name of *res_id*. 144 @param res_id: resource id 145 @param db: dbreader for main db""" 146 widget = Icon(image=get_res_icon_path(res_id)) 147 widget.helptext = db.get_res_name(res_id) 148 widget.scale = True 149 return widget
150
151 152 -def create_resource_selection_dialog(on_click, inventory, db, 153 widget='select_trade_resource.xml', res_filter=None, amount_per_line=None):
154 """Returns a container containing resource icons. 155 @param on_click: called with resource id as parameter on clicks 156 @param inventory: to determine fill status of resource slots 157 @param db: main db instance 158 @param widget: which xml file to use as a template. Default: tabwidget. Required 159 since the resource bar also uses this code (no tabs there though). 160 @param res_filter: callback to decide which icons to use. Default: show all 161 @param amount_per_line: how many resource icons per line. Default: try to fit layout 162 """ 163 from horizons.gui.widgets.imagefillstatusbutton import ImageFillStatusButton 164 dummy_icon_path = "icons/resources/none_gray" 165 166 dlg = load_uh_widget(widget) 167 168 icon_size = ImageFillStatusButton.ICON_SIZE # used for dummy button 169 cell_size = ImageFillStatusButton.CELL_SIZE 170 button_width = cell_size[0] 171 vbox = dlg.findChild(name="resources") 172 amount_per_line = amount_per_line or vbox.width // button_width 173 # Add the zero element to the beginning that allows to remove the currently 174 # sold/bought resource: 175 resources = [0] + db.get_res(only_tradeable=True) 176 current_hbox = HBox(name="hbox_0", padding=0) 177 index = 1 178 for res_id in resources: 179 # don't show resources that are already in the list 180 if res_filter is not None and not res_filter(res_id): 181 continue 182 183 # on click: add this res 184 cb = Callback(on_click, res_id) 185 186 # create button (dummy one or real one) 187 if res_id == 0 or inventory is None: 188 reset_button = ImageButton(max_size=icon_size, name="resource_icon_00") 189 reset_button.path = dummy_icon_path 190 191 button = Container(size=cell_size) 192 # add the "No Resource" image to the container, positioned in the top left 193 button.addChild(reset_button) 194 # capture a mouse click on the container. It's possible to click on the 195 # image itself or into the empty area (below or to the right of the image) 196 button.capture(cb, event_name="mouseClicked") 197 button.name = "resource_{:d}".format(res_id) 198 else: 199 amount = inventory[res_id] 200 filled = int(inventory[res_id] / inventory.get_limit(res_id) * 100) 201 button = ImageFillStatusButton.init_for_res(db, res_id, 202 amount=amount, filled=filled, uncached=True, 203 use_inactive_icon=False, showprice=True) 204 button.capture(cb, event_name="mouseClicked") 205 button.name = "resource_{:d}".format(res_id) 206 207 current_hbox.addChild(button) 208 if index % amount_per_line == 0: 209 vbox.addChild(current_hbox) 210 box_id = index // amount_per_line 211 current_hbox = HBox(name="hbox_{}".format(box_id), padding=0) 212 index += 1 213 vbox.addChild(current_hbox) 214 vbox.adaptLayout() 215 216 return dlg
217