Skip to content

Commit

Permalink
Merge other changes
Browse files Browse the repository at this point in the history
  • Loading branch information
dkratzert committed Jan 20, 2025
1 parent a057fae commit 8db2124
Show file tree
Hide file tree
Showing 11 changed files with 97 additions and 34 deletions.
13 changes: 10 additions & 3 deletions finalcif/appwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
from finalcif.tools.settings import FinalCifSettings
from finalcif.tools.shred import ShredCIF
from finalcif.tools.space_groups import SpaceGroups
from finalcif.tools.spgr_format import spgrps
from finalcif.tools.statusbar import StatusBar
from finalcif.tools.sumformula import formula_str_to_dict, sum_formula_to_html

Expand Down Expand Up @@ -1200,6 +1201,10 @@ def zip_report(self, report_filename: Path) -> None:
with suppress(Exception):
multitable = self.cif.finalcif_file_prefixed(prefix='', suffix='-multitable.docx')
arc.zip.write(filename=multitable, arcname=multitable.name)
with suppress(Exception):
prp_list = self.cif.finalcif_file.parent.glob('*.prp')
sorted_prp = sorted(prp_list, key=lambda x: x.stat().st_mtime)
arc.zip.write(filename=sorted_prp[-1], arcname=sorted_prp[-1].name)

def open_report_document(self, report_filename: Path, multi_table_document: Path) -> None:
if self.cif.is_multi_cif:
Expand Down Expand Up @@ -1253,7 +1258,8 @@ def save_current_cif_file(self) -> Union[bool, None]:
try:
self.cif.save()
self.status_bar.show_message(f' File Saved: {self.cif.finalcif_file}', 10)
print('File saved ...')
if DEBUG:
print('File saved ...')
return True
except Exception as e:
print('Unable to save file:')
Expand Down Expand Up @@ -1617,8 +1623,9 @@ def fill_sum_formula_lineedit(self) -> None:

def fill_space_group_lineedit(self) -> None:
try:
self.ui.Spacegroup_top_LineEdit.setText(
SpaceGroups().to_html(self.cif.space_group))
txt = (f'<body style="">{SpaceGroups().to_html(self.cif.space_group)} '
f'&thinsp;({spgrps[self.cif.space_group][1].get("itnumber")})</body>')
self.ui.Spacegroup_top_LineEdit.setText(txt)
except Exception as e:
print('Space group error:', str(e))
self.ui.Spacegroup_top_LineEdit.setText(self.cif.space_group)
Expand Down
2 changes: 2 additions & 0 deletions finalcif/cif/cif_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@
'_diffrn_radiation_monochromator',
'_diffrn_radiation_polarisn_norm',
'_diffrn_radiation_collimation',
'_bruker_diffrn_measurement_temperature_device',
'_bruker_diffrn_measurement_temperature_device_make',
'_olex2_diffrn_ambient_temperature_device',
'_diffrn_measurement_ambient_temperature_device_make',
'_diffrn_radiation_probe',
Expand Down
7 changes: 5 additions & 2 deletions finalcif/ciforder/order.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from __future__ import annotations

import enum
import sys
from pathlib import Path
Expand All @@ -23,7 +24,7 @@ class Column(enum.IntEnum):


class CifOrder(QtWidgets.QGroupBox):
def __init__(self, parent, cif_file: Path = None):
def __init__(self, parent=None, cif_file: Path = None):
super().__init__(parent)
self.ui = Ui_CifOrderForm()
self.ui.setupUi(self)
Expand Down Expand Up @@ -123,7 +124,7 @@ def set_keys(self, order_keys: List[str] = None):
self.ui.cifOrderTableWidget.insertRow(row)
self.set_row_text(key_text, row)
row += 1
#self.ui.cifOrderTableWidget.resizeColumnsToContents()
# self.ui.cifOrderTableWidget.resizeColumnsToContents()

def set_row_text(self, key_text: str, row: int) -> None:
item1 = CifOrderItem(key_text)
Expand Down Expand Up @@ -189,8 +190,10 @@ def export_cif(self):
return
show_general_warning(self, 'No permission to write file to {}'.format(Path(filename).resolve()))


if __name__ == "__main__":
from finalcif.tools.settings import FinalCifSettings

app = QtWidgets.QApplication(sys.argv)
# form = CifOrder(parent=None, cif_file=Path('test-data/1000007.cif').resolve())
settings = FinalCifSettings()
Expand Down
33 changes: 30 additions & 3 deletions finalcif/gui/combobox.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
from contextlib import suppress
from textwrap import wrap
from typing import TYPE_CHECKING

from PyQt5 import QtCore
from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import Qt, QObject, QEvent
from PyQt5.QtWidgets import QComboBox, QSizePolicy, QAction

from typing import TYPE_CHECKING
from finalcif.gui.validators import validators

light_red = QtGui.QColor(254, 191, 189)
white = QtGui.QColor(255, 255, 255)

if TYPE_CHECKING:
from finalcif.gui.custom_classes import MyCifTable
Expand All @@ -21,6 +24,7 @@ def __init__(self, parent: 'MyCifTable' = None):
super().__init__(parent)
self.parent: 'MyCifTable' = parent
self.cif_key = ''
self.color = white
self.setFocusPolicy(Qt.StrongFocus)
self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength)
self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
Expand Down Expand Up @@ -56,6 +60,29 @@ def eventFilter(self, widget: QObject, event: QEvent):
return True
return QObject.eventFilter(self, widget, event)

def keyPressEvent(self, event: QtGui.QKeyEvent):
super().keyPressEvent(event)
self.validate_text(self.currentText())

def validate_text(self, text: str):
validator = validators.get(self.cif_key, None)
if validator and not validator.valid(text):
self.setBadStyle()
self.setToolTip(validator.help_text)
else:
self.setToolTip('')
self.setRegularStyle()

def setBadStyle(self) -> None:
self.setStyleSheet(f"""
QComboBox {{
border: 3px solid {light_red};
}}
""")

def setRegularStyle(self) -> None:
self.setStyleSheet('')

def setUneditable(self):
# noinspection PyUnresolvedReferences
self.setFlags(self.flags() ^ Qt.ItemIsEditable)
Expand Down
1 change: 1 addition & 0 deletions finalcif/gui/custom_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
light_blue = QColor(244, 244, 249)
blue = QColor(102, 150, 179)
yellow = QColor(250, 247, 150) # #faf796
light_red = QColor(254, 191, 189)


class Column(IntEnum):
Expand Down
11 changes: 9 additions & 2 deletions finalcif/gui/import_selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def _save_selection(self):
self.settings.save_key_value('do_not_import_keys', self.get_keys(include=False))
self.settings.save_key_value('do_not_import_loops', self.get_loops(include=False))

def _empty_saved_selection(self):
def _empty_saved_selection(self) -> None:
self.settings.save_key_value('do_not_import_keys', [])
self.settings.save_key_value('do_not_import_loops', [])

Expand All @@ -82,7 +82,8 @@ def _get_excluded_items(self) -> Tuple[List[str], List[List[str]]]:
return excluded_kv, excluded_loops

def _set_label(self) -> None:
self.ui.importInfoLabel.setText(f"The CIF to import contains {self.keys_to_import} keys "
self.ui.importInfoLabel.setText(f"Importing\n{self.import_cif.fileobj}\n\n"
f"The CIF to import contains {self.keys_to_import} keys "
f"and {self.loops_to_import} loops from which "
f"{len(self.get_keys(include=True)) + len(self.get_loops(include=True))} "
f"are selected for import.")
Expand Down Expand Up @@ -126,6 +127,10 @@ def do_not_import_this_key(self, key: str) -> bool:
return False

def _select_only_new(self) -> None:
self.select_pairs()
self.select_loops()

def select_pairs(self):
rows = self.ui.importTable_keys.rowCount()
for row in range(rows):
widget: QtWidgets.QCheckBox = self.ui.importTable_keys.cellWidget(row, 0)
Expand All @@ -135,6 +140,8 @@ def _select_only_new(self) -> None:
widget.setChecked(False)
continue
widget.setChecked(True)

def select_loops(self):
rows = self.ui.importTable_loops.rowCount()
for row in range(rows):
widget: QtWidgets.QCheckBox = self.ui.importTable_loops.cellWidget(row, 0)
Expand Down
4 changes: 2 additions & 2 deletions finalcif/gui/loops.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

import gemmi

from finalcif.gui.custom_classes import light_blue
from finalcif.gui.custom_classes import light_blue, light_red
from finalcif.gui.plaintextedit import MyQPlainTextEdit
from finalcif.gui.validators import validators

Expand Down Expand Up @@ -249,7 +249,7 @@ def data(self, index: QModelIndex, role: int = None):
(row, col) in [(x['row'], x['column']) for x in self.modified] and self.validate_text(value, col)):
return QVariant(light_blue)
elif role == Qt.BackgroundColorRole and not self.validate_text(value, col):
return QVariant(QColor(254, 191, 189))
return QVariant(light_red)
else:
QVariant(QColor(255, 255, 255))
if role == Qt.EditRole:
Expand Down
4 changes: 2 additions & 2 deletions finalcif/gui/plaintextedit.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from finalcif.gui.validators import validators

if TYPE_CHECKING:
from finalcif.gui.custom_classes import MyCifTable
from finalcif.gui.custom_classes import MyCifTable, light_red


class Column(IntEnum):
Expand Down Expand Up @@ -157,7 +157,7 @@ def keyPressEvent(self, event: QtGui.QKeyEvent):
def validate_text(self, text: str):
validator = validators.get(self.cif_key, None)
if validator and not validator.valid(text):
self.setBackground(QColor(254, 191, 189))
self.setBackground(light_red)
self.setToolTip(validator.help_text)
else:
self.setToolTip('')
Expand Down
42 changes: 29 additions & 13 deletions finalcif/report/templated_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

from PyQt5.QtWidgets import QApplication

from finalcif.cif.text import string_to_utf8
from finalcif.template.xsl.convert import xml_to_html

# This is necessary, because if jinja crashes, we show an error dialog:
Expand Down Expand Up @@ -527,8 +528,8 @@ def get_torsion_symminfo(self) -> str:
def get_hydrogen_symminfo(self) -> str:
return self._hydrogens.symminfo

def get_crystallization_method(self, cif) -> str:
return gstr(cif['_exptl_crystal_recrystallization_method'])
def get_crystallization_method(self, cif: CifContainer) -> str:
return string_to_utf8(gstr(cif['_exptl_crystal_recrystallization_method']))

def get_radiation(self, cif: CifContainer) -> str | RichText:
raise NotImplementedError
Expand All @@ -546,7 +547,7 @@ def hkl_index_limits(self, cif: CifContainer) -> str:
def make_3d(self, cif: CifContainer, options: Options) -> str:
raise NotImplementedError

def space_group_subdoc(self, cif: CifContainer, tpl_doc: DocxTemplate):
def space_group_formatted(self, cif: CifContainer, tpl_doc: DocxTemplate):
raise NotImplementedError

def make_picture(self, options: Options, picfile: Path, tpl_doc: DocxTemplate):
Expand Down Expand Up @@ -661,7 +662,7 @@ def get_refinement_gui(self, cif: CifContainer) -> str:
else:
refinement_gui = '[Unknown program in _computing_structure_refinement]'
self.literature['gui'] = gui_reference
return refinement_gui
return string_to_utf8(refinement_gui)

def _get_scaling_program(self, absdetails: str) -> tuple[str, str]:
scale_prog = ''
Expand Down Expand Up @@ -695,11 +696,11 @@ def get_absortion_correction_program(self, cif: CifContainer) -> str:
scale_prog = 'STOE X-RED'
self.literature['absorption'] = ref.XRedReference('X-RED', 'unknown version')
scale_prog += " " + version
return scale_prog
return string_to_utf8(scale_prog)

def solution_method(self, cif: CifContainer) -> str:
solution_method = gstr(cif['_atom_sites_solution_primary']) or '??'
return solution_method.strip('\n\r')
return solution_method.strip('\n\r').strip()

def refinement_prog(self, cif: CifContainer) -> Reference | str:
refined = gstr(cif['_computing_structure_refinement']) or '??'
Expand Down Expand Up @@ -768,7 +769,10 @@ def get_angstrom_resolution(self, cif: CifContainer):
return f'{d:.2f}'

def get_redundancy(self, cif):
n_refl = cif['_diffrn_reflns_number']
if twinabs := cif['_diffrn_reflns_bruker_twinabs_number']:
n_refl = twinabs
else:
n_refl = cif['_diffrn_reflns_number']
n_all = cif['_reflns_number_total']
try:
redundancy = float(n_refl) / float(n_all)
Expand All @@ -785,6 +789,10 @@ def get_data_to_param(self, cif):
return ''
return f'{redundancy:.2f}'

def refinement_details(self, cif):
details = ' '.join(cif['_refine_special_details'].splitlines(keepends=False)).strip()
return string_to_utf8(details)


class HtmlFormatter(Formatter):

Expand Down Expand Up @@ -812,19 +820,21 @@ def _format_symminfo(self, txt):
def get_bonds_angles_symminfo(self) -> str:
return self._format_symminfo(self._bonds_angles.symminfo)

def get_torsion_symminfo(self):
def get_torsion_symminfo(self) -> str:
return self._format_symminfo(self._torsions.symminfo)

def get_hydrogen_symminfo(self):
def get_hydrogen_symminfo(self) -> str:
return self._format_symminfo(self._hydrogens.symminfo)

def make_3d(self, cif: CifContainer, options: Options) -> str:
return '[3D representation of the structure in html/javascript not implemented]'

def space_group_subdoc(self, cif: CifContainer, _: None) -> str:
def space_group_formatted(self, cif: CifContainer, _: None) -> str:
s = SpaceGroups()
try:
spgrxml = s.to_mathml(cif.space_group)
spgrxml = s.to_html(cif.space_group)
# Mathml doesn't work well in pyQt
# spgrxml = s.to_mathml(cif.space_group)
except KeyError:
spgrxml = '<math xmlns="http://www.w3.org/1998/Math/MathML">?</math>'
return f'{spgrxml} ({cif.spgr_number})'
Expand Down Expand Up @@ -945,7 +955,7 @@ def disorder_description(self, cif: CifContainer) -> RichText:
self.literature['dsr'] = ref.DSRReference2018()
return Disorder(cif).richtext()

def space_group_subdoc(self, cif: CifContainer, tpl_doc: DocxTemplate) -> Subdoc:
def space_group_formatted(self, cif: CifContainer, tpl_doc: DocxTemplate) -> Subdoc:
"""
Generates a Subdoc subdocument with the xml code for a math element in MSWord.
"""
Expand Down Expand Up @@ -1056,6 +1066,12 @@ def make_templated_docx_report(self,
tpl_doc.render(context, jinja_env=jinja_env, autoescape=True)
tpl_doc.save(output_filename)
return True
except PermissionError:
#raise
show_general_warning(parent=None, window_title='Warning',
warn_text=f'The document {output_filename} could not be opened to write the report.\n'
f'Is the file already opened?')
return False
except Exception as e:
show_general_warning(parent=None, window_title='Warning', warn_text='Document generation failed',
info_text=str(e))
Expand Down Expand Up @@ -1124,7 +1140,7 @@ def get_context(self, cif: CifContainer, options: Options, picfile: Path, tpl_do
context = {'options' : options,
'cif' : cif,
'name' : cif.block.name,
'space_group' : self.text_formatter.space_group_subdoc(cif, tpl_doc),
'space_group' : self.text_formatter.space_group_formatted(cif, tpl_doc),
'structure_figure' : self.text_formatter.make_picture(options, picfile, tpl_doc),
'3d_structure' : self.text_formatter.make_3d(cif, options),
'crystallization_method' : self.text_formatter.get_crystallization_method(cif),
Expand Down
2 changes: 1 addition & 1 deletion finalcif/tools/space_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def to_html(self, space_group: str) -> str:
if not space_group:
return ''
txt = self.to_html_without_body(space_group)
return f'<body style="">{txt} &thinsp;({self.spgrps[space_group][1].get("itnumber")})</body>'
return txt

def to_plain_text(self, space_group: str) -> str:
"""
Expand Down
12 changes: 6 additions & 6 deletions finalcif/tools/sumformula.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def sum_formula_to_html(sumform: Dict[str, float | int], break_after: int = 99)
"""
if not sumform:
return ''
l = ['<html><body>']
formula_list = ['<html><body>']
num = 0
for el in sumform:
if sumform[el] == 0 or sumform[el] is None:
Expand All @@ -24,12 +24,12 @@ def sum_formula_to_html(sumform: Dict[str, float | int], break_after: int = 99)
except (TypeError, ValueError):
times = 1
if num > 3 and num % break_after == 0:
l.append("<br>")
formula_list.append("<br>")
if times == 1:
l.append('{}'.format(el))
formula_list.append('{}'.format(el))
else:
l.append("{}<sub>{:g}</sub>".format(el, times))
formula_list.append("{}<sub>{:g}</sub>".format(el, times))
num += 1
l.append('</body></html>')
formula = "".join(l)
formula_list.append('</body></html>')
formula = "".join(formula_list)
return formula

0 comments on commit 8db2124

Please sign in to comment.