1# ---------------------------------------------------------------------- 2# $Id: qrpreferences.py 2640 2014-08-12 02:04:01Z thomas-sturm $ 3# ---------------------------------------------------------------------- 4# (c) 2010 T. Sturm, C. Zengler, 2011-2014 T. Sturm 5# ---------------------------------------------------------------------- 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 10# * Redistributions of source code must retain the relevant 11# copyright notice, this list of conditions and the following 12# disclaimer. 13# * Redistributions in binary form must reproduce the above 14# copyright notice, this list of conditions and the following 15# disclaimer in the documentation and/or other materials provided 16# with the distribution. 17# 18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22# OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29# 30 31import os 32 33from types import StringType 34 35from PySide.QtCore import Qt 36from PySide.QtCore import Signal 37from PySide.QtCore import QSettings 38 39from PySide.QtGui import QDialog 40from PySide.QtGui import QWidget 41from PySide.QtGui import QListWidget 42from PySide.QtGui import QListWidgetItem 43from PySide.QtGui import QListView 44from PySide.QtGui import QStackedWidget 45from PySide.QtGui import QHBoxLayout 46from PySide.QtGui import QFormLayout 47from PySide.QtGui import QPushButton 48from PySide.QtGui import QVBoxLayout 49from PySide.QtGui import QFontDialog 50from PySide.QtGui import QFont 51from PySide.QtGui import QFontInfo 52from PySide.QtGui import QGroupBox 53from PySide.QtGui import QLabel 54from PySide.QtGui import QCheckBox 55from PySide.QtGui import QLineEdit 56from PySide.QtGui import QFontComboBox 57from PySide.QtGui import QComboBox 58from PySide.QtGui import QFontDatabase 59from PySide.QtGui import QMessageBox 60 61from qrlogging import fontLogger 62from qrlogging import signalLogger 63from qrlogging import traceLogger 64 65from qrdefaults import QtReduceDefaults 66from qrdefaults import QtReduceIconSets 67 68 69class QtReducePreferencePane(QDialog): 70 # QtReducePreferencePane are the dialog windows for setting preferences. 71 # Instances are created via menu or keyboard shortcut in QtReduceMainWindow. 72 73 def __init__(self, parent=None): 74 super(QtReducePreferencePane, self).__init__(parent) 75 76 self.__createContents() 77 78 self.toolBar = QtReducePreferencesToolBar(self) 79 self.worksheet = QtReducePreferencesWorksheet(self) 80 self.computation = QtReducePreferencesComputation(self) 81 82 self.pagesWidget = QStackedWidget() 83 self.pagesWidget.addWidget(self.toolBar) 84 self.pagesWidget.addWidget(self.worksheet) 85 self.pagesWidget.addWidget(self.computation) 86 87 self.pagesWidget.setCurrentIndex( 88 self.contentsWidget.row(self.contentsWidget.currentItem())) 89 90 closeButton = QPushButton(self.tr("Close")) 91 closeButton.clicked.connect(self.close) 92 93 horizontalLayout = QHBoxLayout() 94 horizontalLayout.addWidget(self.contentsWidget) 95 horizontalLayout.addWidget(self.pagesWidget) 96 97 buttonsLayout = QHBoxLayout() 98 buttonsLayout.addStretch(1) 99 buttonsLayout.addWidget(closeButton) 100 101 mainLayout = QVBoxLayout() 102 mainLayout.addLayout(horizontalLayout) 103 mainLayout.addLayout(buttonsLayout) 104 105 self.setLayout(mainLayout) 106 107 self.setWindowTitle(self.tr("QReduce Preferences")) 108 109 def changePage(self,current,previous): 110 if not current: 111 current = previous 112 QSettings().setValue("preferences/currentitem",current.text()) 113 self.pagesWidget.setCurrentIndex(self.contentsWidget.row(current)) 114 115 def __createContents(self): 116 self.contentsWidget = QListWidget() 117 self.contentsWidget.setViewMode(QListView.ListMode) 118 self.contentsWidget.setMovement(QListView.Static) 119 120 toolBar = QListWidgetItem(self.contentsWidget) 121 toolBar.setText(self.tr("Toolbar")) 122 toolBar.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) 123 124 worksheet = QListWidgetItem(self.contentsWidget) 125 worksheet.setText(self.tr("Worksheet")) 126 worksheet.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) 127 128 computation = QListWidgetItem(self.contentsWidget) 129 computation.setText(self.tr("Computation")) 130 computation.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) 131 132 currentItem = QSettings().value("preferences/currentitem", 133 self.tr(QtReduceDefaults.CURRENTITEM)) 134 if currentItem == self.tr("Toolbar"): 135 self.contentsWidget.setCurrentItem(toolBar) 136 elif currentItem == self.tr("Worksheet"): 137 self.contentsWidget.setCurrentItem(worksheet) 138 elif currentItem == self.tr("Computation"): 139 self.contentsWidget.setCurrentItem(computation) 140 141 self.contentsWidget.currentItemChanged.connect(self.changePage) 142 143 144class QtReduceComboBox(QComboBox): 145 def __init__(self): 146 super(QtReduceComboBox,self).__init__() 147 self.setFocusPolicy(Qt.NoFocus) 148 self.setEditable(False) 149 150 151class QtReduceIconSizeComboBox(QtReduceComboBox): 152 currentIconSizeChanged = Signal(StringType) 153 154 def __init__(self,parent=None): 155 super(QtReduceIconSizeComboBox,self).__init__() 156 self.currentIndexChanged.connect(self.currentIndexChangedHandler) 157 158 def currentIndexChangedHandler(self,index): 159 return self.currentIconSizeChanged.emit(self.currentText()) 160 161 162class QtReducePreferencesToolBar(QWidget): 163 def __init__(self,parent=None): 164 super(QtReducePreferencesToolBar,self).__init__(parent) 165 166 settings = QSettings() 167 168 toolBarGroup = QGroupBox(self.tr("Toolbar")) 169 170 self.iconSetCombo = QtReduceComboBox() 171 iDbKeys = QtReduceIconSets().db.keys() 172 iDbKeys.sort() 173 self.iconSetCombo.addItems(iDbKeys) 174 175 settings.setValue("toolbar/iconset","Oxygen") # Hack 176 177 traceLogger.debug("toolbar/iconset=%s" % settings.value("toolbar/iconset")) 178 179 self.iconSetCombo.setCurrentIndex( 180 self.iconSetCombo.findText( 181 settings.value("toolbar/iconset",QtReduceDefaults.ICONSET))) 182 183 self.iconSizeCombo = QtReduceIconSizeComboBox() 184 self.iconSizeCombo.addItems(["16","22","32"]) 185 self.iconSizeCombo.setCurrentIndex( 186 self.iconSizeCombo.findText( 187 str(settings.value("toolbar/iconsize", 188 QtReduceDefaults.ICONSIZE)))) 189 190 self.showCombo = QtReduceComboBox() 191 self.showCombo.addItems([self.tr("Symbol and Text"), 192 self.tr("Only Symbol"), 193 self.tr("Only Text")]) 194 self.showCombo.setCurrentIndex(self.showCombo.findText( 195 settings.value("toolbar/buttonstyle", 196 self.tr(QtReduceDefaults.BUTTONSTYLE)))) 197 198 toolBarLayout = QFormLayout() 199# toolBarLayout.addRow(self.tr("Symbol Set"),self.iconSetCombo) 200 toolBarLayout.addRow(self.tr("Symbol Size"),self.iconSizeCombo) 201 toolBarLayout.addRow(self.tr("Show"),self.showCombo) 202 203 toolBarGroup.setLayout(toolBarLayout) 204 205 mainLayout = QVBoxLayout() 206 mainLayout.addWidget(toolBarGroup) 207 208 self.setLayout(mainLayout) 209 210 211class QtReduceFontComboBox(QtReduceComboBox): 212 currentFontChanged = Signal(QFont) 213 214 def __init__(self,parent=None): 215 super(QtReduceFontComboBox,self).__init__() 216 fdb = QFontDatabase() 217 l = [] 218 self.fontDict = {} 219 for fam in fdb.families(QFontDatabase.Latin): 220 for sty in fdb.styles(fam): 221 if not fam in l and fdb.isFixedPitch(fam,sty) \ 222 and not fdb.bold(fam,sty) and not fdb.italic(fam,sty) \ 223 and self.__osxHack(fam): 224 fontLogger.debug("family=%s, style=%s, isFixedPitch=%s" % 225 (fam, sty, fdb.isFixedPitch(fam,sty))) 226 sizes = fdb.smoothSizes(fam,sty) 227 if sizes: 228 font = fdb.font(fam,sty,sizes[0]) 229 if not font.exactMatch(): 230 fontLogger.debug("no exactMatch for %s %s %s" % 231 (fam,sty,sizes[0])) 232 233 l += [fam] 234 self.fontDict.update({str(fam):font}) 235 l.sort 236 self.addItems(l) 237 self.currentIndexChanged.connect(self.currentIndexChangedHandler) 238 239 def __osxHack(self,fam): 240 if os.uname()[0] != "Darwin": 241 return True 242 if fam.find("Andale") != -1 \ 243 or fam.find("Bitstream") != -1 \ 244 or fam.find("Consolas") != -1 \ 245 or fam.find("Courier") != -1 \ 246 or fam.find("DejaVu") != -1 \ 247 or fam.find("Lucida") != -1 \ 248 or fam.find("Monaco") != -1: 249 return True 250 return False 251 252 def setCurrentFont(self,font): 253 info = QFontInfo(font) 254 self.setCurrentIndex(self.findText(info.family())) 255 256 def currentFont(self): 257 return self.fontDict[self.currentText()] 258 259 def currentIndexChangedHandler(self,index): 260 return self.currentFontChanged.emit(self.currentFont()) 261 262 263class QtReduceFontSizeComboBox(QtReduceComboBox): 264 currentFontSizeChanged = Signal(StringType) 265 266 def __init__(self,parent=None): 267 super(QtReduceFontSizeComboBox,self).__init__() 268 self.currentIndexChanged.connect(self.currentIndexChangedHandler) 269 270 def currentFontSize(self): 271 return self.findText(currentSize) 272 273 def currentIndexChangedHandler(self,index): 274 return self.currentFontSizeChanged.emit(self.currentText()) 275 276 277class QtReduceFontSizeComboBoxFs(QtReduceComboBox): 278 currentFontSizeChangedFs = Signal(StringType) 279 280 def __init__(self,parent=None): 281 super(QtReduceFontSizeComboBoxFs,self).__init__() 282 self.currentIndexChanged.connect(self.currentIndexChangedHandler) 283 284 def currentFontSize(self): 285 return self.findText(currentSize) 286 287 def currentIndexChangedHandler(self,index): 288 return self.currentFontSizeChangedFs.emit(self.currentText()) 289 290 291class QtReducePreferencesWorksheet(QWidget): 292 def __init__(self,parent=None): 293 super(QtReducePreferencesWorksheet,self).__init__(parent) 294 295 fontGroup = QGroupBox(self.tr("Fonts")) 296 297 self.fontCombo = QtReduceFontComboBox(self) 298 self.setFocusPolicy(Qt.NoFocus) 299 self.fontCombo.setEditable(False) 300 self.fontCombo.setCurrentFont(self.parent().parent().controller.view.font()) 301 302 self.sizeCombo = QtReduceFontSizeComboBox() 303 self.sizeComboFs = QtReduceFontSizeComboBoxFs() 304 self.findSizes(self.fontCombo.currentFont()) 305 self.fontCombo.currentFontChanged.connect(self.findSizes) 306 307 fontLayout = QFormLayout() 308 fontLayout.addRow(self.tr("General Worksheet Font"),self.fontCombo) 309 fontLayout.addRow(self.tr("Font Size"),self.sizeCombo) 310 fontLayout.addRow(self.tr("Full Screen Font Size"),self.sizeComboFs) 311 312 fontGroup.setLayout(fontLayout) 313 314 mainLayout = QVBoxLayout() 315 mainLayout.addWidget(fontGroup) 316 317 self.setLayout(mainLayout) 318 319 def findSizes(self,font): 320 fontLogger.debug("font.key()=%s" % font.key()) 321 fontDatabase = QFontDatabase() 322 323 self.sizeCombo.blockSignals(True) 324 self.sizeCombo.clear() 325 326 self.sizeComboFs.blockSignals(True) 327 self.sizeComboFs.clear() 328 329 styleStr = fontDatabase.styleString(font) 330 if fontDatabase.isSmoothlyScalable(font.family(),styleStr): 331 for size in QFontDatabase.standardSizes(): 332 self.sizeCombo.addItem(str(size)) 333 self.sizeComboFs.addItem(str(size)) 334 else: 335 for size in fontDatabase.smoothSizes(font.family(),styleStr): 336 self.sizeCombo.addItem(str(size)) 337 self.sizeComboFs.addItem(str(size)) 338 339 self.sizeCombo.blockSignals(False) 340 self.sizeComboFs.blockSignals(False) 341 342 currentSize = unicode(QSettings().value("worksheet/fontsize", 343 QtReduceDefaults.FONTSIZE)) 344 sizeIndex = self.sizeCombo.findText(currentSize) 345 self.sizeCombo.setCurrentIndex(sizeIndex) 346 347 currentSize = unicode(QSettings().value("worksheet/fontsizefs", 348 QtReduceDefaults.FONTSIZEFS)) 349 sizeIndex = self.sizeCombo.findText(currentSize) 350 self.sizeComboFs.setCurrentIndex(sizeIndex) 351 352 353class QtReducePreferencesComputation(QWidget): 354 def __init__(self,parent=None): 355 super(QtReducePreferencesComputation,self).__init__(parent) 356 357 reduceGroup = QGroupBox("Reduce") 358 359 self.reduceBinary = QLineEdit() 360 361 # font = self.reduceBinary.font() 362 # font.setFamily(QSettings().value("worksheet/fontfamily", 363 # QtReduceDefaults.FONTFAMILY)) 364 # self.reduceBinary.setFont(font) 365 366 self.reduceBinary.setText(QSettings().value("computation/reduce", 367 QtReduceDefaults.REDUCE)) 368 369 self.reduceBinary.editingFinished.connect(self.editingFinishedHandler) 370 371 reduceLayout = QFormLayout() 372 reduceLayout.addRow(self.tr("Reduce Binary"),self.reduceBinary) 373 374 reduceGroup.setLayout(reduceLayout) 375 376 mainLayout = QVBoxLayout() 377 mainLayout.addWidget(reduceGroup) 378 379 self.setLayout(mainLayout) 380 381 def editingFinishedHandler(self): 382 settings = QSettings() 383 old = settings.value("computation/reduce",QtReduceDefaults.REDUCE) 384 new = self.reduceBinary.text() 385 if old == new: 386 return 387 self.reduceBinary.blockSignals(True) 388 tit = "Change Binary?" 389 txt = self.tr("Do you really want to change this setting?") 390 itxt = self.tr("If yes, then the binary ") 391 itxt += '"' + new + '" ' 392 itxt += self.tr("will be used at the next restart.") 393 mbox = QMessageBox(self) 394 mbox.setIcon(QMessageBox.Question) 395 mbox.setWindowModality(Qt.WindowModal) 396 mbox.setWindowTitle(tit) 397 mbox.setText(txt) 398 mbox.setInformativeText(itxt) 399 mbox.setStandardButtons(QMessageBox.Yes|QMessageBox.No) 400 button = mbox.exec_() 401 if button == QMessageBox.Yes: 402 settings.setValue("computation/reduce",new) 403 else: 404 self.reduceBinary.setText(old) 405 self.reduceBinary.blockSignals(False) 406