From e8e9488806200056cc39664807916ed5aa706052 Mon Sep 17 00:00:00 2001 From: regeciovad Date: Thu, 22 Oct 2015 17:07:21 +0200 Subject: [PATCH 01/11] New class ThreadWrapper (#189) --- rpg/gui/thread.py | 62 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 rpg/gui/thread.py diff --git a/rpg/gui/thread.py b/rpg/gui/thread.py new file mode 100644 index 0000000..2531e11 --- /dev/null +++ b/rpg/gui/thread.py @@ -0,0 +1,62 @@ +from multiprocessing import Process, Queue +from threading import Thread +from io import StringIO +from contextlib import redirect_stdout + + +class _StringIO(StringIO): + """ Overwriting of StringIO for real time output caching. """ + + def __init__(self, buff): + super(_StringIO, self).__init__() + self.buff = buff + + def write(self, s): + super(_StringIO, self).write(s) + if s and s != '\n': + self.buff.put(s) + + +class ThreadWrapper(): + """ Unblocking way to connect functions with GUI for redirection + it's output in real time. """ + + def __init__(self, widget, func, *args): + """ Runs function in new process and add output + to GUI widget from thread with queue. """ + self._widget = widget + self._queue = Queue() + self._process = Process(target=self.wrapper, + args=(self._queue, func, args, )) + self._thread = Thread(target=self.writer) + self._running = False + + def run(self): + """ Starts the process and the thread. """ + if not self._running: + self._process.start() + self._thread.start() + self._running = True + + def kill(self): + """ Stops the process and the thread (there are no official + way to stop thread. This will unlocked thread and it will + be stopped with GUI - without error). """ + self._thread._tstate_lock = None + self._thread._stop() + self._thread.join() + self._process.terminate() + + @staticmethod + def wrapper(queue, func, args): + """ Redirects stdout and call function. + Output will be in queue.""" + f = _StringIO(queue) + with redirect_stdout(f): + func(*args) + f.close() + + def writer(self): + """ Appends output from function to the GUI widget. """ + while self._process.is_alive(): + self._widget.append(self._queue.get()) From c1fc5f08b010a7bc4ae2aeb70cd15b369a50f292 Mon Sep 17 00:00:00 2001 From: regeciovad Date: Sat, 24 Oct 2015 16:41:25 +0200 Subject: [PATCH 02/11] Gui - srpm build process is shown --- rpg/__init__.py | 6 +++++- rpg/gui/wizard.py | 40 ++++++++++++++++++++++++++++++++++------ 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/rpg/__init__.py b/rpg/__init__.py index 66e697c..d024f30 100644 --- a/rpg/__init__.py +++ b/rpg/__init__.py @@ -220,13 +220,17 @@ def write_spec(self): with open(str(self.spec_path), 'w') as spec_file: spec_file.write(str(self.spec)) - def build_srpm(self): + def build_srpm(self, path=None): """ Builds srpm into base directory. """ if not self.spec.Source or not self.archive_path.exists(): self.create_archive() self.write_spec() self._package_builder.build_srpm( self.spec_path, self.archive_path, self.base_dir) + if path: + Command("cp " + path_to_str(self.srpm_path) + " " + + str(path)).execute() + print ('SRPM pakcage was created.') def build_rpm(self, target_distro, target_arch): """ Build rpm from srpm. If srpm does not exists, diff --git a/rpg/gui/wizard.py b/rpg/gui/wizard.py index 26c8306..f71736a 100644 --- a/rpg/gui/wizard.py +++ b/rpg/gui/wizard.py @@ -2,15 +2,15 @@ from PyQt5 import QtWidgets, QtCore, QtGui from PyQt5.QtWidgets import (QLabel, QVBoxLayout, QLineEdit, QCheckBox, QGroupBox, QPushButton, QGridLayout, - QTextEdit, QFileDialog, + QTextEdit, QFileDialog, QDialog, QComboBox, QWizard, QFrame) from rpg.gui.dialogs import DialogImport -from rpg.utils import path_to_str from pathlib import Path from rpg.command import Command import subprocess import platform from threading import Thread +from rpg.gui.thread import ThreadWrapper class Wizard(QtWidgets.QWizard): @@ -60,7 +60,7 @@ def __init__(self, base, parent=None): self.setPage(self.PageCoprDistro, CoprDistroPage(self)) self.setPage(self.PageCoprBuild, CoprBuildPage(self)) self.setPage(self.PageCoprFinal, CoprFinalPage(self)) - self.setStartId(self.PageIntro) + self.setStartId(self.PageImport) class IntroPage(QtWidgets.QWizardPage): @@ -1022,10 +1022,38 @@ def openBuildPathFileDialog(self): def buildSrpm(self): self.textBuildSRPMLabel.setText('Building SRPM...') self.textBuildSRPMLabel.repaint() - self.base.build_srpm() - Command("cp " + path_to_str(self.base.srpm_path) + " " + - self.buildLocationEdit.text()).execute() self.base.final_path = self.buildLocationEdit.text() + + self.srpm_dialog = QDialog(self) + self.srpm_dialog.resize(600, 400) + self.srpm_dialog.setWindowTitle('Building SRPM') + self.srpm_progress = QTextEdit() + self.srpm_progress.setReadOnly(True) + self.srpm_progress.setText('Building SRPM...') + self.cancelButton = QPushButton('Cancel') + self.cancelButton.setMinimumHeight(45) + self.cancelButton.setMaximumHeight(45) + self.cancelButton.setMinimumWidth(100) + self.cancelButton.setMaximumWidth(115) + self.cancelButton.clicked.connect(self.CancelSRPM) + mainLayout = QVBoxLayout() + mainLayout.addSpacing(50) + mainLayout.addWidget(self.srpm_progress) + mainLayout.addSpacing(50) + grid = QGridLayout() + grid.addWidget(self.cancelButton) + mainLayout.addLayout(grid) + self.srpm_dialog.setLayout(mainLayout) + + self.srpm_dialog.show() + self.srpm_process = ThreadWrapper(self.srpm_progress, + self.base.build_srpm, + self.base.final_path) + self.srpm_process.run() + + def CancelSRPM(self): + self.srpm_dialog.close() + self.srpm_process.kill() self.textBuildSRPMLabel.setText('Your source package was build in ' + self.base.final_path) From 06eae99daf63dd5f3833ca07f3610b48b006b5e6 Mon Sep 17 00:00:00 2001 From: regeciovad Date: Mon, 26 Oct 2015 09:47:22 +0100 Subject: [PATCH 03/11] Gui - rpm build process is shown --- rpg/__init__.py | 12 ++++++++---- rpg/gui/wizard.py | 39 +++++++++++++++++++++++++++++++++------ rpg/package_builder.py | 1 + 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/rpg/__init__.py b/rpg/__init__.py index d024f30..9d3e2bb 100644 --- a/rpg/__init__.py +++ b/rpg/__init__.py @@ -229,10 +229,10 @@ def build_srpm(self, path=None): self.spec_path, self.archive_path, self.base_dir) if path: Command("cp " + path_to_str(self.srpm_path) + " " + - str(path)).execute() + str(path)).execute() print ('SRPM pakcage was created.') - def build_rpm(self, target_distro, target_arch): + def build_rpm(self, target_distro, target_arch, path=None): """ Build rpm from srpm. If srpm does not exists, it will be created. """ try: @@ -241,14 +241,18 @@ def build_rpm(self, target_distro, target_arch): self.build_srpm() self._package_builder.build_rpm( str(self.srpm_path), target_distro, target_arch, self.base_dir) + if path: + packages = self.rpm_path + for package in packages: + Command("cp " + str(package) + " " + path).execute() - def build_rpm_recover(self, distro, arch): + def build_rpm_recover(self, distro, arch, path=None): """ Repeatedly build rpm with mock and finds all build errors. May raise RuntimeError on failed recover. """ def build(): self.build_srpm() - self.build_rpm(distro, arch) + self.build_rpm(distro, arch, path) def analyse(): _files_to_pkgs.installed(self.base_dir, self.spec, self.sack) diff --git a/rpg/gui/wizard.py b/rpg/gui/wizard.py index f71736a..3056719 100644 --- a/rpg/gui/wizard.py +++ b/rpg/gui/wizard.py @@ -1059,15 +1059,42 @@ def CancelSRPM(self): def buildRpm(self): self.textBuildRPMLabel.setText('Building RPM...') - self.textBuildSRPMLabel.repaint() + self.textBuildRPMLabel.repaint() self.base.final_path = self.buildLocationEdit.text() + + self.rpm_dialog = QDialog(self) + self.rpm_dialog.resize(600, 400) + self.rpm_dialog.setWindowTitle('Building RPM') + self.rpm_progress = QTextEdit() + self.rpm_progress.setReadOnly(True) + self.rpm_progress.setText('Building RPM...') + self.cancelButton = QPushButton('Cancel') + self.cancelButton.setMinimumHeight(45) + self.cancelButton.setMaximumHeight(45) + self.cancelButton.setMinimumWidth(100) + self.cancelButton.setMaximumWidth(115) + self.cancelButton.clicked.connect(self.CancelRPM) + mainLayout = QVBoxLayout() + mainLayout.addSpacing(50) + mainLayout.addWidget(self.rpm_progress) + mainLayout.addSpacing(50) + grid = QGridLayout() + grid.addWidget(self.cancelButton) + mainLayout.addLayout(grid) + self.rpm_dialog.setLayout(mainLayout) + arch = self.BuildArchEdit.currentText() distro = self.BuildDistroEdit.currentText() - self.base.build_rpm_recover(distro, arch) - packages = self.base.rpm_path - for package in packages: - Command("cp " + str(package) + " " + - self.base.final_path).execute() + self.rpm_dialog.show() + self.rpm_process = ThreadWrapper(self.rpm_progress, + self.base.build_rpm_recover, + distro, arch, + self.base.final_path) + self.rpm_process.run() + + def CancelRPM(self): + self.rpm_dialog.close() + self.rpm_process.kill() self.textBuildRPMLabel.setText( 'Your package was build in ' + self.base.final_path) diff --git a/rpg/package_builder.py b/rpg/package_builder.py index ad39501..168ef09 100644 --- a/rpg/package_builder.py +++ b/rpg/package_builder.py @@ -83,6 +83,7 @@ def check_output(proc): line = proc.stdout.readline().decode("utf-8") if self._regex.search(line): yield line + print(line) self.build_ret_code = proc.returncode _ret = list( From c309b49ea48acc49bd68eb86da542fc105872226 Mon Sep 17 00:00:00 2001 From: regeciovad Date: Mon, 26 Oct 2015 09:53:45 +0100 Subject: [PATCH 04/11] Gui - cleanupPage in ImportPage --- rpg/gui/wizard.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/rpg/gui/wizard.py b/rpg/gui/wizard.py index 3056719..d2488a8 100644 --- a/rpg/gui/wizard.py +++ b/rpg/gui/wizard.py @@ -60,7 +60,7 @@ def __init__(self, base, parent=None): self.setPage(self.PageCoprDistro, CoprDistroPage(self)) self.setPage(self.PageCoprBuild, CoprBuildPage(self)) self.setPage(self.PageCoprFinal, CoprFinalPage(self)) - self.setStartId(self.PageImport) + self.setStartId(self.PageIntro) class IntroPage(QtWidgets.QWizardPage): @@ -235,16 +235,24 @@ def validatePage(self): self.base.target_distro = self.DistroEdit.currentText() self.base.load_project_from_url(self.importEdit.text().strip()) self.base.run_extracted_source_analysis() - new_thread = Thread( + self.new_thread = Thread( target=self.base.fetch_repos, args=(self.base.target_distro, self.base.target_arch)) - new_thread.start() + self.new_thread.start() self.importEdit.setStyleSheet("") return True else: self.importEdit.setStyleSheet(self.redQLineEdit) return False + def cleanupPage(self): + """ Stops the thread (there are no official way to stop thread. + This will unlocked thread and it will be stopped + with GUI - without error). """ + self.new_thread._tstate_lock = None + self.new_thread._stop() + self.new_thread.join() + def nextId(self): ''' [int] Function that determines the next page after the current one - returns integer value and then checks, which value is page" From 6367c2bd9906ae776aa2a7a81372bf6472902018 Mon Sep 17 00:00:00 2001 From: regeciovad Date: Wed, 28 Oct 2015 09:53:00 +0100 Subject: [PATCH 05/11] Gui - MandatoryPage's subtitle (fix #302) --- rpg/gui/wizard.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rpg/gui/wizard.py b/rpg/gui/wizard.py index d2488a8..9852f12 100644 --- a/rpg/gui/wizard.py +++ b/rpg/gui/wizard.py @@ -282,7 +282,10 @@ def __init__(self, Wizard, parent=None): "rgb(233,233,233);}") self.setTitle(self.tr(" Mandatory fields")) - self.setSubTitle(self.tr("Basic required information")) + self.setSubTitle(self.tr("Basic required information. " + + "Name, version and release " + + "will be used to create unique " + + "package name.")) self.nameLabel = QLabel("Name*") self.nameEdit = QLineEdit() From 32e20515c3c94bcb764cce05aeed9da8488af4ae Mon Sep 17 00:00:00 2001 From: regeciovad Date: Wed, 28 Oct 2015 11:57:03 +0100 Subject: [PATCH 06/11] Gui- new text widget (fix #348) --- rpg/gui/thread.py | 2 +- rpg/gui/wizard.py | 71 ++++++++++++++++++++++++----------------------- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/rpg/gui/thread.py b/rpg/gui/thread.py index 2531e11..c30b5ad 100644 --- a/rpg/gui/thread.py +++ b/rpg/gui/thread.py @@ -59,4 +59,4 @@ def wrapper(queue, func, args): def writer(self): """ Appends output from function to the GUI widget. """ while self._process.is_alive(): - self._widget.append(self._queue.get()) + self._widget.appendPlainText(self._queue.get()) diff --git a/rpg/gui/wizard.py b/rpg/gui/wizard.py index 9852f12..aadac09 100644 --- a/rpg/gui/wizard.py +++ b/rpg/gui/wizard.py @@ -2,7 +2,7 @@ from PyQt5 import QtWidgets, QtCore, QtGui from PyQt5.QtWidgets import (QLabel, QVBoxLayout, QLineEdit, QCheckBox, QGroupBox, QPushButton, QGridLayout, - QTextEdit, QFileDialog, QDialog, + QPlainTextEdit, QFileDialog, QDialog, QComboBox, QWizard, QFrame) from rpg.gui.dialogs import DialogImport from pathlib import Path @@ -39,7 +39,7 @@ def __init__(self, base, parent=None): QWizard.BackButton, QWizard.NextButton, QWizard.FinishButton]) self.setButtonLayout(btnList) - self.setStyleSheet("QTextEdit { border-style: solid;" + + self.setStyleSheet("QPlainTextEdit { border-style: solid;" + "border-width: 1px;" + "border-color: rgb(178, 182, 178);" + "border-radius: 3px;" + @@ -439,7 +439,7 @@ def __init__(self, Wizard, parent=None): "American English (required).")) self.descriptionLabel = QLabel("Description") - self.descriptionEdit = QTextEdit() + self.descriptionEdit = QPlainTextEdit() self.descriptionEdit.setMinimumHeight(30) self.descriptionLabel.setBuddy(self.descriptionEdit) self.descriptionLabelText = QLabel( @@ -492,9 +492,9 @@ def nextId(self): class ScriptsPage(QtWidgets.QWizardPage): def initializePage(self): - self.prepareEdit.setText(str(self.base.spec.prep)) - self.buildEdit.setText(str(self.base.spec.build)) - self.checkEdit.setText(str(self.base.spec.check)) + self.prepareEdit.insertPlainText(str(self.base.spec.prep)) + self.buildEdit.insertPlainText(str(self.base.spec.build)) + self.checkEdit.insertPlainText(str(self.base.spec.check)) def __init__(self, Wizard, parent=None): super(ScriptsPage, self).__init__(parent) @@ -513,7 +513,7 @@ def __init__(self, Wizard, parent=None): "

") prepareLabel = QLabel("%prepare: ") - self.prepareEdit = QTextEdit() + self.prepareEdit = QPlainTextEdit() prepareLabelText = QLabel( self.base.tip_html_style % ("Script commands to prepare the program (e.g. to " @@ -522,14 +522,14 @@ def __init__(self, Wizard, parent=None): "%autosetup -n NAME if the source file unpacks into NAME.")) buildLabel = QLabel("%build: ") - self.buildEdit = QTextEdit() + self.buildEdit = QPlainTextEdit() buildLabelText = QLabel( self.base.tip_html_style % ("Script commands to build the program (e.g. to compile it)" " and get it ready for installing.")) checkLabel = QLabel("%check: ") - self.checkEdit = QTextEdit() + self.checkEdit = QPlainTextEdit() checkLabelText = QLabel( self.base.tip_html_style % "Script commands to test the program.") @@ -601,10 +601,10 @@ def nextId(self): class InstallPage(QtWidgets.QWizardPage): def initializePage(self): - self.installEdit.setText(str(self.base.spec.install)) - self.pretransEdit.setText(str(self.base.spec.pretrans)) - self.preEdit.setText(str(self.base.spec.pre)) - self.postEdit.setText(str(self.base.spec.post)) + self.installEdit.insertPlainText(str(self.base.spec.install)) + self.pretransEdit.insertPlainText(str(self.base.spec.pretrans)) + self.preEdit.insertPlainText(str(self.base.spec.pre)) + self.postEdit.insertPlainText(str(self.base.spec.post)) def __init__(self, Wizard, parent=None): super(InstallPage, self).__init__(parent) @@ -622,25 +622,25 @@ def __init__(self, Wizard, parent=None): "

") pretransLabel = QLabel("%pretrans: ") - self.pretransEdit = QTextEdit() + self.pretransEdit = QPlainTextEdit() pretransLabelText = QLabel( self.base.tip_html_style % "At the start of transaction.") preLabel = QLabel("%pre: ") - self.preEdit = QTextEdit() + self.preEdit = QPlainTextEdit() preLabelText = QLabel( self.base.tip_html_style % "Before a package is installed.") installLabel = QLabel("%install: ") - self.installEdit = QTextEdit() + self.installEdit = QPlainTextEdit() installLabelText = QLabel( self.base.tip_html_style % "Script commands to install the program.") postLabel = QLabel("%post: ") - self.postEdit = QTextEdit() + self.postEdit = QPlainTextEdit() postLabelText = QLabel( self.base.tip_html_style % "After a package is installed.") @@ -708,9 +708,10 @@ def nextId(self): class RequiresPage(QtWidgets.QWizardPage): def initializePage(self): - self.bRequiresEdit.setText('\n'.join(self.base.spec.BuildRequires)) - self.requiresEdit.setText('\n'.join(self.base.spec.Requires)) - self.providesEdit.setText('\n'.join(self.base.spec.Provides)) + self.bRequiresEdit.insertPlainText( + '\n'.join(self.base.spec.BuildRequires)) + self.requiresEdit.insertPlainText('\n'.join(self.base.spec.Requires)) + self.providesEdit.insertPlainText('\n'.join(self.base.spec.Provides)) def __init__(self, Wizard, parent=None): super(RequiresPage, self).__init__(parent) @@ -721,7 +722,7 @@ def __init__(self, Wizard, parent=None): self.setSubTitle(self.tr("Write requires and provides")) buildRequiresLabel = QLabel("BuildRequires: ") - self.bRequiresEdit = QTextEdit() + self.bRequiresEdit = QPlainTextEdit() self.bRequiresEdit.setMaximumHeight(220) buildRequiresLabelText = QLabel( self.base.tip_html_style % @@ -735,7 +736,7 @@ def __init__(self, Wizard, parent=None): "

") requiresLabel = QLabel("Requires: ") - self.requiresEdit = QTextEdit() + self.requiresEdit = QPlainTextEdit() self.requiresEdit.setMaximumHeight(220) requiresLabelText = QLabel( self.base.tip_html_style % @@ -743,7 +744,7 @@ def __init__(self, Wizard, parent=None): "when the program is installed.")) providesLabel = QLabel("Provides: ") - self.providesEdit = QTextEdit() + self.providesEdit = QPlainTextEdit() providesLabelText = QLabel( self.base.tip_html_style % "List virtual package names that this package provides.") @@ -801,9 +802,9 @@ def nextId(self): class UninstallPage(QtWidgets.QWizardPage): def initializePage(self): - self.postunEdit.setText(str(self.base.spec.postun)) - self.preunEdit.setText(str(self.base.spec.preun)) - self.posttransEdit.setText(str(self.base.spec.posttrans)) + self.postunEdit.insertPlainText(str(self.base.spec.postun)) + self.preunEdit.insertPlainText(str(self.base.spec.preun)) + self.posttransEdit.insertPlainText(str(self.base.spec.posttrans)) def __init__(self, Wizard, parent=None): super(UninstallPage, self).__init__(parent) @@ -820,19 +821,19 @@ def __init__(self, Wizard, parent=None): "and what to do after uninstallation.

") preunLabel = QLabel("%preun: ") - self.preunEdit = QTextEdit() + self.preunEdit = QPlainTextEdit() preunLabelText = QLabel( self.base.tip_html_style % "Before a package is uninstalled.") postunLabel = QLabel("%postun: ") - self.postunEdit = QTextEdit() + self.postunEdit = QPlainTextEdit() postunLabelText = QLabel( self.base.tip_html_style % "After a package is uninstalled.") posttransLabel = QLabel("%posttrans: ") - self.posttransEdit = QTextEdit() + self.posttransEdit = QPlainTextEdit() posttransLabelText = QLabel( self.base.tip_html_style % "At the end of transaction.") @@ -1038,9 +1039,9 @@ def buildSrpm(self): self.srpm_dialog = QDialog(self) self.srpm_dialog.resize(600, 400) self.srpm_dialog.setWindowTitle('Building SRPM') - self.srpm_progress = QTextEdit() + self.srpm_progress = QPlainTextEdit() self.srpm_progress.setReadOnly(True) - self.srpm_progress.setText('Building SRPM...') + self.srpm_progress.insertPlainText('Building SRPM...') self.cancelButton = QPushButton('Cancel') self.cancelButton.setMinimumHeight(45) self.cancelButton.setMaximumHeight(45) @@ -1076,9 +1077,9 @@ def buildRpm(self): self.rpm_dialog = QDialog(self) self.rpm_dialog.resize(600, 400) self.rpm_dialog.setWindowTitle('Building RPM') - self.rpm_progress = QTextEdit() + self.rpm_progress = QPlainTextEdit() self.rpm_progress.setReadOnly(True) - self.rpm_progress.setText('Building RPM...') + self.rpm_progress.insertPlainText('Building RPM...') self.cancelButton = QPushButton('Cancel') self.cancelButton.setMinimumHeight(45) self.cancelButton.setMaximumHeight(45) @@ -1371,13 +1372,13 @@ def __init__(self, Wizard, parent=None): self.textBuildLabel = QLabel() self.packageDescLabel = QLabel("Description ") - self.packageDescEdit = QTextEdit() + self.packageDescEdit = QPlainTextEdit() self.packageDescLabelText = QLabel( self.base.tip_html_style % "Description for your package, optional.") self.packageInstuctionLabel = QLabel("Instructions ") - self.packageInstuctionEdit = QTextEdit() + self.packageInstuctionEdit = QPlainTextEdit() self.packageInstuctionLabelText = QLabel( self.base.tip_html_style % ("How install your project, where users can report bugs " From 6530d04f7dd9f100c1d6aa69d0dd52107a24e075 Mon Sep 17 00:00:00 2001 From: regeciovad Date: Wed, 4 Nov 2015 17:02:26 +0100 Subject: [PATCH 07/11] Gui - Loading cursor added (fix #361) --- rpg/gui/wizard.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/rpg/gui/wizard.py b/rpg/gui/wizard.py index aadac09..077159c 100644 --- a/rpg/gui/wizard.py +++ b/rpg/gui/wizard.py @@ -3,7 +3,7 @@ from PyQt5.QtWidgets import (QLabel, QVBoxLayout, QLineEdit, QCheckBox, QGroupBox, QPushButton, QGridLayout, QPlainTextEdit, QFileDialog, QDialog, - QComboBox, QWizard, QFrame) + QComboBox, QWizard, QFrame, QApplication) from rpg.gui.dialogs import DialogImport from pathlib import Path from rpg.command import Command @@ -479,6 +479,7 @@ def validatePage(self): else: self.base.spec.description = self.descriptionEdit.toPlainText() self.base.spec.Summary = self.summaryEdit.text() + QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.base.run_patched_source_analysis() return True @@ -495,6 +496,7 @@ def initializePage(self): self.prepareEdit.insertPlainText(str(self.base.spec.prep)) self.buildEdit.insertPlainText(str(self.base.spec.build)) self.checkEdit.insertPlainText(str(self.base.spec.check)) + QApplication.restoreOverrideCursor() def __init__(self, Wizard, parent=None): super(ScriptsPage, self).__init__(parent) @@ -590,6 +592,7 @@ def validatePage(self): self.base.spec.check = Command(self.checkEdit.toPlainText()) if self.buildArchCheckbox.isChecked(): self.base.spec.BuildArch = "noarch" + QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.base.build_project() self.base.run_compiled_source_analysis() return True @@ -605,6 +608,7 @@ def initializePage(self): self.pretransEdit.insertPlainText(str(self.base.spec.pretrans)) self.preEdit.insertPlainText(str(self.base.spec.pre)) self.postEdit.insertPlainText(str(self.base.spec.post)) + QApplication.restoreOverrideCursor() def __init__(self, Wizard, parent=None): super(InstallPage, self).__init__(parent) @@ -697,6 +701,7 @@ def validatePage(self): self.base.spec.pretrans = Command(self.pretransEdit.toPlainText()) self.base.spec.pre = Command(self.preEdit.toPlainText()) self.base.spec.post = Command(self.postEdit.toPlainText()) + QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.base.install_project() self.base.run_installed_source_analysis() return True @@ -712,6 +717,7 @@ def initializePage(self): '\n'.join(self.base.spec.BuildRequires)) self.requiresEdit.insertPlainText('\n'.join(self.base.spec.Requires)) self.providesEdit.insertPlainText('\n'.join(self.base.spec.Provides)) + QApplication.restoreOverrideCursor() def __init__(self, Wizard, parent=None): super(RequiresPage, self).__init__(parent) @@ -878,6 +884,7 @@ def validatePage(self): self.base.spec.postun = Command(self.postunEdit.toPlainText()) self.base.spec.preun = Command(self.preunEdit.toPlainText()) self.base.spec.posttrans = Command(self.posttransEdit.toPlainText()) + QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.base.write_spec() return True @@ -888,6 +895,7 @@ def nextId(self): class BuildPage(QtWidgets.QWizardPage): def initializePage(self): + QApplication.restoreOverrideCursor() self.buildLocationEdit.setText(expanduser("~")) self.distro = self.base.target_distro self.arch = self.base.target_arch @@ -1262,6 +1270,7 @@ def validatePage(self): self.base.coprpackageUrl = self.packageUrlEdit.text() self.base.coprlogin = self.loginEdit.text() self.base.coprtoken = self.tokenEdit.text() + QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.base.copr_set_config(self.base.coprusername, self.base.coprlogin, self.base.coprtoken) return True @@ -1272,6 +1281,9 @@ def nextId(self): class CoprDistroPage(QtWidgets.QWizardPage): + def initializePage(self): + QApplication.restoreOverrideCursor() + def __init__(self, Wizard, parent=None): super(CoprDistroPage, self).__init__(parent) @@ -1414,6 +1426,7 @@ def __init__(self, Wizard, parent=None): self.setLayout(mainLayout) def validatePage(self): + QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.textBuildLabel.setText( "

" + @@ -1463,6 +1476,7 @@ def nextId(self): class CoprFinalPage(QtWidgets.QWizardPage): def initializePage(self): + QApplication.restoreOverrideCursor() self.newproject = self.base.coprusername + \ "/" + self.base.coprpackageName self.webpage = "https://copr.fedoraproject.org/api/coprs/" + \ From 9533024723ec46992b411f640cd22e4f0e7c4b1e Mon Sep 17 00:00:00 2001 From: regeciovad Date: Wed, 25 Nov 2015 12:52:50 +0100 Subject: [PATCH 08/11] Gui - build info with SRPM, RPM and Copr (fix #189) --- rpg/__init__.py | 20 ++++++++- rpg/gui/thread.py | 1 + rpg/gui/wizard.py | 98 +++++++++++++++++++++--------------------- rpg/package_builder.py | 6 ++- 4 files changed, 74 insertions(+), 51 deletions(-) diff --git a/rpg/__init__.py b/rpg/__init__.py index 9d3e2bb..c6fe7c9 100644 --- a/rpg/__init__.py +++ b/rpg/__init__.py @@ -222,15 +222,16 @@ def write_spec(self): def build_srpm(self, path=None): """ Builds srpm into base directory. """ + print('Building SRPM...') if not self.spec.Source or not self.archive_path.exists(): self.create_archive() self.write_spec() + print('Spec file created.') self._package_builder.build_srpm( self.spec_path, self.archive_path, self.base_dir) if path: Command("cp " + path_to_str(self.srpm_path) + " " + str(path)).execute() - print ('SRPM pakcage was created.') def build_rpm(self, target_distro, target_arch, path=None): """ Build rpm from srpm. If srpm does not exists, @@ -282,6 +283,23 @@ def build_project(self): self.compiled_dir, self.spec.build) + def copr_create_and_build(self, name, chroots, desc, intro, url): + print("Creating new project...") + try: + self.copr_create_project(name, chroots, desc, intro) + except: + print("Error in creating project! Please check your login.") + return + print("Creating new project - DONE") + print("Build proccess started...") + print("It takes a while, but it may be safely interrupted.") + try: + self.copr_build(name, url) + except: + print("Error in building project! Please check your url.") + return + print("Building new project - DONE") + def copr_set_config(self, username, login, token): """ Logs into copr with username, login and token. This has to be called before copr_create_project and copr_build diff --git a/rpg/gui/thread.py b/rpg/gui/thread.py index c30b5ad..5f09fce 100644 --- a/rpg/gui/thread.py +++ b/rpg/gui/thread.py @@ -60,3 +60,4 @@ def writer(self): """ Appends output from function to the GUI widget. """ while self._process.is_alive(): self._widget.appendPlainText(self._queue.get()) + self._widget.repaint() diff --git a/rpg/gui/wizard.py b/rpg/gui/wizard.py index 077159c..18d3412 100644 --- a/rpg/gui/wizard.py +++ b/rpg/gui/wizard.py @@ -1049,7 +1049,6 @@ def buildSrpm(self): self.srpm_dialog.setWindowTitle('Building SRPM') self.srpm_progress = QPlainTextEdit() self.srpm_progress.setReadOnly(True) - self.srpm_progress.insertPlainText('Building SRPM...') self.cancelButton = QPushButton('Cancel') self.cancelButton.setMinimumHeight(45) self.cancelButton.setMaximumHeight(45) @@ -1057,9 +1056,9 @@ def buildSrpm(self): self.cancelButton.setMaximumWidth(115) self.cancelButton.clicked.connect(self.CancelSRPM) mainLayout = QVBoxLayout() - mainLayout.addSpacing(50) + mainLayout.addSpacing(40) mainLayout.addWidget(self.srpm_progress) - mainLayout.addSpacing(50) + mainLayout.addSpacing(40) grid = QGridLayout() grid.addWidget(self.cancelButton) mainLayout.addLayout(grid) @@ -1095,9 +1094,9 @@ def buildRpm(self): self.cancelButton.setMaximumWidth(115) self.cancelButton.clicked.connect(self.CancelRPM) mainLayout = QVBoxLayout() - mainLayout.addSpacing(50) + mainLayout.addSpacing(40) mainLayout.addWidget(self.rpm_progress) - mainLayout.addSpacing(50) + mainLayout.addSpacing(40) grid = QGridLayout() grid.addWidget(self.cancelButton) mainLayout.addLayout(grid) @@ -1365,12 +1364,11 @@ def initializePage(self): self.textBuildLabel.setText( "

" + - "New project " + self.newproject + - " will be created.
" + + "style=\" font-size:20pt;\">" + "You can also add descriptions and instructions" + " for your package.
" + - "Next step will build package with Copr." + + "The build button will create new project " + + self.newproject + " and build package with Copr." + "

") def __init__(self, Wizard, parent=None): @@ -1382,7 +1380,6 @@ def __init__(self, Wizard, parent=None): self.setSubTitle(self.tr("Copr additional information")) self.textBuildLabel = QLabel() - self.packageDescLabel = QLabel("Description ") self.packageDescEdit = QPlainTextEdit() self.packageDescLabelText = QLabel( @@ -1415,6 +1412,13 @@ def __init__(self, Wizard, parent=None): gridInstuction.addWidget(self.packageInstuctionEdit, 0, 1, 1, 8) gridInstuction.addWidget(self.packageInstuctionLabelText, 1, 0, 1, 8) + self.coprBuildButton = QPushButton('Build package with Copr') + self.coprBuildButton.setMinimumHeight(45) + self.coprBuildButton.setMaximumHeight(45) + self.coprBuildButton.setMinimumWidth(200) + self.coprBuildButton.setMaximumWidth(205) + self.coprBuildButton.clicked.connect(self.buildCopr) + mainLayout.addSpacing(25) frameDesc.setLayout(gridDesc) frameInstuction.setLayout(gridInstuction) @@ -1423,51 +1427,49 @@ def __init__(self, Wizard, parent=None): mainLayout.addSpacing(15) mainLayout.addWidget(frameInstuction) mainLayout.addSpacing(15) + mainLayout.addWidget(self.coprBuildButton, 0, QtCore.Qt.AlignCenter) self.setLayout(mainLayout) def validatePage(self): - QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) - self.textBuildLabel.setText( - "

" + - "Creating new project..." + - "

") - self.textBuildLabel.repaint() + return True + + def buildCopr(self): + self.copr_dialog = QDialog(self) + self.copr_dialog.resize(600, 400) + self.copr_dialog.setWindowTitle('Building Copr') + self.copr_progress = QPlainTextEdit() + self.copr_progress.setReadOnly(True) + self.cancelButton = QPushButton('Cancel') + self.cancelButton.setMinimumHeight(45) + self.cancelButton.setMaximumHeight(45) + self.cancelButton.setMinimumWidth(100) + self.cancelButton.setMaximumWidth(115) + self.cancelButton.clicked.connect(self.CancelCopr) + mainLayout = QVBoxLayout() + mainLayout.addSpacing(40) + mainLayout.addWidget(self.copr_progress) + mainLayout.addSpacing(40) + grid = QGridLayout() + grid.addWidget(self.cancelButton) + mainLayout.addLayout(grid) + self.copr_dialog.setLayout(mainLayout) + + self.copr_dialog.show() self.base.coprdesc = self.packageDescEdit.toPlainText() self.base.coprintro = self.packageInstuctionEdit.toPlainText() - try: - self.base.copr_create_project(self.base.coprpackageName, + + self.copr_process = ThreadWrapper(self.copr_progress, + self.base.copr_create_and_build, + self.base.coprpackageName, self.base.coprversion, self.base.coprdesc, - self.base.coprintro) - except subprocess.CalledProcessError: - self.textBuildLabel.setText( - "

" + - "Error in creating project!" + - "
Please check your log in information" + - "

") - return False - self.textBuildLabel.setText( - "

" + - "Creating new project - DONE
" + - "Build proccess started...
" + - "It takes a while, but it may be safely interrupted." - "

") - self.textBuildLabel.repaint() - try: - self.base.copr_build( - self.base.coprpackageName, self.base.coprpackageUrl) - except subprocess.CalledProcessError: - self.textBuildLabel.setText( - "

" + - "Error in building project!" + - "
Please check your url information" + - "

") - return False - return True + self.base.coprintro, + self.base.coprpackageUrl) + self.copr_process.run() + + def CancelCopr(self): + self.copr_dialog.close() + self.copr_process.kill() def nextId(self): return Wizard.PageCoprFinal diff --git a/rpg/package_builder.py b/rpg/package_builder.py index 168ef09..735f1f3 100644 --- a/rpg/package_builder.py +++ b/rpg/package_builder.py @@ -48,10 +48,12 @@ def __init__(self): def build_srpm(spec_file, tarball, output_dir): """ Builds source rpm from spec and tarball and moves it to the output directory """ + print('Starting build command...') Command("rpmdev-setuptree").execute() - Command("cp " + path_to_str(tarball) + + Command("cp -v " + path_to_str(tarball) + ' $(rpm --eval "%{_topdir}")/SOURCES').execute() - output = Command("rpmbuild -bs " + path_to_str(spec_file)).execute() + output = Command("rpmbuild -bs -v " + path_to_str(spec_file)).execute() + print('SRPM package was created.') Command("mv " + path_to_str(output.split()[-1]) + " " + path_to_str(output_dir)).execute() From d3d5666baf59f607118fb80b8853616b1d25f6aa Mon Sep 17 00:00:00 2001 From: regeciovad Date: Fri, 27 Nov 2015 13:18:57 +0100 Subject: [PATCH 09/11] Gui - copy rpm and sprm in wizard --- rpg/__init__.py | 15 ++++----------- rpg/gui/thread.py | 1 - rpg/gui/wizard.py | 13 +++++++++---- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/rpg/__init__.py b/rpg/__init__.py index c6fe7c9..0d232a1 100644 --- a/rpg/__init__.py +++ b/rpg/__init__.py @@ -220,7 +220,7 @@ def write_spec(self): with open(str(self.spec_path), 'w') as spec_file: spec_file.write(str(self.spec)) - def build_srpm(self, path=None): + def build_srpm(self): """ Builds srpm into base directory. """ print('Building SRPM...') if not self.spec.Source or not self.archive_path.exists(): @@ -229,11 +229,8 @@ def build_srpm(self, path=None): print('Spec file created.') self._package_builder.build_srpm( self.spec_path, self.archive_path, self.base_dir) - if path: - Command("cp " + path_to_str(self.srpm_path) + " " + - str(path)).execute() - def build_rpm(self, target_distro, target_arch, path=None): + def build_rpm(self, target_distro, target_arch): """ Build rpm from srpm. If srpm does not exists, it will be created. """ try: @@ -242,18 +239,14 @@ def build_rpm(self, target_distro, target_arch, path=None): self.build_srpm() self._package_builder.build_rpm( str(self.srpm_path), target_distro, target_arch, self.base_dir) - if path: - packages = self.rpm_path - for package in packages: - Command("cp " + str(package) + " " + path).execute() - def build_rpm_recover(self, distro, arch, path=None): + def build_rpm_recover(self, distro, arch): """ Repeatedly build rpm with mock and finds all build errors. May raise RuntimeError on failed recover. """ def build(): self.build_srpm() - self.build_rpm(distro, arch, path) + self.build_rpm(distro, arch) def analyse(): _files_to_pkgs.installed(self.base_dir, self.spec, self.sack) diff --git a/rpg/gui/thread.py b/rpg/gui/thread.py index 5f09fce..c30b5ad 100644 --- a/rpg/gui/thread.py +++ b/rpg/gui/thread.py @@ -60,4 +60,3 @@ def writer(self): """ Appends output from function to the GUI widget. """ while self._process.is_alive(): self._widget.appendPlainText(self._queue.get()) - self._widget.repaint() diff --git a/rpg/gui/wizard.py b/rpg/gui/wizard.py index 18d3412..28fc35d 100644 --- a/rpg/gui/wizard.py +++ b/rpg/gui/wizard.py @@ -1066,13 +1066,15 @@ def buildSrpm(self): self.srpm_dialog.show() self.srpm_process = ThreadWrapper(self.srpm_progress, - self.base.build_srpm, - self.base.final_path) + self.base.build_srpm) self.srpm_process.run() def CancelSRPM(self): self.srpm_dialog.close() self.srpm_process.kill() + if self.base.srpm_path: + Command("cp " + str(self.base.srpm_path) + " " + + str(self.base.final_path)).execute() self.textBuildSRPMLabel.setText('Your source package was build in ' + self.base.final_path) @@ -1107,13 +1109,16 @@ def buildRpm(self): self.rpm_dialog.show() self.rpm_process = ThreadWrapper(self.rpm_progress, self.base.build_rpm_recover, - distro, arch, - self.base.final_path) + distro, arch) self.rpm_process.run() def CancelRPM(self): self.rpm_dialog.close() self.rpm_process.kill() + if self.base.rpm_path: + for package in self.base.rpm_path: + Command("cp " + str(package) + " " + + str(self.base.final_path)).execute() self.textBuildRPMLabel.setText( 'Your package was build in ' + self.base.final_path) From c8a55721a1591267fc50e58ac3a2cfcf52dfb27b Mon Sep 17 00:00:00 2001 From: regeciovad Date: Sat, 5 Mar 2016 20:58:07 +0100 Subject: [PATCH 10/11] Gui - fix: undefined self.new_thread bug --- rpg/gui/wizard.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/rpg/gui/wizard.py b/rpg/gui/wizard.py index 28fc35d..e7ccaa6 100644 --- a/rpg/gui/wizard.py +++ b/rpg/gui/wizard.py @@ -106,6 +106,7 @@ def __init__(self, Wizard, parent=None): super(ImportPage, self).__init__(parent) self.base = Wizard.base + self.new_thread = None self.redQLineEdit = ("QLineEdit { border-style: solid;" + "border-width: 1px;" + "border-color: #FF3333;" + @@ -249,9 +250,10 @@ def cleanupPage(self): """ Stops the thread (there are no official way to stop thread. This will unlocked thread and it will be stopped with GUI - without error). """ - self.new_thread._tstate_lock = None - self.new_thread._stop() - self.new_thread.join() + if self.new_thread: + self.new_thread._tstate_lock = None + self.new_thread._stop() + self.new_thread.join() def nextId(self): ''' [int] Function that determines the next page after the current one From 503570245f7f0daf78b8a557d55799ff335accad Mon Sep 17 00:00:00 2001 From: regeciovad Date: Tue, 8 Mar 2016 19:06:54 +0100 Subject: [PATCH 11/11] Gui - fix: import url added --- rpg/gui/wizard.py | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/rpg/gui/wizard.py b/rpg/gui/wizard.py index e7ccaa6..5399abc 100644 --- a/rpg/gui/wizard.py +++ b/rpg/gui/wizard.py @@ -9,6 +9,7 @@ from rpg.command import Command import subprocess import platform +import urllib.request from threading import Thread from rpg.gui.thread import ThreadWrapper @@ -202,12 +203,7 @@ def __init__(self, Wizard, parent=None): self.setLayout(mainLayout) def checkPath(self): - ''' Checks, if path to import is correct while typing''' - path = Path(self.importEdit.text()) - if(path.exists()): - self.importEdit.setStyleSheet("") - else: - self.importEdit.setStyleSheet(self.redQLineEdit) + self.importEdit.setStyleSheet("") def importPath(self): ''' Returns path selected file or archive''' @@ -230,21 +226,26 @@ def validatePage(self): ###### Setting up RPG class references ###### ''' # Verifying path + QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) path = Path(self.importEdit.text()) - if(path.exists()): - self.base.target_arch = self.ArchEdit.currentText() - self.base.target_distro = self.DistroEdit.currentText() - self.base.load_project_from_url(self.importEdit.text().strip()) - self.base.run_extracted_source_analysis() - self.new_thread = Thread( - target=self.base.fetch_repos, args=(self.base.target_distro, - self.base.target_arch)) - self.new_thread.start() - self.importEdit.setStyleSheet("") - return True - else: - self.importEdit.setStyleSheet(self.redQLineEdit) - return False + if not (path.exists()): + urlpath = self.importEdit.text() + try: + urllib.request.urlopen(urlpath) + except: + self.importEdit.setStyleSheet(self.redQLineEdit) + QApplication.restoreOverrideCursor() + return False + self.base.target_arch = self.ArchEdit.currentText() + self.base.target_distro = self.DistroEdit.currentText() + self.base.load_project_from_url(self.importEdit.text().strip()) + self.base.run_extracted_source_analysis() + self.new_thread = Thread( + target=self.base.fetch_repos, args=(self.base.target_distro, + self.base.target_arch)) + self.new_thread.start() + self.importEdit.setStyleSheet("") + return True def cleanupPage(self): """ Stops the thread (there are no official way to stop thread. @@ -271,6 +272,7 @@ def initializePage(self): self.releaseEdit.setText("1") self.licenseEdit.setText(str(self.base.spec.License)) self.URLEdit.setText(str(self.base.spec.URL)) + QApplication.restoreOverrideCursor() def __init__(self, Wizard, parent=None): super(MandatoryPage, self).__init__(parent)