WxPython開發(fā)之列表數(shù)據(jù)的自定義打印處理
在WxPython跨平臺開發(fā)框架中,我們大多數(shù)情況下,數(shù)據(jù)記錄通過wx.Grid的數(shù)據(jù)表格進(jìn)行展示,其中表格的數(shù)據(jù)記錄的顯示和相關(guān)處理,通過在基類窗體 BaseListFrame 進(jìn)行統(tǒng)一的處理,因此對于常規(guī)的數(shù)據(jù)記錄打印,我們也可以在其中集成相關(guān)的打印處理,本篇隨筆介紹如何利用WxPython內(nèi)置的打印數(shù)據(jù)組件實(shí)現(xiàn)列表數(shù)據(jù)的自定義打印處理,以及對記錄進(jìn)行分頁等常規(guī)操作。
1、WxPython內(nèi)置的打印數(shù)據(jù)組件
wx.PrintPreview
、wx.Printer
和 wx.Printout
是 wxPython 提供的用于打印功能的核心類。它們幫助開發(fā)者在程序中實(shí)現(xiàn)打印和打印預(yù)覽功能。下面是關(guān)于這三個(gè)類的詳細(xì)介紹及其使用方法。
wx.Printout
wx.Printout
是一個(gè)抽象類,通常需要被子類化以便在打印時(shí)自定義頁面內(nèi)容。你可以通過繼承 wx.Printout
類來實(shí)現(xiàn)自定義打印內(nèi)容,重寫 OnPrintPage
方法來決定每一頁的打印內(nèi)容。
常用方法:
OnPrintPage(page)
: 該方法是打印過程中每一頁的核心方法。你需要在這里繪制要打印的內(nèi)容,例如文本、圖片、圖形等。每次頁面需要繪制時(shí),該方法會被調(diào)用。GetDC()
: 獲取一個(gè)打印設(shè)備上下文(wx.DC
對象),用于在頁面上繪制內(nèi)容。HasPage(page)
: 該方法用于檢查是否有足夠的內(nèi)容來填充打印頁面,返回True
或False
。
簡單的wx.PrintOut的子類繼承處理如下所示。
import wx class MyPrintout(wx.Printout): def __init__(self): super().__init__() def OnPrintPage(self, page): dc = self.GetDC() dc.SetFont(wx.Font(10, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)) dc.DrawText("Hello, this is a print test!", 100, 100) return True
一般情況下,我們肯定會添加很多相關(guān)的繪制邏輯處理,這里只是簡單的介紹。
wx.Printer
wx.Printer
是用來執(zhí)行實(shí)際打印操作的類。它用于與打印機(jī)進(jìn)行交互,發(fā)送頁面內(nèi)容到打印機(jī)。你可以使用 wx.Printer
來選擇打印機(jī)、啟動(dòng)打印作業(yè)并發(fā)送打印內(nèi)容。
常用方法:
Print(printout)
: 用于啟動(dòng)打印操作,將printout
對象中的內(nèi)容發(fā)送到打印機(jī)。GetCurrentPrinterName()
: 獲取當(dāng)前選定的打印機(jī)名稱。SetPageSize(width, height)
: 設(shè)置打印頁面的大小。SetPrintData(print_data)
: 設(shè)置打印的數(shù)據(jù),控制打印的紙張大小、打印質(zhì)量等
wx.PrintPreview
wx.PrintPreview
用于實(shí)現(xiàn)打印預(yù)覽功能,它允許你查看打印內(nèi)容的預(yù)覽,而不是直接將內(nèi)容發(fā)送到打印機(jī)。通過打印預(yù)覽,你可以看到打印文檔的外觀,并進(jìn)行調(diào)整(如分頁、內(nèi)容布局等)。
常用方法:
IsOk()
: 檢查打印預(yù)覽是否成功創(chuàng)建。Show()
: 顯示打印預(yù)覽窗口。SetPagesPerPreview(pages)
: 設(shè)置每個(gè)預(yù)覽頁面顯示的頁面數(shù)。
通常,wx.PrintPreview
是在打印機(jī)選擇之前使用的,它會展示打印內(nèi)容的預(yù)覽。預(yù)覽確認(rèn)后,用戶可以選擇繼續(xù)打印,程序則使用 wx.Printer
實(shí)際執(zhí)行打印操作。
繪制二維表的打印預(yù)覽可以通過在 wx.Printout
類的 OnPrintPage
方法中逐行逐列繪制表格內(nèi)容。為了實(shí)現(xiàn)更復(fù)雜的表格布局,通常需要計(jì)算每一行和每一列的位置,確保文本不會超出頁面邊界,并在必要時(shí)分頁。
如果需要在 wxPython 中實(shí)現(xiàn)打印預(yù)覽,可以使用 wx.PrintPreview
類來生成打印預(yù)覽。這個(gè)類允許你在顯示打印預(yù)覽時(shí),不直接打印內(nèi)容,而是顯示一個(gè)打印的“模擬”視圖,用戶可以在此視圖中查看打印效果,決定是否繼續(xù)打印。
高級功能:表格樣式
如果你需要更復(fù)雜的表格樣式(例如,添加邊框、背景色或其他格式化功能),你可以通過 dc.SetPen()
、dc.SetBrush()
等方法來實(shí)現(xiàn)。
示例:給表格添加邊框
def draw_table_border(dc, x_pos, y_pos, width, height): dc.SetPen(wx.Pen(wx.Colour(0, 0, 0))) # 設(shè)置邊框顏色 dc.SetBrush(wx.Brush(wx.Colour(255, 255, 255))) # 設(shè)置背景顏色 dc.DrawRectangle(x_pos, y_pos, width, height)
你可以在 OnPrintPage
方法中調(diào)用此函數(shù),為每個(gè)單元格添加邊框。
2、在框架的基類窗體中實(shí)現(xiàn)自定義打印及分頁處理
在我的WxPython跨平臺開發(fā)框架中,我們對于常規(guī)窗體列表界面做了抽象處理,一般絕大多數(shù)的邏輯封裝在基類上,基類提供一些可重寫的函數(shù)給子類實(shí)現(xiàn)彈性化的處理。
如下是基類窗體和其他窗體之間的集成關(guān)系。
一般列表界面,如下所示。
我們打印模塊的處理,需要把這些列表的記錄顯示在打印預(yù)覽界面上,并決定是否繼續(xù)打印即可。
前面介紹了wx.PrintOut的子類處理,只是簡單的輸出一些文本信息,一般對于更復(fù)雜的打印需求(例如表格或多頁文檔),你可以在 OnPrintPage
中使用更復(fù)雜的繪制邏輯。例如,繪制多個(gè)表格行或根據(jù)頁數(shù)拆分內(nèi)容等等。
我們先來看看實(shí)現(xiàn)后的打印預(yù)覽界面效果,有一個(gè)感官認(rèn)識后再繼續(xù)探尋它的實(shí)現(xiàn)機(jī)制。
打印預(yù)覽入口,在列表界面上右鍵,彈出打印菜單,如下界面所示。
打印預(yù)覽界面如下所示。
我們來看看,對于列表數(shù)據(jù),我們是如何一步步通過自定義的方式實(shí)現(xiàn)打印的內(nèi)容繪制的。
打印有幾個(gè)注意的地方,由于不同的操作系統(tǒng)或者不同的設(shè)置可能會導(dǎo)致打印面板的尺寸有所差異,因此最好統(tǒng)一轉(zhuǎn)換為一個(gè)邏輯的尺寸處理;另外分頁的處理是關(guān)鍵,需要根據(jù)字體文字計(jì)算出你繪制一行所需要的高度,然后根據(jù)操作的頁面高度計(jì)算可以繪制的行數(shù),然后在表格行記錄中循環(huán)處理,判斷達(dá)到每頁的行數(shù)就換行即可。
在我們的WxPython跨平臺框架中列表入口菜單的實(shí)現(xiàn)如下代碼所示,主要就是調(diào)用MyPrintOut的自定義對象,然后調(diào)用PrintPreview進(jìn)行打印預(yù)覽窗體的顯示即可,如下代碼所示。
def OnPrint(self, event: wx.Event) -> None: """打印表格""" # 創(chuàng)建打印數(shù)據(jù)對象 pageData = wx.PrintData() pageData.SetPaperId(wx.PAPER_A4) # 設(shè)置紙張類型為 A4 pageData.SetOrientation(wx.LANDSCAPE) # 設(shè)置紙張方向?yàn)榭v向 # 將打印數(shù)據(jù)設(shè)置到打印機(jī)對象 data = wx.PrintDialogData(pageData) # 窗口標(biāo)題 page_title = self.GetTitle() # 創(chuàng)建打印輸出對象 printout = ctrl.MyPrintout(self.grid, page_title) printout2 = ctrl.MyPrintout(self.grid, page_title) # 創(chuàng)建打印機(jī)對象,并將打印數(shù)據(jù)傳遞給打印機(jī) # printer = wx.Printer(data) preview = wx.PrintPreview(printout, printout2, data=data) if preview.IsOk(): preview.SetZoom(100) frame = wx.PreviewFrame( preview, self, "打印預(yù)覽", pos=self.GetPosition(), size=self.GetSize() ) frame.Initialize() frame.Show(True)
wx.PrintPreview需要接受兩個(gè)PrintOut對象,如代碼:printout, printout2
wx.PrintData 主要就是指定頁面尺寸及布局等相關(guān)信息,具體的邏輯還是在自定義的 MyPrintout 類里面,它接受當(dāng)前的wx.Grid對象和當(dāng)前頁面的標(biāo)題。
而ctrl.MyPrintOut是我們根據(jù)需要打印的二維表內(nèi)容進(jìn)行的邏輯封裝,它們的類初始化代碼如下。
class MyPrintout(wx.Printout): """創(chuàng)建一個(gè)自定義的 wx.Printout 類,在其中定義如何打印 wx.Grid 的內(nèi)容。""" def __init__( self, grid: gridlib.Grid, title: str = "打印標(biāo)題", margins=(wx.Point(10, 10), wx.Point(10, 10)), ): super().__init__() self.grid = grid self.title = title self.margins = margins # 獲取網(wǎng)格的行列數(shù) self.row_count = self.grid.GetNumberRows() self.col_count = self.grid.GetNumberCols() self.current_row = 0 # 記錄當(dāng)前打印的行數(shù)
我們記錄表格對象,以及表格的行數(shù)和列數(shù),以及當(dāng)前打印的行數(shù),默認(rèn)從0開始,在每次計(jì)算分頁的時(shí)候,需要知道當(dāng)前的記錄才能接著繪制剩余的記錄。
我們通過轉(zhuǎn)換邏輯單位,獲得繪制設(shè)備的頁面高度和寬度,然后每行記錄的高度,我們根據(jù)內(nèi)容的實(shí)際高度+一定的空白間距,如下代碼
font = wx.Font( FONTSIZE, wx.FONTFAMILY_TELETYPE, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL ) dc.SetFont(font) # 計(jì)算每行的高度 self.lineHeight = dc.GetCharHeight() + 5 # 加上5像素的間距
計(jì)算每頁可以繪制多少行,需要記錄摒除標(biāo)題內(nèi)容、表格標(biāo)題行以及相關(guān)空白,剩下的頁面高度除以一行的高度,進(jìn)行計(jì)算獲得即可。如下代碼所示。
# 標(biāo)題高度 + 標(biāo)題底部空白行高度 + 表頭高度 y_offset = 20 + 20 + 20 # 計(jì)算每頁可以顯示的行數(shù) self.linesPerPage = int((self.pageHeight - y_offset) // self.lineHeight)
然后,我們在PrintOut的子類實(shí)現(xiàn)中重寫 OnPreparePrinting(self) 函數(shù),這個(gè)函數(shù)在繪制頁面前執(zhí)行一次,我們需要在其中計(jì)算出頁面的數(shù)量。
# 計(jì)算頁面數(shù) self.numPages = self.row_count // self.linesPerPage if self.row_count % self.linesPerPage != 0: self.numPages += 1
然后在重寫兩個(gè)函數(shù),決定是否存在下一頁,以及頁碼信息,如下所示的函數(shù)實(shí)現(xiàn)。
def HasPage(self, page): return page <= self.numPages def GetPageInfo(self): return (1, self.numPages, 1, self.numPages)
剩下的就是實(shí)現(xiàn)
def OnPrintPage(self, page: int) -> bool:
這個(gè)函數(shù)就是主要控制繪制內(nèi)容和分頁標(biāo)識的處理的。
繪制的內(nèi)容,主要根據(jù)起始位置,并設(shè)置相關(guān)的字體大小實(shí)現(xiàn)繪制即可,我們簡單介紹一下,如下是在頁面頂部的中間繪制標(biāo)題。
# 繪制標(biāo)題,居中顯示 text_width, text_height = dc.GetTextExtent(self.title) title_x = (width - text_width) // 2 # 計(jì)算居中的X位置 dc.DrawText(self.title, int(title_x), int(y_offset))
線條的繪制也是類似,確定位置和顏色等,畫線繪制即可。
# 設(shè)置線條顏色 dc.SetPen(wx.Pen(wx.Colour(0, 0, 0))) # 黑色線條 # 將所有的坐標(biāo)轉(zhuǎn)換為整數(shù) dc.DrawLine(line_x, line_y, line_x + line_width, line_y)
繪制表頭的時(shí)候,我們切換會正常的字體大小,然后遍歷繪制
# 繪制表頭 font.SetPointSize(FONTSIZE) dc.SetFont(font) for col in range(self.col_count): dc.DrawText( self.grid.GetColLabelValue(col), int(x_offset + sum(col_widths[:col])), int(y_offset), )
其中的 col_widths 是我們前面根據(jù)表格的列數(shù)量進(jìn)行計(jì)算的寬度集合
col_widths = [self.grid.GetColSize(col) for col in range(self.col_count)]
以上內(nèi)容是我們每頁都需要繪制的常規(guī)信息,因此先繪制報(bào)表表頭、線條、表格標(biāo)題行這些。
下面我們需要根據(jù)當(dāng)前page的頁碼來計(jì)算當(dāng)前開始的行記錄,page為1的時(shí)候,那么當(dāng)前的開始行號是0,否則就是根據(jù)頁碼計(jì)算數(shù)值。
self.current_row = (page - 1) * self.linesPerPage # 計(jì)算當(dāng)前頁面的起始行數(shù)
下面就是對數(shù)據(jù)進(jìn)行分頁的處理了,如果需要分頁,在OnPrintPage函數(shù)中返回False,否則返回True,如下代碼所示。
# 繪制表格的內(nèi)容 lines_on_page = 0 # 記錄當(dāng)前頁面的行數(shù) for row in range(self.current_row, self.row_count): y_offset += self.lineHeight # 增加行的高度 for col in range(self.col_count): cell_value = self.grid.GetCellValue(row, col) dc.DrawText( cell_value, x_offset + sum(col_widths[:col]), y_offset, ) lines_on_page += 1 # 如果當(dāng)前頁面的行數(shù)已經(jīng)達(dá)到最大值,則繪制頁腳,并返回False,繼續(xù)打印下一頁 if lines_on_page >= self.linesPerPage: self.draw_footer(dc, page) # 繪制頁腳,底部的頁碼 return False # 打印完一頁后返回False,繼續(xù)打印下一頁 self.draw_footer(dc, page) return True
而底部的頁碼信息,我們簡單繪制當(dāng)前頁面和頁面總數(shù)即可,如下函數(shù)。
def draw_footer(self, dc: wx.DC, page: int): # 繪制當(dāng)前頁碼 page_num_text = f"當(dāng)前頁碼: {page} / {self.numPages}" page_num_width, page_num_height = dc.GetTextExtent(page_num_text) dc.DrawText( page_num_text, int((self.pageWidth - page_num_width) // 2), int(self.y2), )
最后獲得隨筆開始前介紹的效果。
如果沒有分頁信息,那么底部空白一些,還是會繪制頁碼信息,如下是沒有更多記錄的時(shí)候打印預(yù)覽的界面效果
以上就是整個(gè)實(shí)現(xiàn)的過程,我們在WxPython開發(fā)框架中自定義PrintOut對象,實(shí)現(xiàn)WxPython跨平臺開發(fā)框架之列表數(shù)據(jù)的通用打印處理過程。
到此這篇關(guān)于WxPython開發(fā)之列表數(shù)據(jù)的自定義打印處理的文章就介紹到這了,更多相關(guān)WxPython列表數(shù)據(jù)打印內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python?pandas刪除指定行/列數(shù)據(jù)的方法實(shí)例
這篇文章主要給大家介紹了關(guān)于Python?pandas刪除指定行/列數(shù)據(jù)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-01-01使用python字典統(tǒng)計(jì)CSV數(shù)據(jù)的步驟和示例代碼
為了使用Python字典來統(tǒng)計(jì)CSV數(shù)據(jù),我們可以使用內(nèi)置的csv模塊來讀取CSV文件,并使用字典來存儲統(tǒng)計(jì)信息,以下是一個(gè)詳細(xì)的步驟和完整的代碼示例,需要的朋友可以參考下2024-12-12淺談Keras參數(shù) input_shape、input_dim和input_length用法
這篇文章主要介紹了淺談Keras參數(shù) input_shape、input_dim和input_length用法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-06-06PyQt使用QPropertyAnimation開發(fā)簡單動(dòng)畫
這篇文章主要介紹了PyQt使用QPropertyAnimation開發(fā)簡單動(dòng)畫,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04python中編寫函數(shù)并調(diào)用的知識點(diǎn)總結(jié)
在本篇文章里小編給各位整理的是一篇關(guān)于python中編寫函數(shù)并調(diào)用的知識點(diǎn)總結(jié)內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。2021-01-01Python使用sqlite3模塊內(nèi)置數(shù)據(jù)庫
這篇文章主要介紹了Python使用sqlite3模塊內(nèi)置數(shù)據(jù)庫,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05python3 pandas 讀取MySQL數(shù)據(jù)和插入的實(shí)例
下面小編就為大家分享一篇python3 pandas 讀取MySQL數(shù)據(jù)和插入的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-04-04