PrintView improvements (#250)

- Allow printer selection directly from PrintView
- Print button bypasses the system print dialog, since label printing is quite different from typical printing tasks
- Separate button to optionally use system print dialog, if needed
This commit is contained in:
Jaye Evins
2025-11-17 17:04:04 -05:00
committed by GitHub
parent b69ada13db
commit bcc6b3ec9d
7 changed files with 463 additions and 33 deletions
+2
View File
@@ -26,6 +26,7 @@ set (glabels_sources
NotebookUtil.cpp NotebookUtil.cpp
ObjectEditor.cpp ObjectEditor.cpp
PreferencesDialog.cpp PreferencesDialog.cpp
PrinterMonitor.cpp
PrintView.cpp PrintView.cpp
PropertiesView.cpp PropertiesView.cpp
Preview.cpp Preview.cpp
@@ -59,6 +60,7 @@ set (glabels_qobject_headers
MergeView.h MergeView.h
ObjectEditor.h ObjectEditor.h
PreferencesDialog.h PreferencesDialog.h
PrinterMonitor.h
PrintView.h PrintView.h
PropertiesView.h PropertiesView.h
Preview.h Preview.h
+152 -3
View File
@@ -21,11 +21,16 @@
#include "PrintView.h" #include "PrintView.h"
#include "PrinterMonitor.h"
#include "model/Settings.h" #include "model/Settings.h"
#include <QDebug>
#include <QFileDialog>
#include <QMessageBox>
#include <QPrinter> #include <QPrinter>
#include <QPrinterInfo>
#include <QPrintDialog> #include <QPrintDialog>
#include <QtDebug>
namespace glabels namespace glabels
@@ -41,6 +46,13 @@ namespace glabels
titleLabel->setText( QString( "<span style='font-size:18pt;'>%1</span>" ).arg( tr("Print") ) ); titleLabel->setText( QString( "<span style='font-size:18pt;'>%1</span>" ).arg( tr("Print") ) );
auto* printerMonitor = PrinterMonitor::instance();
loadDestinations( printerMonitor->availablePrinters() );
connect( printerMonitor, SIGNAL(availablePrintersChanged(const QStringList&)),
this, SLOT(onAvailablePrintersChanged(const QStringList&)) );
setDestination( model::Settings::recentPrinter() );
preview->setRenderer( &mRenderer ); preview->setRenderer( &mRenderer );
} }
@@ -59,6 +71,17 @@ namespace glabels
} }
///
/// Available printers changed handler
///
void PrintView::onAvailablePrintersChanged( const QStringList& printers )
{
auto savedSelection = destinationCombo->currentText();
loadDestinations( printers );
setDestination( savedSelection );
}
/// ///
/// Model changed handler /// Model changed handler
/// ///
@@ -185,9 +208,62 @@ namespace glabels
/// ///
void PrintView::onPrintButtonClicked() void PrintView::onPrintButtonClicked()
{ {
auto printerName = destinationCombo->currentText();
auto printerInfo = QPrinterInfo::printerInfo( printerName );
bool isPrinter = !printerInfo.isNull();
QPrinter printer( QPrinter::HighResolution ); QPrinter printer( QPrinter::HighResolution );
printer.setColorMode( QPrinter::Color ); printer.setColorMode( QPrinter::Color );
printer.setPrinterName( model::Settings::recentPrinter() );
if ( isPrinter )
{
printer.setPrinterName( printerName );
mRenderer.print( &printer );
model::Settings::setRecentPrinter( printerName );
}
else
{
QString fileName =
QFileDialog::getSaveFileName( this,
tr("Print to file (PDF)"),
defaultPdf(),
tr("PDF files (*.pdf);;All files (*)"),
nullptr,
QFileDialog::DontConfirmOverwrite );
if ( !fileName.isEmpty() )
{
if ( QFileInfo::exists(fileName) )
{
QMessageBox msgBox( this );
msgBox.setWindowTitle( tr("Print to file (PDF)") );
msgBox.setIcon( QMessageBox::Warning );
msgBox.setText( tr("%1 already exists.").arg(fileName) );
msgBox.setInformativeText( tr("Do you want to overwrite it?") );
msgBox.setStandardButtons( QMessageBox::Yes | QMessageBox::No );
msgBox.setDefaultButton( QMessageBox::No );
if ( msgBox.exec() == QMessageBox::No )
{
return;
}
}
printer.setOutputFileName( fileName );
printer.setOutputFormat( QPrinter::PdfFormat );
mRenderer.print( &printer );
}
}
}
///
/// System Dialog Button Clicked handler
///
void PrintView::onSystemDialogButtonClicked()
{
QPrinter printer( QPrinter::HighResolution );
printer.setColorMode( QPrinter::Color );
printer.setPrinterName( destinationCombo->currentText() );
QPrintDialog printDialog( &printer, this ); QPrintDialog printDialog( &printer, this );
printDialog.setOption( QAbstractPrintDialog::PrintToFile, true ); printDialog.setOption( QAbstractPrintDialog::PrintToFile, true );
@@ -201,9 +277,82 @@ namespace glabels
{ {
mRenderer.print( &printer ); mRenderer.print( &printer );
model::Settings::setRecentPrinter( printer.printerName() ); if ( !printer.printerName().isEmpty() )
{
model::Settings::setRecentPrinter( printer.printerName() );
}
} }
} }
///
/// Load available printers
///
void PrintView::loadDestinations( const QStringList& printers )
{
destinationCombo->blockSignals( true );
destinationCombo->clear();
for ( auto& printerName : printers )
{
destinationCombo->addItem( QIcon::fromTheme( "glabels-print" ), printerName );
}
if ( destinationCombo->count() )
{
destinationCombo->insertSeparator( destinationCombo->count() );
}
destinationCombo->addItem( QIcon::fromTheme( "glabels-file-new" ), tr( "Print to file (PDF)" ) );
destinationCombo->blockSignals( false );
}
///
/// Generate default PDF filename
///
QString PrintView::defaultPdf()
{
if ( !mModel )
{
return "output.pdf";
}
return mModel->dirPath() + "/" + mModel->shortName() + ".pdf";
}
///
/// Set destination to printerName if valid, or to a fallback value
///
void PrintView::setDestination( const QString& printerName )
{
destinationCombo->blockSignals( true );
auto printerInfo = QPrinterInfo::printerInfo( printerName );
if ( !printerInfo.isNull() )
{
// printerName is a valid printer
destinationCombo->setCurrentText( printerName );
}
else
{
auto defaultPrinterName = QPrinterInfo::defaultPrinterName();
auto defaultPrinterInfo = QPrinterInfo::printerInfo( defaultPrinterName );
if ( !defaultPrinterInfo.isNull() )
{
// defaultPinterName is a valid printer
destinationCombo->setCurrentText( defaultPrinterName );
}
else
{
// No default printer available, set to first item in combo (probably "print to file")
destinationCombo->setCurrentIndex( 0 );
}
}
destinationCombo->blockSignals( false );
}
} // namespace glabels } // namespace glabels
+11
View File
@@ -57,10 +57,21 @@ namespace glabels
// Slots // Slots
///////////////////////////////// /////////////////////////////////
private slots: private slots:
void onAvailablePrintersChanged( const QStringList& printers );
void onModelChanged(); void onModelChanged();
void updateView(); void updateView();
void onFormChanged(); void onFormChanged();
void onPrintButtonClicked(); void onPrintButtonClicked();
void onSystemDialogButtonClicked();
/////////////////////////////////
// Private methods
/////////////////////////////////
private:
void loadDestinations( const QStringList& printers );
QString defaultPdf();
void setDestination( const QString& printerName );
///////////////////////////////// /////////////////////////////////
+88
View File
@@ -0,0 +1,88 @@
/* PrinterMonitor.cpp
*
* Copyright (C) 2025 Jaye Evins <evins@snaught.com>
*
* This file is part of gLabels-qt.
*
* gLabels-qt is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* gLabels-qt is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with gLabels-qt. If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrinterMonitor.h"
#include <QPrinterInfo>
#include <QDebug>
namespace glabels
{
///
/// Static data
///
std::unique_ptr<PrinterMonitor> PrinterMonitor::mInstance;
///
/// Constructor
///
PrinterMonitor::PrinterMonitor()
{
mCurrentAvailablePrinters = QPrinterInfo::availablePrinterNames();
mTimer.reset( new QTimer( this ) );
connect( mTimer.get(), SIGNAL(timeout()), this, SLOT(onTimerTimeout()) );
mTimer->start( 1000 );
}
///
/// Get singleton instance
///
PrinterMonitor* PrinterMonitor::instance()
{
if ( !mInstance )
{
mInstance.reset( new PrinterMonitor() );
}
return mInstance.get();
}
///
/// Get available printers
///
const QStringList& PrinterMonitor::availablePrinters() const
{
return mCurrentAvailablePrinters;
}
///
/// On timer timeout
///
void PrinterMonitor::onTimerTimeout()
{
auto newAvailablePrinters = QPrinterInfo::availablePrinterNames();
if ( newAvailablePrinters != mCurrentAvailablePrinters )
{
mCurrentAvailablePrinters = newAvailablePrinters;
emit availablePrintersChanged( mCurrentAvailablePrinters );
}
}
} // namespace glabels
+87
View File
@@ -0,0 +1,87 @@
/* PrinterMonitor.h
*
* Copyright (C) 2025 Jaye Evins <evins@snaught.com>
*
* This file is part of gLabels-qt.
*
* gLabels-qt is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* gLabels-qt is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with gLabels-qt. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PrinterMonitor_h
#define PrinterMonitor_h
#include <QObject>
#include <QStringList>
#include <QTimer>
#include <memory>
namespace glabels
{
///
/// Printer Monitor
///
class PrinterMonitor : public QObject
{
Q_OBJECT
/////////////////////////////////
// Life Cycle
/////////////////////////////////
private:
PrinterMonitor();
public:
static PrinterMonitor* instance();
/////////////////////////////////
// Public methods
/////////////////////////////////
public:
const QStringList& availablePrinters() const;
/////////////////////////////////
// Slots
/////////////////////////////////
private slots:
void onTimerTimeout();
/////////////////////////////////
// Signals
/////////////////////////////////
signals:
void availablePrintersChanged( const QStringList& availablePrinters );
/////////////////////////////////
// Private Members
/////////////////////////////////
private:
static std::unique_ptr<PrinterMonitor> mInstance;
std::unique_ptr<QTimer> mTimer;
QStringList mCurrentAvailablePrinters;
};
}
#endif // PrinterMonitor_h
+97 -28
View File
@@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>852</width> <width>852</width>
<height>796</height> <height>874</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@@ -53,6 +53,34 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Destination</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QComboBox" name="destinationCombo">
<property name="styleSheet">
<string notr="true">text-align:left; padding:3px;</string>
</property>
<property name="currentText">
<string/>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="placeholderText">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item> <item>
<widget class="QGroupBox" name="printRangeBox"> <widget class="QGroupBox" name="printRangeBox">
<property name="sizePolicy"> <property name="sizePolicy">
@@ -449,7 +477,7 @@
<string notr="true">text-align:left; padding:3px;</string> <string notr="true">text-align:left; padding:3px;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Print...</string> <string>Print</string>
</property> </property>
<property name="icon"> <property name="icon">
<iconset theme="glabels-print"> <iconset theme="glabels-print">
@@ -531,6 +559,13 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QPushButton" name="systemDialogButton">
<property name="text">
<string>Use system print dialog...</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>
@@ -645,8 +680,8 @@
<slot>onFormChanged()</slot> <slot>onFormChanged()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>175</x> <x>164</x>
<y>122</y> <y>260</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>2</x> <x>2</x>
@@ -661,8 +696,8 @@
<slot>onFormChanged()</slot> <slot>onFormChanged()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>182</x> <x>216</x>
<y>444</y> <y>574</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>2</x> <x>2</x>
@@ -677,8 +712,8 @@
<slot>onFormChanged()</slot> <slot>onFormChanged()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>152</x> <x>186</x>
<y>475</y> <y>608</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>6</x> <x>6</x>
@@ -693,8 +728,8 @@
<slot>onFormChanged()</slot> <slot>onFormChanged()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>156</x> <x>190</x>
<y>506</y> <y>642</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>6</x> <x>6</x>
@@ -709,8 +744,8 @@
<slot>onFormChanged()</slot> <slot>onFormChanged()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>641</x> <x>639</x>
<y>760</y> <y>827</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>1</x> <x>1</x>
@@ -725,8 +760,8 @@
<slot>onPrintButtonClicked()</slot> <slot>onPrintButtonClicked()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>291</x> <x>312</x>
<y>589</y> <y>724</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>5</x> <x>5</x>
@@ -741,8 +776,8 @@
<slot>onFormChanged()</slot> <slot>onFormChanged()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>159</x> <x>156</x>
<y>243</y> <y>390</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>7</x> <x>7</x>
@@ -757,8 +792,8 @@
<slot>onFormChanged()</slot> <slot>onFormChanged()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>59</x> <x>93</x>
<y>108</y> <y>258</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>6</x> <x>6</x>
@@ -773,8 +808,8 @@
<slot>onFormChanged()</slot> <slot>onFormChanged()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>142</x> <x>188</x>
<y>149</y> <y>299</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>5</x> <x>5</x>
@@ -789,8 +824,8 @@
<slot>onFormChanged()</slot> <slot>onFormChanged()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>226</x> <x>275</x>
<y>145</y> <y>299</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>8</x> <x>8</x>
@@ -805,8 +840,8 @@
<slot>onFormChanged()</slot> <slot>onFormChanged()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>52</x> <x>85</x>
<y>261</y> <y>401</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>3</x> <x>3</x>
@@ -821,8 +856,8 @@
<slot>onFormChanged()</slot> <slot>onFormChanged()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>231</x> <x>264</x>
<y>314</y> <y>449</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>5</x> <x>5</x>
@@ -837,8 +872,8 @@
<slot>onFormChanged()</slot> <slot>onFormChanged()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>199</x> <x>254</x>
<y>362</y> <y>679</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>4</x> <x>4</x>
@@ -846,11 +881,45 @@
</hint> </hint>
</hints> </hints>
</connection> </connection>
<connection>
<sender>destinationCombo</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>PrintView</receiver>
<slot>onFormChanged()</slot>
<hints>
<hint type="sourcelabel">
<x>289</x>
<y>110</y>
</hint>
<hint type="destinationlabel">
<x>254</x>
<y>6</y>
</hint>
</hints>
</connection>
<connection>
<sender>systemDialogButton</sender>
<signal>clicked()</signal>
<receiver>PrintView</receiver>
<slot>onSystemDialogButtonClicked()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>836</y>
</hint>
<hint type="destinationlabel">
<x>373</x>
<y>871</y>
</hint>
</hints>
</connection>
</connections> </connections>
<slots> <slots>
<slot>onPrintButtonClicked()</slot> <slot>onPrintButtonClicked()</slot>
<slot>onFormChanged()</slot> <slot>onFormChanged()</slot>
<slot>onPrinterPropertiesButtonClicked()</slot> <slot>onPrinterPropertiesButtonClicked()</slot>
<slot>onBrowseButtonClicked()</slot>
<slot>onSystemDialogButtonClicked()</slot>
</slots> </slots>
<buttongroups> <buttongroups>
<buttongroup name="buttonGroup"/> <buttongroup name="buttonGroup"/>
+26 -2
View File
@@ -619,11 +619,19 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Print...</source> <source>Start groups at position:</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>Start groups at position:</source> <source>Destination</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Print</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use system print dialog...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
@@ -2113,6 +2121,22 @@
<source>(Will print a total of %1 items on 1 page.)</source> <source>(Will print a total of %1 items on 1 page.)</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Print to file (PDF)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>PDF files (*.pdf);;All files (*)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1 already exists.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Do you want to overwrite it?</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>glabels::PropertiesView</name> <name>glabels::PropertiesView</name>