More langtool improvements
All checks were successful
Build Dusk / build-psp (push) Successful in 1m5s
Build Dusk / build-linux (push) Successful in 1m2s

This commit is contained in:
2025-11-16 00:04:28 -06:00
parent 68b63d3007
commit be422d0a1e
4 changed files with 167 additions and 189 deletions

View File

@@ -1,8 +1,9 @@
#
msgid "" msgid ""
msgstr "" msgstr ""
"Language: en_US" "Language: en_US\n"
"Content-Type: text/plain; charset=UTF-8" "Content-Type: text/plain; charset=UTF-8\n"
"Plural-Forms: nplurals=2; plural=(n != 1);" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "ui.test" msgid "ui.test"
msgstr "Hello this is a test." msgstr "Hello this is a test."
@@ -12,4 +13,3 @@ msgstr "This is a map test."
msgid "test.test2" msgid "test.test2"
msgstr "This is another test." msgstr "This is another test."

View File

@@ -1,14 +1,14 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from PyQt5.QtWidgets import QMainWindow, QApplication, QAction, QMenuBar, QMessageBox, QFileDialog, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QTableWidget, QTableWidgetItem, QHeaderView, QPushButton from PyQt5.QtWidgets import QMainWindow, QApplication, QAction, QMenuBar, QMessageBox, QFileDialog, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QTableWidget, QTableWidgetItem, QHeaderView, QPushButton, QTabWidget, QFormLayout
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
import sys import sys
import os import os
import polib
class LangToolWindow(QMainWindow): class LangToolWindow(QMainWindow):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.setWindowTitle("Dusk Language Editor") self.setWindowTitle("Dusk Language Editor")
self.showMaximized()
self.setGeometry(100, 100, 800, 600) self.setGeometry(100, 100, 800, 600)
self.current_file = None self.current_file = None
self.dirty = False self.dirty = False
@@ -19,35 +19,40 @@ class LangToolWindow(QMainWindow):
central = QWidget() central = QWidget()
layout = QVBoxLayout(central) layout = QVBoxLayout(central)
# Header editor tabs = QTabWidget()
header_layout = QHBoxLayout() # Header Tab
header_layout.addWidget(QLabel("Language:")) header_tab = QWidget()
header_layout = QFormLayout(header_tab)
self.language_edit = QLineEdit() self.language_edit = QLineEdit()
header_layout.addWidget(self.language_edit) self.language_edit.setMaximumWidth(220)
header_layout.addWidget(QLabel("Plural-Forms:")) header_layout.addRow(QLabel("Language:"), self.language_edit)
self.plural_edit = QLineEdit() self.plural_edit = QLineEdit()
header_layout.addWidget(self.plural_edit) self.plural_edit.setMaximumWidth(320)
header_layout.addWidget(QLabel("Content-Type:")) header_layout.addRow(QLabel("Plural-Forms:"), self.plural_edit)
self.content_type_edit = QLineEdit("text/plain; charset=UTF-8") self.content_type_edit = QLineEdit("text/plain; charset=UTF-8")
header_layout.addWidget(self.content_type_edit) self.content_type_edit.setMaximumWidth(320)
layout.addLayout(header_layout) header_layout.addRow(QLabel("Content-Type:"), self.content_type_edit)
tabs.addTab(header_tab, "Header")
# PO entries editor # Strings Tab
strings_tab = QWidget()
strings_layout = QVBoxLayout(strings_tab)
self.po_table = QTableWidget() self.po_table = QTableWidget()
self.po_table.setColumnCount(2) self.po_table.setColumnCount(2)
self.po_table.setHorizontalHeaderLabels(["msgid", "msgstr"]) self.po_table.setHorizontalHeaderLabels(["msgid", "msgstr"])
self.po_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.po_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.po_table.verticalHeader().setMinimumWidth(22) self.po_table.verticalHeader().setMinimumWidth(22)
self.po_table.verticalHeader().setDefaultAlignment(Qt.AlignRight | Qt.AlignVCenter) self.po_table.verticalHeader().setDefaultAlignment(Qt.AlignRight | Qt.AlignVCenter)
layout.addWidget(self.po_table) strings_layout.addWidget(self.po_table)
# Row add/remove buttons
row_btn_layout = QHBoxLayout() row_btn_layout = QHBoxLayout()
add_row_btn = QPushButton("Add Row") add_row_btn = QPushButton("Add Row")
remove_row_btn = QPushButton("Remove Row") remove_row_btn = QPushButton("Remove Row")
row_btn_layout.addWidget(add_row_btn) row_btn_layout.addWidget(add_row_btn)
row_btn_layout.addWidget(remove_row_btn) row_btn_layout.addWidget(remove_row_btn)
layout.addLayout(row_btn_layout) strings_layout.addLayout(row_btn_layout)
tabs.addTab(strings_tab, "Strings")
layout.addWidget(tabs)
add_row_btn.clicked.connect(self.add_row) add_row_btn.clicked.connect(self.add_row)
remove_row_btn.clicked.connect(self.remove_row) remove_row_btn.clicked.connect(self.remove_row)
@@ -121,51 +126,34 @@ class LangToolWindow(QMainWindow):
self.load_po_file(file_path) self.load_po_file(file_path)
def load_po_file(self, file_path): def load_po_file(self, file_path):
language = "" po = polib.pofile(file_path)
plural = "" language = po.metadata.get('Language', '')
entries = [] plural = po.metadata.get('Plural-Forms', '')
with open(file_path, "r", encoding="utf-8") as f: content_type = po.metadata.get('Content-Type', 'text/plain; charset=UTF-8')
lines = f.readlines()
i = 0
while i < len(lines):
line = lines[i].strip()
if line.startswith('"Language:'):
language = line.split(':',1)[1].strip('"\n ')
elif line.startswith('"Plural-Forms:'):
plural = line.split(':',1)[1].strip('"\n ')
elif line.startswith('msgid'):
msgid = line[6:].strip('"')
i += 1
msgstr = lines[i].strip()[7:].strip('"') if i < len(lines) and lines[i].strip().startswith('msgstr') else ""
entries.append((msgid, msgstr))
i += 1
self.language_edit.setText(language) self.language_edit.setText(language)
self.plural_edit.setText(plural) self.plural_edit.setText(plural)
self.po_table.setRowCount(len(entries)) self.content_type_edit.setText(content_type)
for row, (msgid, msgstr) in enumerate(entries): self.po_table.setRowCount(len(po))
self.po_table.setItem(row, 0, QTableWidgetItem(msgid)) for row, entry in enumerate(po):
self.po_table.setItem(row, 1, QTableWidgetItem(msgstr)) self.po_table.setItem(row, 0, QTableWidgetItem(entry.msgid))
self.po_table.setItem(row, 1, QTableWidgetItem(entry.msgstr))
def save_file(self, file_path): def save_file(self, file_path):
language = self.language_edit.text() po = polib.POFile()
plural = self.plural_edit.text() po.metadata = {
content_type = self.content_type_edit.text() 'Language': self.language_edit.text(),
entries = [] 'Content-Type': self.content_type_edit.text(),
'Plural-Forms': self.plural_edit.text(),
}
for row in range(self.po_table.rowCount()): for row in range(self.po_table.rowCount()):
msgid = self.po_table.item(row, 0) msgid_item = self.po_table.item(row, 0)
msgstr = self.po_table.item(row, 1) msgstr_item = self.po_table.item(row, 1)
msgid_text = msgid.text() if msgid else "" msgid = msgid_item.text() if msgid_item else ''
msgstr_text = msgstr.text() if msgstr else "" msgstr = msgstr_item.text() if msgstr_item else ''
if msgid_text or msgstr_text: if msgid or msgstr:
entries.append((msgid_text, msgstr_text)) entry = polib.POEntry(msgid=msgid, msgstr=msgstr)
with open(file_path, "w", encoding="utf-8") as f: po.append(entry)
f.write('msgid ""\nmsgstr ""\n') po.save(file_path)
f.write(f'"Language: {language}"\n')
f.write(f'"Content-Type: {content_type}"\n')
f.write(f'"Plural-Forms: {plural}"\n')
f.write('\n')
for msgid, msgstr in entries:
f.write(f'msgid "{msgid}"\nmsgstr "{msgstr}"\n\n')
self.dirty = False self.dirty = False
self.update_save_action() self.update_save_action()
@@ -197,6 +185,16 @@ class LangToolWindow(QMainWindow):
self.po_table.removeRow(row) self.po_table.removeRow(row)
self.set_dirty() self.set_dirty()
def closeEvent(self, event):
if self.dirty:
reply = QMessageBox.question(self, "Save Changes?", "Do you want to save changes before exiting?", QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
if reply == QMessageBox.Cancel:
event.ignore()
return
elif reply == QMessageBox.Yes:
self.handle_save()
event.accept()
if __name__ == "__main__": if __name__ == "__main__":
app = QApplication(sys.argv) app = QApplication(sys.argv)
window = LangToolWindow() window = LangToolWindow()

View File

@@ -0,0 +1,105 @@
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QOpenGLWidget
from OpenGL.GL import *
from OpenGL.GLU import *
class GLWidget(QOpenGLWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.xRot = 20.0
self.yRot = 30.0
self.zRot = 0.0
self.rotation_speed = 2.0
self.timer = QTimer(self)
self.timer.timeout.connect(self.updateRotation)
def initializeGL(self):
version = glGetString(GL_VERSION)
print("GL version:", version)
glClearColor(0.1, 0.1, 0.1, 1.0)
glEnable(GL_DEPTH_TEST)
glShadeModel(GL_SMOOTH)
glEnable(GL_COLOR_MATERIAL)
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)
light_position = [4.0, 4.0, 8.0, 1.0]
glLightfv(GL_LIGHT0, GL_POSITION, light_position)
def resizeGL(self, w, h):
if h == 0:
h = 1
glViewport(0, 0, w, h)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glLoadIdentity()
gluPerspective(45.0, float(w) / float(h), 0.1, 100.0)
glMatrixMode(GL_MODELVIEW)
def paintGL(self):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
glTranslatef(0.0, 0.0, -7.0)
glRotatef(self.xRot, 1.0, 0.0, 0.0)
glRotatef(self.yRot, 0.0, 1.0, 0.0)
glRotatef(self.zRot, 0.0, 0.0, 1.0)
self.drawCube()
def drawCube(self):
glBegin(GL_QUADS)
# Front (red)
glColor3f(1.0, 0.0, 0.0)
glVertex3f(-1.0, -1.0, 1.0)
glVertex3f( 1.0, -1.0, 1.0)
glVertex3f( 1.0, 1.0, 1.0)
glVertex3f(-1.0, 1.0, 1.0)
# Back (green)
glColor3f(0.0, 1.0, 0.0)
glVertex3f(-1.0, -1.0, -1.0)
glVertex3f(-1.0, 1.0, -1.0)
glVertex3f( 1.0, 1.0, -1.0)
glVertex3f( 1.0, -1.0, -1.0)
# Top (blue)
glColor3f(0.0, 0.0, 1.0)
glVertex3f(-1.0, 1.0, -1.0)
glVertex3f(-1.0, 1.0, 1.0)
glVertex3f( 1.0, 1.0, 1.0)
glVertex3f( 1.0, 1.0, -1.0)
# Bottom (yellow)
glColor3f(1.0, 1.0, 0.0)
glVertex3f(-1.0, -1.0, -1.0)
glVertex3f( 1.0, -1.0, -1.0)
glVertex3f( 1.0, -1.0, 1.0)
glVertex3f(-1.0, -1.0, 1.0)
# Right (magenta)
glColor3f(1.0, 0.0, 1.0)
glVertex3f( 1.0, -1.0, -1.0)
glVertex3f( 1.0, 1.0, -1.0)
glVertex3f( 1.0, 1.0, 1.0)
glVertex3f( 1.0, -1.0, 1.0)
# Left (cyan)
glColor3f(0.0, 1.0, 1.0)
glVertex3f(-1.0, -1.0, -1.0)
glVertex3f(-1.0, -1.0, 1.0)
glVertex3f(-1.0, 1.0, 1.0)
glVertex3f(-1.0, 1.0, -1.0)
glEnd()
def startRotation(self):
if not self.timer.isActive():
self.timer.start(30)
def stopRotation(self):
if self.timer.isActive():
self.timer.stop()
def resetView(self):
self.xRot = 20.0
self.yRot = 30.0
self.zRot = 0.0
self.update()
def updateRotation(self):
self.xRot += self.rotation_speed
self.yRot += self.rotation_speed * 0.8
self.zRot += self.rotation_speed * 0.5
self.update()

View File

@@ -4,141 +4,16 @@ from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QApplication, QMainWindow, QWidget,
QHBoxLayout, QVBoxLayout, QPushButton, QHBoxLayout, QVBoxLayout, QPushButton,
QLabel, QSlider, QOpenGLWidget, QDialog, QRadioButton, QDialogButtonBox, QLabel, QSlider, QDialog, QRadioButton, QDialogButtonBox,
QAction, QFileDialog, QLineEdit QAction, QFileDialog, QLineEdit
) )
from OpenGL.GL import * from editortool.map.glwidget import GLWidget
from OpenGL.GLU import *
class GLWidget(QOpenGLWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.xRot = 20.0
self.yRot = 30.0
self.zRot = 0.0
self.rotation_speed = 2.0
self.timer = QTimer(self)
self.timer.timeout.connect(self.updateRotation)
# ---------- OpenGL lifecycle ----------
def initializeGL(self):
# Check that context is valid
version = glGetString(GL_VERSION)
print("GL version:", version)
glClearColor(0.1, 0.1, 0.1, 1.0)
glEnable(GL_DEPTH_TEST)
glShadeModel(GL_SMOOTH)
glEnable(GL_COLOR_MATERIAL)
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)
light_position = [4.0, 4.0, 8.0, 1.0]
glLightfv(GL_LIGHT0, GL_POSITION, light_position)
def resizeGL(self, w, h):
if h == 0:
h = 1
glViewport(0, 0, w, h)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glLoadIdentity()
gluPerspective(45.0, float(w) / float(h), 0.1, 100.0)
glMatrixMode(GL_MODELVIEW)
def paintGL(self):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
glTranslatef(0.0, 0.0, -7.0)
glRotatef(self.xRot, 1.0, 0.0, 0.0)
glRotatef(self.yRot, 0.0, 1.0, 0.0)
glRotatef(self.zRot, 0.0, 0.0, 1.0)
self.drawCube()
# ---------- 3D object ----------
def drawCube(self):
glBegin(GL_QUADS)
# Front (red)
glColor3f(1.0, 0.0, 0.0)
glVertex3f(-1.0, -1.0, 1.0)
glVertex3f( 1.0, -1.0, 1.0)
glVertex3f( 1.0, 1.0, 1.0)
glVertex3f(-1.0, 1.0, 1.0)
# Back (green)
glColor3f(0.0, 1.0, 0.0)
glVertex3f(-1.0, -1.0, -1.0)
glVertex3f(-1.0, 1.0, -1.0)
glVertex3f( 1.0, 1.0, -1.0)
glVertex3f( 1.0, -1.0, -1.0)
# Top (blue)
glColor3f(0.0, 0.0, 1.0)
glVertex3f(-1.0, 1.0, -1.0)
glVertex3f(-1.0, 1.0, 1.0)
glVertex3f( 1.0, 1.0, 1.0)
glVertex3f( 1.0, 1.0, -1.0)
# Bottom (yellow)
glColor3f(1.0, 1.0, 0.0)
glVertex3f(-1.0, -1.0, -1.0)
glVertex3f( 1.0, -1.0, -1.0)
glVertex3f( 1.0, -1.0, 1.0)
glVertex3f(-1.0, -1.0, 1.0)
# Right (magenta)
glColor3f(1.0, 0.0, 1.0)
glVertex3f( 1.0, -1.0, -1.0)
glVertex3f( 1.0, 1.0, -1.0)
glVertex3f( 1.0, 1.0, 1.0)
glVertex3f( 1.0, -1.0, 1.0)
# Left (cyan)
glColor3f(0.0, 1.0, 1.0)
glVertex3f(-1.0, -1.0, -1.0)
glVertex3f(-1.0, -1.0, 1.0)
glVertex3f(-1.0, 1.0, 1.0)
glVertex3f(-1.0, 1.0, -1.0)
glEnd()
# ---------- Animation control ----------
def startRotation(self):
if not self.timer.isActive():
self.timer.start(30) # ms
def stopRotation(self):
if self.timer.isActive():
self.timer.stop()
def resetView(self):
self.xRot = 20.0
self.yRot = 30.0
self.zRot = 0.0
self.update()
def updateRotation(self):
self.xRot += self.rotation_speed
self.yRot += self.rotation_speed * 0.8
self.zRot += self.rotation_speed * 0.5
self.update()
class MapWindow(QMainWindow): class MapWindow(QMainWindow):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.setWindowTitle("3D Cube with GUI Buttons (QOpenGLWidget)") self.setWindowTitle("Dusk Map Editor")
self.resize(1280, 720) self.resize(1280, 720)
# File menu setup # File menu setup