turns-00035.parquet:11452
6f09f28034418bd2cf44bffd
turn 1/2gpt-4o-2024-08-06EnglishGermany1584 words
degenerate_repetitionAbsentFinal dense release
USER
Analyze the code for redundancies that violate the don't repeat yourself principle for python code. Python 3.12 code: import math
import re
from typing import Any, Union, Callable
from math import radians, pi, e, sqrt, log10, log, ceil
from PySide6.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLineEdit, QTextEdit, QScrollBar, QPushButton, QLabel, QFrame,
QGridLayout
)
from PySide6.QtGui import QFont
from PySide6.QtCore import Qt, QSize
def convert_angle(func: Callable[[float], float], x: float, use_degrees: bool) -> float:
"""Apply trig function with optional degree conversion."""
return func(radians(x) if use_degrees else x)
def kilograms_to_pounds(kg: float) -> float:
"""Convert kilograms to pounds."""
return kg / 0.45359237
def pounds_to_kilograms(lb: float) -> float:
"""Convert pounds to kilograms."""
return lb * 0.45359237
def celsius_to_kelvin(c: float) -> float:
"""Convert Celsius to Kelvin."""
return c + 273.15
def kelvin_to_celsius(k: float) -> float:
"""Convert Kelvin to Celsius."""
return k - 273.15
def celsius_to_fahrenheit(c: float) -> float:
"""Convert Celsius to Fahrenheit."""
return c * 9 / 5 + 32
def fahrenheit_to_celsius(f: float) -> float:
"""Convert Fahrenheit to Celsius."""
return (f - 32) * 5 / 9
def capitalize_sentence(sentence: str) -> str:
"""Capitalize first letter of each word."""
return ' '.join(word.capitalize() for word in sentence.split())
def remove_duplicates(text: str) -> str:
"""Remove duplicate words and empty lines."""
words = ' '.join(line.strip() for line in text.splitlines() if line.strip()).split()
return ' '.join(dict.fromkeys(words))
def get_safe_globals(use_degrees: bool, variables: dict) -> dict:
"""Provide safe globals for expression evaluation."""
trig_funcs = {'sin', 'cos', 'tan', 'asin', 'acos', 'atan'}
safe_globals = {
"sqrt": sqrt,
"log": log10,
"ln": log,
"pi": pi,
"e": e,
"lb": kilograms_to_pounds,
"kg": pounds_to_kilograms,
"C": kelvin_to_celsius,
"K": celsius_to_kelvin,
"F": celsius_to_fahrenheit,
"CF": fahrenheit_to_celsius,
"capitalize": capitalize_sentence,
"duplicates": remove_duplicates,
}
for func in trig_funcs:
safe_globals[func] = lambda x, f=func: convert_angle(getattr(math, f), x, use_degrees)
safe_globals.update(variables)
return safe_globals
def preprocess_text_functions(expression: str) -> str:
"""Preprocess text functions with argument quotes."""
def add_quotes(match):
func, args = match.groups()
return f'{func}("{args}")' if func in ['capitalize', 'duplicates'] else match.group(0)
return re.sub(r'\b(capitalize|duplicates)\s*\(\s*([^)]+)\s*\)', add_quotes, expression)
def validate_inverse_trig(expression: str, safe_globals: dict) -> Union[str, None]:
"""Check inverse trig function domain validity."""
for func, arg in re.findall(r"(\b\w+\b)\s*\(\s*([^)]+)\s*\)", expression):
if func in ["asin", "acos"]:
try:
value = eval(arg, {"__builtins__": None}, safe_globals)
if abs(value) > 1:
return f"Error: {func}(x) is undefined for |x| > 1"
except Exception as e:
return f"Error: {e}"
return None
def safe_evaluate(expression: str, safe_globals: dict, use_e_notation: bool) -> Union[str, Any]:
"""Safely evaluate expression and format result."""
try:
result = eval(expression, {"__builtins__": None}, safe_globals)
if isinstance(result, float) and abs(result) < 1e-10:
result = 0.0
if isinstance(result, (int, float)):
result = f"{result:.2e}" if use_e_notation else round(result, 2)
return result
except Exception as e:
return f"Error: {e}"
def evaluate_expression(expression: str, variables: dict, use_degrees: bool = False, use_e_notation: bool = False) -> Union[float, str]:
"""Evaluate math expression considering modes."""
assignment_match = re.match(r'^\s*([a-zA-Z_]\w*)\s*=\s*(.+)$', expression)
if assignment_match:
var_name, var_expr = assignment_match.groups()
safe_globals = get_safe_globals(use_degrees, variables)
var_value = safe_evaluate(var_expr, safe_globals, use_e_notation)
if isinstance(var_value, (int, float)):
variables[var_name] = var_value
return f"{var_name} = {var_value}"
return var_value
else:
safe_globals = get_safe_globals(use_degrees, variables)
expression = expression.replace("^", "**")
expression = preprocess_text_functions(expression)
error = validate_inverse_trig(expression, safe_globals)
if error:
return error
return safe_evaluate(expression, safe_globals, use_e_notation)
class CalculatorGUI(QMainWindow):
"""PySide6-based calculator application."""
def __init__(self):
super().__init__()
self.use_degrees = False
self.use_e_notation = False
self.variables = {}
self.history_entries = []
self.setup_window()
self.create_widgets()
self.layout_widgets()
self.connect_signals()
def setup_window(self) -> None:
"""Set main window properties."""
self.setWindowTitle("Calculator")
self.setGeometry(100, 100, 1280, 720)
def create_widgets(self) -> None:
"""Initialize GUI components."""
font = QFont()
font.setPointSize(12)
font.setFamily('Literata')
self.entry = QLineEdit()
self.entry.setFont(font)
self.entry.setPlaceholderText('Enter expression')
self.entry.setClearButtonEnabled(True)
self.entry.returnPressed.connect(self.calculate)
self.history_text = QTextEdit()
self.history_text.setFont(font)
self.history_text.setReadOnly(True)
self.scrollbar = QScrollBar(Qt.Vertical)
self.history_text.setVerticalScrollBar(self.scrollbar)
self.search_button = QPushButton("🔍", self.history_text)
self.search_button.setFixedSize(24, 24)
self.search_button.setToolTip("Search History")
self.search_button.setStyleSheet("""
QPushButton {
border: none;
background-color: rgba(255, 255, 255, 200);
font-size: 14px;
border-radius: 12px;
}
QPushButton:hover {
background-color: rgba(0, 120, 215, 200);
color: white;
}
""")
self.search_button.clicked.connect(self.toggle_search)
self.search_bar = QLineEdit(self.history_text)
self.search_bar.setPlaceholderText("Search...")
self.search_bar.setFixedHeight(24)
self.search_bar.setVisible(False)
self.search_bar.setStyleSheet("""
QLineEdit {
border: 1px solid gray;
border-radius: 4px;
padding: 2px 4px;
background-color: white;
}
""")
self.search_bar.textChanged.connect(self.filter_history)
self.search_bar.setClearButtonEnabled(True)
self.function_buttons = {
"Functions": ["sqrt", "sin", "cos", "tan", "asin", "acos", "atan", "log", "ln"],
"Conversions": ["lb", "kg", "K", "C", "F", "CF"],
"Text": ["capitalize", "duplicates"],
"Constants": ["pi", "e"],
"Settings": []
}
self.function_frame = QFrame()
self.create_function_buttons(font)
def create_function_buttons(self, font: QFont) -> None:
"""Create buttons for calculator functions."""
layout = QGridLayout()
categories = [("Functions", 3), ("Conversions", 2), ("Text", 2), ("Constants", 2), ("Settings", 1)]
starting_col = 0
for category, col_span in categories:
label = QLabel(category)
label.setFont(font)
layout.addWidget(label, 0, starting_col, 1, col_span, Qt.AlignCenter)
buttons = self.function_buttons.get(category, [])
if category == "Functions":
for i, func in enumerate(buttons):
button = QPushButton(func)
button.setFont(font)
button.clicked.connect(lambda _, f=func: self.insert_function(f, False))
layout.addWidget(button, (i // 3) + 1, starting_col + (i % 3))
elif category == "Settings":
self.mode_button = QPushButton("Radians")
self.mode_button.setFont(font)
self.mode_button.clicked.connect(self.toggle_mode)
layout.addWidget(self.mode_button, 1, starting_col, 1, 1)
self.e_notation_button = QPushButton("E Notation: Off")
self.e_notation_button.setFont(font)
self.e_notation_button.clicked.connect(self.toggle_e_notation)
layout.addWidget(self.e_notation_button, 2, starting_col, 1, 1)
elif category == "Conversions":
for idx, func in enumerate(buttons):
display_text = {
"lb": "lb(kg)", "kg": "kg(lb)", "K": "K(°C)",
"C": "°C(K)", "F": "F(°C)", "CF": "°C(F)"
}.get(func, func)
button = QPushButton(display_text)
button.setFont(font)
button.clicked.connect(lambda _, f=func: self.insert_function(f, False))
row, col = 1 + idx // 2, starting_col + idx % 2
layout.addWidget(button, row, col)
else:
num_buttons = len(buttons)
num_rows = ceil(num_buttons / col_span)
for idx, func in enumerate(buttons):
button = QPushButton(func)
button.setFont(font)
button.clicked.connect(lambda _, f=func, c=(category == "Constants"): self.insert_function(f, c))
row, col = 1 + idx // col_span, starting_col + idx % col_span
layout.addWidget(button, row, col)
for extra in range(col_span * num_rows - num_buttons):
layout.addWidget(QLabel(""), 1 + extra // col_span, starting_col + extra % col_span)
starting_col += col_span
self.function_frame.setLayout(layout)
def layout_widgets(self) -> None:
"""Arrange widgets in the main window."""
central_widget = QWidget()
main_layout = QVBoxLayout()
main_layout.addWidget(self.entry)
history_layout = QHBoxLayout()
history_layout.addWidget(self.history_text)
history_layout.addWidget(self.scrollbar)
main_layout.addLayout(history_layout)
main_layout.addWidget(self.function_frame)
central_widget.setLayout(main_layout)
self.setCentralWidget(central_widget)
def connect_signals(self) -> None:
"""Connect widget signals."""
QApplication.instance().focusChanged.connect(self.on_focus_changed)
def toggle_mode(self) -> None:
"""Toggle between degrees and radians."""
self.use_degrees = not self.use_degrees
self.mode_button.setText("Degrees" if self.use_degrees else "Radians")
def toggle_e_notation(self) -> None:
"""Toggle scientific notation for results."""
self.use_e_notation = not self.use_e_notation
self.e_notation_button.setText(f"E Notation: {'On' if self.use_e_notation else 'Off'}")
def insert_function(self, function: str, is_constant: bool = False) -> None:
"""Insert function or constant in entry."""
current_text = self.entry.text()
cursor_position = self.entry.cursorPosition()
if is_constant:
new_text = f"{current_text[:cursor_position]}{function}"
new_cursor_position = cursor_position + len(function)
else:
new_text = f"{current_text[:cursor_position]}{function}() {current_text[cursor_position:]}"
new_cursor_position = cursor_position + len(function) + 1
self.entry.setText(new_text)
self.entry.setCursorPosition(new_cursor_position)
self.entry.setFocus()
def calculate(self) -> None:
"""Evaluate entered expression."""
expression = self.entry.text().strip()
if expression:
result = evaluate_expression(
expression, self.variables, use_degrees=self.use_degrees, use_e_notation=self.use_e_notation
)
self.display_result(expression, result)
self.entry.clear()
def display_result(self, expression: str, result: Union[float, str]) -> None:
"""Show result in history."""
mode = "D" if self.use_degrees else "R"
if re.match(r'^\s*[a-zA-Z_]\w*\s*=\s*.+$', expression):
history_entry = f"[{mode}] {result}"
else:
history_entry = f"[{mode}] {expression} = {result}"
self.history_entries.append(history_entry)
self.refresh_history()
self.history_text.verticalScrollBar().setValue(
self.history_text.verticalScrollBar().maximum()
)
def toggle_search(self) -> None:
"""Toggle search bar visibility."""
is_visible = self.search_bar.isVisible()
self.search_bar.setVisible(not is_visible)
if not is_visible:
self.search_bar.setFocus()
self.search_bar.setText("")
else:
self.search_bar.clear()
self.update_search_bar_position()
self.refresh_history()
def filter_history(self, text: str) -> None:
"""Filter history based on search input."""
self.refresh_history(text)
def refresh_history(self, filter_text: str = "") -> None:
"""Refresh history view with optional filtering."""
self.history_text.clear()
for entry in self.history_entries:
if filter_text.lower() in entry.lower():
self.history_text.append(entry)
def resizeEvent(self, event) -> None:
"""Handle window resize events."""
super().resizeEvent(event)
self.update_search_bar_position()
def update_search_bar_position(self) -> None:
"""Reposition search button and bar."""
margin_right = 5
margin_top = 5
spacing = 5
history_width = self.history_text.viewport().width()
self.search_button.move(
history_width - self.search_button.width() - margin_right,
margin_top
)
if self.search_bar.isVisible():
search_bar_width = int(history_width * 0.25)
if search_bar_width + self.search_button.width() + spacing > history_width:
search_bar_width = history_width - self.search_button.width() - spacing - margin_right
self.search_bar.setFixedWidth(search_bar_width)
self.search_bar.move(
self.search_button.x() - self.search_bar.width() - spacing,
self.search_button.y()
)
def on_focus_changed(self, old: QWidget, new: QWidget) -> None:
"""Auto-hide search if focus is lost."""
if self.search_bar.isVisible() and not self.search_bar.text().strip() and new != self.search_bar:
self.toggle_search()
def showEvent(self, event) -> None:
"""Position search button on showing the window."""
super().showEvent(event)
self.update_search_bar_position()
def main() -> None:
"""Start the calculator application."""
app = QApplication([])
calculator = CalculatorGUI()
calculator.show()
app.exec()
if __name__ == "__main__":
main()
ASSISTANT
The provided Python code appears to follow the Don't Repeat Yourself (DRY) principle reasonably well, but there are a few areas that can be optimized to avoid redundancy and improve maintainability. Here’s an analysis of the code with some suggestions:
### Redundancy Analysis:
1. **Multiple Imports from the Same Module**:
- The code uses two different import statements for the `math` module:
```python
import math
from math import radians, pi, e, sqrt, log10, log, ceil
```
- **Suggestion**: Consolidate these imports into a single line, such as:
```python
from math import radians, pi, e, sqrt, log10, log, ceil
```
2. **Lambda Functions in Loops**:
- The code sets up `safe_globals` with lambda expressions:
```python
for func in trig_funcs:
safe_globals[func] = lambda x, f=func: convert_angle(getattr(math, f), x, use_degrees)
```
- While this pattern might seem useful for reducing code, it can be difficult to read and maintain.
- **Suggestion**: Consider using a helper function instead of a lambda inside the loop. This can make the code clearer:
```python
def make_trig_func(func, use_degrees):
return lambda x: convert_angle(getattr(math, func), x, use_degrees)
for func in trig_funcs:
safe_globals[func] = make_trig_func(func, use_degrees)
```
3. **Toggle State Functions**:
- There are two similar toggle methods:
- `toggle_mode()`
- `toggle_e_notation()`
- **Suggestion**: If more toggling functionalities are expected, consider creating a generic toggle method or utility to handle these similar tasks:
```python
def toggle_state(self, attribute: str, button: QPushButton, text_off: str, text_on: str):
current_state = getattr(self, attribute)
new_state = not current_state
setattr(self, attribute, new_state)
button.setText(text_on if new_state else text_off)
# Use this method in your toggle functions
def toggle_mode(self) -> None:
self.toggle_state('use_degrees', self.mode_button, 'Radians', 'Degrees')
def toggle_e_notation(self) -> None:
self.toggle_state('use_e_notation', self.e_notation_button, 'E Notation: Off', 'E Notation: On')
```
4. **Repetitive Button Creation**:
- The code for button creation and function configuration could be abstracted further to eliminate repetition found in categories of buttons.
- **Suggestion**: Implement a helper method for creating buttons to reduce redundancy in the `create_function_buttons` method.
By addressing these elements, the code can become more concise, easier to understand, and maintain, enhancing its DRY compliance.