LINUXTALKS.CO

Project T - новый проект лаборатории на Python + PyQt6

 , , , ,

L


0

1

Котаны, приветствую!

Спешу сообщить, что Лаборатория свободных технологих объявляет о старте нового проекта с кодовым названием «Project T». Он будет использовать таблицы для хранения данных. Как только будет готова первая версия, прикручу SQLite-базу.
Исходный код будет открыт. Лицензия GPLv2. Для оффтопика буду распространять в виде exe-файла полноценной программы. Так что все смогут принять участие в её тестировании.
Так же я должен буду изучить способы автотетирования с использованием Python’а, на этом проекте.
Программу буду использовать в своём сервисе. Так же она послужит портфолио, если буду устраиваться на работу на дядю тестировщиком или кодером, если клюентов у моего сервиса не будет.
Потом подумаем вместе, какое название ей придумать с использованием буквы ё - это обязательно.

Есть ли понятие класса в Python’e? Мне сразу проектировать его как ООП или в скриптовом языке это невозможно?

★★★★★★
Ответ на: комментарий от kevlarbeaver

У меня есть 5 проектов: Кроссворд на С++, электронная библиотека и система тестирования школьников, кроссворд на lazarus и прога для статических сайтов. Это новый проЭкт.

xwicked
★★★★★★
Windows / Firefox (LT)

В инете пишут про создание панели главного меню руками, а как обраться к главному меню из ui-формы? Вижу, что активно используют так называемый Sender() с указателем того, кто отправляет сигнал. Так вот как правильно всё это привязать?

xwicked
★★★★★★
Windows / Firefox (LT)
Ответ на: комментарий от Harald

чому не на сишечке или цепепе

В прошлой теме написал, что мне юную самочку нужно научить кодингу и этот проект, как обучающий материал. Ей в школе преподавали python, поэтому я не хочу травмировать её психику c++. :D

xwicked
★★★★★★
Последнее исправление: xwicked (всего исправлений: 1)

Android / Firefox (PL)
Ответ на: комментарий от MrSugoma2

Я относительно недавно прозрел и понял, что Python является злом и должен быть запрещен.

Согласен, но мне нужно на собственном примере убедиться, насколько всё плохо.

xwicked
★★★★★★
Android / Firefox (PL)
Ответ на: комментарий от cocucka

Никогда не знаешь, как подействуют грибы, которые он жрёт перед началом разработки

Когда я говорил про скорый выпуск следующей версии кроссворда, то у меня ничего не получалось. Чтобы не спугнуть, пока не говорю что это, а если получится и будет время для разработки, то расскажу. :ь

xwicked
★★★★★★
Windows / Firefox (NL)
Ответ на: комментарий от cocucka

Прозреваю ERP или ещё какую мамкину бухгалтерию, например, учёт еды доя рецептов.

Вполне себе развитие электронной библиотеки.



Android / Firefox (T1)
from PyQt6 import QtWidgets, QtCore
import sys, os, ui_mainwindow, TAboutProgram_ui

app = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QMainWindow()
uiMain = ui_mainwindow.Ui_MainWindow()
uiAbout = TAboutProgram_ui.Ui_TAboutProgram
uiMain.setupUi(window)
uiMain.pushButton.clicked.connect(QtWidgets.QApplication.instance().quit)
#def sExitProgram(self): QtWidgets.QApplication.instance().quit
selectedAction = app.sender()
uiMain.menubar.actionAt((QtCore.QPoint(selectedAction.objectName))).triggered.connect(QtWidgets.QApplication.instance().quit)
window.show()
sys.exit(app.exec())

Мне нужно до начала программы посвязывать все пункты меню со слотами. sender() и self, я так понимаю не могу использовать, так как это объекты вермени выполнения?

xwicked
★★★★★★
Последнее исправление: xwicked (всего исправлений: 1)

Windows / Firefox (LT)
uiMain.menubar.actions()[0].setText("Yes!")

Первый прогресс. Это сработало. Теперь нужно как-то вычислять индекс нажатого меню и могу перейти к связыванию со слотами.

xwicked
★★★★★★
Последнее исправление: xwicked (всего исправлений: 1)

Windows / Firefox (LT)
**  main.py - Главный файл программы.
**  Является частью программы Project T, распространяемой под
**  Стандартной общественной лицензией, версии 2 (GNU GPL v2).
** ======================================================================
**  Copyleft 2026 by Lab Free Technologies <[email protected]>
** ==============================================================================
**  Copyleft 2026  Лаборатория свободных технологий <[email protected]>
** ==============================================================================
*/
from PyQt6 import QtWidgets, QtCore
import sys, os, mainwindow_ui, TAboutProgram_ui

class uiMain (QtWidgets.QMainWindow, mainwindow_ui.Ui_MainWindow):
    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)
        self.setupUi(self);

    def slotQuit(self):
        QtCore.QCoreApplication.exit()

app = QtWidgets.QApplication(sys.argv)
window = uiMain()
window.show()
sys.exit(app.exec())

Первый рабочий код. Слоты сделал в дизайнере и связываю их с исполняемой частью уже в коде.
Установил Pyinstaller и сделал исполняемый файл, но не могу побороть зависимости libc и ld-linux-x86-64.so.2. С libc проблему решил:

DIR=`pwd`
LD_LYBRARY_PATH=$LD_LYBRARY_PATH:$DIR
export LD_LYBRARY_PATH

А ld-linux-x86-64.so.2 не видит, лезет в /lib64.

xwicked
★★★★★★
Windows / Firefox (AE)
# ======================================================================
#   main.py - Главный файл программы.
#   Является частью программы Project T, распространяемой под
#   Стандартной общественной лицензией, версии 2 (GNU GPL v2).
# ======================================================================
#   Copyleft 2026 by Lab Free Technologies <[email protected]>
# ==============================================================================
#   Copyleft 2026  Лаборатория свободных технологий <[email protected]>
# ==============================================================================
#

# from PyQt6 import QtWidgets, QtCore
from PyQt6.QtCore import QCoreApplication
from PyQt6.QtCore import QDate
from PyQt6.QtCore import QTime
from PyQt6.QtWidgets import QApplication
from PyQt6.QtWidgets import QDateEdit
from PyQt6.QtWidgets import QTimeEdit
from PyQt6.QtWidgets import QMainWindow
from PyQt6.QtWidgets import QWidget
from PyQt6.QtWidgets import QTableWidgetItem
import sys, os, time, mainwindow_ui, TAboutProgram_ui

app = QApplication(sys.argv)

class uiAbout (QWidget, TAboutProgram_ui.Ui_TAboutProgram):
    def __init__(self):
        QWidget.__init__(self)
        self.setupUi(self);

wAbout = uiAbout()

class uiMain (QMainWindow, mainwindow_ui.Ui_MainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setupUi(self);

    def slotQuit(self):
        QCoreApplication.exit()

    def slotAbout(self):
        wAbout.show()

    def slotSave(self):
        arr = [],[]
        iHours = 0
        iTotal = 1
        with open("table.txt", "wt") as fTable:
            for i in range(self.tableWidget.rowCount()):
                arr[iHours].append(self.tableWidget.item(i,iHours).text())
                print(arr[iHours][i])
                print(arr[iHours][i], file = fTable)
            for i in range(self.tableWidget.rowCount()):
                arr[iTotal].append(self.tableWidget.item(i,iTotal).text())
                print(arr[iTotal][i])
                print(arr[iTotal][i], file = fTable)
                
    def slotOpen(self):
        arr = [],[]
        iHours = 0
        iTotal = 1
        vFile = None
        with open("table.txt", "rt") as fTable:
            for i in range(self.tableWidget.rowCount()):
                arr[iHours].append(fTable.readline())
                self.tableWidget.item(i,iHours).setText(arr[iHours][i].strip())
                #print(arr[iHours][i])
            for i in range(self.tableWidget.rowCount()):
                arr[iTotal].append(fTable.readline())
                self.tableWidget.item(i,iTotal).setText(arr[iTotal][i].strip())
                #print(arr[iTotal][i])

    def slotCellChanged(self, i, j):
        iCostHours = int(self.leCostHours.text())
        iTotal = 1
        s = self.tableWidget.item(i, j).text()
        if s.isdigit() == True:
            if int(s) > 15:
                s = str(15)
                self.tableWidget.item(i, j).setText(s)
            s = str(int(s) * iCostHours)
            print(s)
            print(i)
            print(iTotal)
            print(j)
            #self.tableWidget.item(i, j).setText(s)#эта строка вызыывет сегфолт :(
        else:
            self.tableWidget.item(i, j).setText("")

    def slotCellClicked(self, i, j):
        iHours = 0
        iTotal = 1
        if j == iTotal:
            self.tableWidget.setCurrentCell(i, iHours)

wMain = uiMain()
wMain.show()
wMain.tableWidget.resizeColumnsToContents()
wMain.tableWidget.resizeRowsToContents()

for i in range(wMain.tableWidget.rowCount()):
    for j in range(wMain.tableWidget.columnCount()):
        wMain.tableWidget.setItem(i,j, QTableWidgetItem())

currentTime = time.localtime()
print(currentTime.tm_year, ":", currentTime.tm_mon, ":", currentTime.tm_mday)
wMain.dateEdit.setDate(QDate(currentTime.tm_year,currentTime.tm_mon,currentTime.tm_mday))
print(currentTime.tm_hour, ":", currentTime.tm_min, ":", currentTime.tm_sec)
wMain.timeEdit.setTime(QTime(currentTime.tm_hour,currentTime.tm_min))
sys.exit(app.exec())

Пытаюсь обработать изменение ячейки таблицы и почему-то вылетает сегфолт в slotCellChanged(self, i, j): Что я упустил?

xwicked
★★★★★★
Последнее исправление: xwicked (всего исправлений: 1)

Windows / Firefox (AE)
Ответ на: комментарий от Anoxemian

Стив Баллмер врывается на сцену и, прыгая в диком угаре, орет: «recursion, recursion, recursion!!!!!»

Да, я походу это понял и теперь пытаюсь эту рекурсию побороть глобальной переменной со счётчиком, но не могу никак её объявить. Во всех местах уже пихал - не видит. Думает что она локальная и всё.

iCount = iCount + 1
             ^^^^^^
UnboundLocalError: cannot access local variable 'iCount' where it is not associated with a value
xwicked
★★★★★★
Windows / Firefox (AE)
Ответ на: комментарий от Anoxemian
   def slotCellChanged(self, i, j):
        iHours = 0
        iTotal = 1
        iCostHours = int(self.leCostHours.text())
        s = self.tableWidget.item(i, iHours).text()
        if s.isdigit() == True:
            if int(s) > 15:
                s = str(15)
                self.tableWidget.item(i, iHours).setText(s)
            s = str(int(s) * iCostHours)
            if self.iCount < 2:
                self.iCount = self.iCount + 1
                self.tableWidget.item(i, iTotal).setText(s)
        else:
            self.tableWidget.item(i, j).setText("")

    def slotCellClicked(self, i, j):
        self.iCount = 0
        iHours = 0
        iTotal = 1
        if j == iTotal:
            self.tableWidget.setCurrentCell(i, iHours)

Под конец дня победил глобальной переменной iCount. Обращение через self почему-то. :D

xwicked
★★★★★★
Windows / Firefox (AE)
Ответ на: комментарий от Holger

если таблица то можно использовать csv

csv точно не будет. Мы с ученицей сначала освоим текстовые файлы, потом бинарные, а потом я всё в SQLite3-базу зафигачу.

xwicked
★★★★★★
Windows / Firefox (AE)

Я нашёл отладчик! В конфигурацию запуска, нужно добавить

python -m pdb %{CurrentDocument:FilePath}

python.exe - в нём встроен отладчик. Тупо запуск с параметрами. :)

xwicked
★★★★★★
Windows / Firefox (AE)


Народ, а как мне правильно настроить QtCreator + python + QyQt6, чтобы автодополнения работали и чтобы в классы заходил и показывал все переменные. Допустим, я создал wDialog QFileDialog(), а он мне показал, какие свойства и методы мне доступны. Это связано как-то с ClangCodeModel?
У меня этой шляпы не было, пока не включил CLangCodeModel, а когда включил оно появилось, но криво с сотней warning’ов и периодически отваливается.

xwicked
★★★★★★
Последнее исправление: xwicked (всего исправлений: 1)

Windows / Firefox (AE)
LanguageClient Сервер языка Python (Z:\python\python.exe): Неожиданное завершение. Перезапуск через 5 секунд.

Нашёл ошибку. Кто знает как починить? Переустановить через pip?

xwicked
★★★★★★
Windows / Firefox (AE)

Project T - программа Табель для учёта отработанных часов для моей абстрактной помощницы в вакууме, которую я планирую найти за 140р/час.

https://disk.yandex.ru/d/OUyLOY24h-jy3w - Исходник версии 0.1.0. Сейчас программа представляет собой одну таблицу в 31 строку, которые соответствуют дням месяца и 2 столбца(часы, всего денег за этот день). Поля: часовая ставка, Аванс, процент за сделанную работу, всего за 1 месяц. Дата, время потом удалю, не нашёл применение.
Справа лог, представляющий собой все действия по работе в программе(изменение ячеек и т.д.), чтобы можно было лещей раздавать, за добавление лишних часов.
Сохранение идёт с текстовые файлы построчно, обычной функцией «print».
Сейчас идёт отладка и добавление мелочей, типо: гашение пунктов меню и диалоги изменения документа. Пищу на оффтопике, но тестировать всё буду на физических железяках в GNU / Linux и MacOS.

xwicked
★★★★★★
Последнее исправление: xwicked (всего исправлений: 2)

Windows / Firefox (AE)

Текущий код:

from PyQt6.QtCore import QCoreApplication
from PyQt6.QtCore import QDate
from PyQt6.QtCore import QTime
from PyQt6.QtWidgets import QApplication
from PyQt6.QtWidgets import QMainWindow
from PyQt6.QtWidgets import QWidget
from PyQt6.QtWidgets import QTableWidgetItem
from PyQt6.QtWidgets import QFileDialog
from PyQt6.QtWidgets import QMessageBox

import sys
import time
import UImainwindow
import TAboutProgram_ui

app = QApplication(sys.argv)


class uiAbout (QWidget, TAboutProgram_ui.Ui_TAboutProgram):
    def __init__(self):
        QWidget.__init__(self)
        self.setupUi(self);


wAbout = uiAbout()


class uiMain (QMainWindow, UImainwindow.Ui_MainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setupUi(self);
    iCount = 0
    iHours = 0
    iTotal = 1
    bOpenFile = False
    sLastState = ""
    bSaveState = True
    sFileName = ""
    sTempFileName = ""


    def slotQuit(self):
        QCoreApplication.exit()

    def slotAbout(self):
        wAbout.show()

    def slotSaveAs(self):
        # wDialogSave = QFileDialog()
        # wDialogSave.setFileMode(QFileDialog.FileMode.AnyFiles)
        self.sTempFileName = QFileDialog.getSaveFileName(self, "Save file table", ".", "Text file (.txt)", "Any Files (*)")
        if self.sTempFileName[0] == "":
            return
        self.sTempFileName = self.sTempFileName[0]
        self.slotSave()

    def slotSave(self):
        arr = [], []
        if self.sTempFileName != "":
            sOpenFileName = self.sTempFileName
            self.sTempFileName = ""
        else:
            sOpenFileName = self.sFileName
        with open(sOpenFileName, "wt") as fTable:
            for i in range(self.tableWidget.rowCount()):
                arr[self.iHours].append(self.tableWidget.item(i,self.iHours).text())
                print(arr[self.iHours][i], file=fTable)
            for i in range(self.tableWidget.rowCount()):
                arr[self.iTotal].append(self.tableWidget.item(i,self.iTotal).text())
                print(arr[self.iTotal][i], file=fTable)
            print(self.leCostHours.text(), file=fTable)
            print(self.leExpense.text(), file=fTable)
            print(self.lePercent.text(), file=fTable)
            print(self.leTotal.text(), file=fTable)

    def slotOpen(self):
        self.bOpenFile = True
        self.sFileName = QFileDialog.getOpenFileName(self, "Open file table", ".", "Any Files (*)")
        if self.sFileName[0] == "":
            return
        self.sFileName = self.sFileName[0]
        self.setWindowTitle(self.sFileName + " - Project T")
        arr = [], []

        with open(self.sFileName, "rt") as fTable:
            for i in range(self.tableWidget.rowCount()):
                arr[self.iHours].append(fTable.readline())
                self.tableWidget.item(i, self.iHours).setText(arr[self.iHours][i].strip())
            for i in range(self.tableWidget.rowCount()):
                arr[self.iTotal].append(fTable.readline())
                self.tableWidget.item(i, self.iTotal).setText(arr[self.iTotal][i].strip())
            self.leCostHours.setText(fTable.readline().strip())
            self.leExpense.setText(fTable.readline().strip())
            self.lePercent.setText(fTable.readline().strip())
            self.leTotal.setText(fTable.readline().strip())
        self.bOpenFile = False
        self.actionClose.setEnabled(True)

    def calculateTotalMoney(self):
        arr = []
        iTotalMoney = 0
        for i in range(self.tableWidget.rowCount()):
            s = self.tableWidget.item(i, self.iTotal).text()
            if s.isdigit() == False:
                arr.append(0)
            else:
                arr.append(int(s))
            iTotalMoney = iTotalMoney + arr[i]
        return iTotalMoney

    def slotCellChanged(self, i, j):
        if self.bOpenFile == True:
            return
        self.bSaveState = False
        sCosthours = self.leCostHours.text()
        if sCosthours.isdigit():
            iCostHours = int(sCosthours)
        s = self.tableWidget.item(i, self.iHours).text()
        if s.isdigit() == True:
            if int(s) > 15:
                s = str(15)
                self.tableWidget.item(i, self.iHours).setText(s)
            s = str(int(s) * iCostHours)
            if self.iCount < 2:
                self.iCount = self.iCount + 1
                self.tableWidget.item(i, self.iTotal).setText(s)
        else:
            self.tableWidget.item(i, j).setText("")

        self.leTotal.setText(str(self.calculateTotalMoney()))
        sCurrentState = self.tableWidget.item(i, self.iHours).text()
        self.listWidget.addItem(self.sLastState + " часов \\ поменялось на: " + sCurrentState + " часов\\" + self.dateEdit.date().toString() + " \\" +
            self.timeEdit.time().toString() + "\\ В ячейке номер: " + str(i))
        wMain.actionSave.setEnabled(True)
        wMain.actionSaveAs.setEnabled(True)

    def slotCellClicked(self, i, j):
        self.iCount = 0
        if j == self.iTotal:
            self.tableWidget.setCurrentCell(i, self.iHours)
        self.sLastState = self.tableWidget.item(i, self.iHours).text()

    def slotCreate(self):
        for i in range(wMain.tableWidget.rowCount()):
           for j in range(wMain.tableWidget.columnCount()):
                self.tableWidget.item(i, j).setText("")
        self.leCostHours.setText("")
        self.leExpense.setText("")
        self.lePercent.setText("")
        self.leTotal.setText("")

    def slotClose(self):
        if self.bSaveState == False:
            msgBox = QMessageBox(self)
            msgBox.setText("The document has been modified.")
            msgBox.setInformativeText("Do you want to save your changes?")
            msgBox.setStandardButtons(QMessageBox.StandardButton.Save | QMessageBox.StandardButton.Discard | QMessageBox.StandardButton.Cancel)
            msgBox.setDefaultButton(QMessageBox.StandardButton.Save)
            msg = msgBox.exec()
            if msg == QMessageBox.StandardButton.Save:
                self.slotSave()
            elif msg == QMessageBox.StandardButton.Cancel:
                return
        for i in range(wMain.tableWidget.rowCount()):
            for j in range(wMain.tableWidget.columnCount()):
                self.tableWidget.item(i, j).setText("")
        self.leCostHours.setText("")
        self.leExpense.setText("")
        self.lePercent.setText("")
        self.leTotal.setText("")
        self.listWidget.clear()
        self.setWindowTitle("Project T")
        self.sFileName = ""
        self.bSaveState = True
        self.actionClose.setDisabled(True)
        self.actionSave.setDisabled(True)
        self.actionSaveAs.setDisabled(True)


wMain = uiMain()
iCount = 0
wMain.show()
wMain.tableWidget.resizeColumnsToContents()
wMain.tableWidget.resizeRowsToContents()

wMain.bOpenFile = True
for i in range(wMain.tableWidget.rowCount()):
    for j in range(wMain.tableWidget.columnCount()):
        wMain.tableWidget.setItem(i,j, QTableWidgetItem())
wMain.bOpenFile = False

currentTime = time.localtime()
wMain.dateEdit.setDate(QDate(currentTime.tm_year,currentTime.tm_mon,currentTime.tm_mday))
wMain.timeEdit.setTime(QTime(currentTime.tm_hour,currentTime.tm_min))
wMain.setWindowTitle("Project T")
wMain.actionClose.setDisabled(True)
wMain.actionSave.setDisabled(True)
wMain.actionSaveAs.setDisabled(True)
sys.exit(app.exec())
xwicked
★★★★★★
Windows / Firefox (AE)