wxpython. Нужна помощь с запутанной ошибкой

#python #sockets #wxpython

#python #сокеты #wxpython

Вопрос:

Этот код работает для кого-то другого на другом компьютере, но, похоже, он не будет работать для меня. Я использую python 2.7.7. Он хорошо работал для двух других людей, но, похоже, он просто не нравится мне или моему компьютеру, потому что всякий раз, когда я его запускаю, он выдает мне сообщение об ошибке. Что вы, ребята, думаете?

 Traceback (most recent call last):
  File "C:Python27python projectsclient with gui.py", line 43, in <module>
    frame = WindowFrame(None, 'ChatClient')
  File "C:Python27python projectsclient with gui.py", line 10, in __init__
    self.dc = wx.PaintDC(self.panel)  # <<< This was changed
  File "C:Python27python projectslibsite-packageswx-3.0-mswwx_gdi.py", line 5215, in __init__
    _gdi_.PaintDC_swiginit(self,_gdi_.new_PaintDC(*args, **kwargs))
PyAssertionError: C   assertion "Assert failure" failed at ....srcmswdcclient.cpp(277) in wxPaintDCImpl::wxPaintDCImpl(): wxPaintDCImpl may be created only in EVT_PAINT handler!





import socket
import wx

class WindowFrame(wx.Frame):
    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent, title = title, size=(500, 400))
        self.panel=wx.Panel(self)
        self.panel.SetBackgroundColour("#0B3861")
        self.control = wx.TextCtrl(self.panel, style = wx.TE_MULTILINE, size =(410, 28), pos=(0,329))
        self.dc = wx.PaintDC(self.panel)  # <<< This was changed
        # Sets up the socket connection
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        host = "127.0.0.1"
        port = 6667
        self.s.connect((host,port))

        # creates send button and binds to event
        sendbutton=wx.Button(self.panel, label ="Send", pos =(414,325), size=(65,35))
        self.panel.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_BUTTON, self.SendPress, sendbutton )

        self.Centre()
        self.Show()

        #Draws white rectangle
    def OnPaint(self, event):
        self.dc.SetPen(wx.Pen('black'))
        self.dc.SetBrush(wx.Brush('white'))
        self.shapeRectangle=self.dc.DrawRectangle(20, 20, 444, 280)
        self.Show(True)


        # Sets the function of the send button
    def SendPress(self, event):
        self.sent = self.control.GetValue()
        self.s.send(self.sent)
        self.control.Clear()
        self.dc.DrawText(self.sent, 0, 300 )
        self.s.close()

if __name__=="__main__":
    app = wx.App(False)
    frame = WindowFrame(None, 'ChatClient')
    app.MainLoop()
 

Ответ №1:

Я получаю ту же ошибку, что и вы, при запуске этого кода. Глядя на документацию, связанную ниже «wx.PaintDC должен быть создан, если приложение хочет рисовать в клиентской области окна из обработчика событий EVT_PAINT».

http://www.wxpython.org/docs/api/wx.PaintDC-class.html

Не могли бы вы рассмотреть возможность изменения этого на клиентский DC? «Wx.ClientDC должен быть создан, если приложение хочет рисовать в клиентской области окна извне события EVT_PAINT»

http://www.wxpython.org/docs/api/wx.ClientDC-class.html

Обычно я думаю, что вы создаете PaintDC в методе, привязанном к событию EVT_PAINT, я проверил несколько примеров своего кода, и, как правило, я использую ClientDC, когда хочу посмотреть атрибуты или повлиять на холст извне метода Paint.

Для этого я бы изменил:

 self.dc = wx.PaintDC(self.panel)
 

Для:

 self.dc = wx.ClientDC(self.panel)
 

Вот полная измененная версия вашего кода, использующая оригинальный PaintDC для выполнения того, что, я думаю, вы пытаетесь сделать.

 import socket
import wx

class WindowFrame(wx.Frame):
    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent, title = title, size=(500, 400))
        self.panel=wx.Panel(self)
        self.panel.SetBackgroundColour("#0B3861")
        self.control = wx.TextCtrl(self.panel, style = wx.TE_MULTILINE, size =(410, 28), pos=(0,329))
        self.textLog = []
        # Sets up the socket connection
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        host = "127.0.0.1"
        port = 6667
        self.s.connect((host,port))

        # creates send button and binds to event
        sendbutton=wx.Button(self.panel, label ="Send", pos =(414,325), size=(65,35))
        self.panel.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_BUTTON, self.SendPress, sendbutton )

        self.Centre()
        self.Show()

        #Draws white rectangle
    def OnPaint(self, event):
        dc = wx.PaintDC(self.panel) 
        dc.SetPen(wx.Pen('black'))
        dc.SetBrush(wx.Brush('white'))
        dc.Clear()
        x, y = 20,20
        self.shapeRectangle=dc.DrawRectangle(x, y, 444, 280)
        fnt = dc.GetFont()        
        for i,line in enumerate(self.textLog):
            if i%2:
                dc.SetTextForeground(wx.RED)
            else:
                dc.SetTextForeground(wx.BLUE)
        dc.SetFont(fnt)
            dc.DrawText(line, x, y)
            y  = dc.GetCharHeight()

        # Sets the function of the send button
    def SendPress(self, event):
        self.sent = self.control.GetValue()
        self.s.send(self.sent)
        self.control.Clear()
        self.textLog.append(self.sent)
        self.panel.Refresh()
        self.s.close()

if __name__=="__main__":
    app = wx.App(False)
    frame = WindowFrame(None, 'ChatClient')
    app.MainLoop()
 

Комментарии:

1. не могли бы вы опубликовать пример, пожалуйста

2. Извините, что я был неясен, я изменил: self.dc = wx.PaintDC(self.panel) на self.dc = wx.ClientDC(self.panel)

3. Это сработало, но я хочу, чтобы оно печаталось на прямоугольнике, который я нарисовал, и когда я self.dc =wx.ClientDC(self.panel) в self.dc =wx.ClientDC(self.shapeRectangle), это дало мне обратную трассировку ошибки (последний последний вызов): File «C:Python27python проектыклиентс gui.py «, строка 43, в <module> frame = WindowFrame(Нет, ‘ChatClient’) Файл «C:Python27python проекты клиент с gui.py «, строка 10, в init self.dc = wx.ClientDC(self.shapeRectangle) # <<< Это было изменено AttributeError: объект ‘WindowFrame’ не имеет атрибута ‘shapeRectangle

4. Я вижу, здесь есть несколько вариантов, в зависимости от того, что вы хотите сделать, хотите ли вы отобразить все прошлые отправленные сообщения или только самые последние? Если вы измените свои координаты self.do.DrawText, вы можете поместить этот текст в прямоугольник, однако он будет заменен при перерисовке прямоугольника. Я подозреваю, что вы хотите добавить текст в список, а затем заставить холст перерисовывать себя, печатая все прошлые сообщения, это правильно?

5. Я отредактировал свой ответ и добавил версию, которая делает то, что, как я думаю, вы пытаетесь сделать, дайте мне знать, если это неверно. Я тестировал его без кода сокета, поскольку ничего не знаю о сокетах и тому подобном.

Ответ №2:

В примере довольно много проблем:

  • Абсолютное позиционирование вместо размеров
  • Нет автоматического переноса при рисовании (как правило, рендерить текст самостоятельно — плохая идея)
  • Странный метод различения отправки / recv ( i%2 ???)

В этом случае было бы гораздо уместнее использовать правильный элемент управления для цветного текста. Подключите свой механизм отправки / получения по своему усмотрению

 import wx
from wx.stc import StyledTextCtrl

class WindowFrame(wx.Frame):
    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent, title = title, size=(500, 400))

        self.panel = wx.Panel(self)
        self.panel.SetBackgroundColour("#0B3861")
        self.stc = StyledTextCtrl(self.panel, -1)
        self.control = wx.TextCtrl(self.panel, style = wx.TE_MULTILINE)
        sendbutton=wx.Button(self.panel, label ="Send")
        recvbutton=wx.Button(self.panel, label ="Receive")

        sendbutton.Bind(wx.EVT_BUTTON, self.SendPress)
        recvbutton.Bind(wx.EVT_BUTTON, self.RecvPress)

        szmain = wx.BoxSizer(wx.VERTICAL)
        szmain.Add(self.stc, 1, wx.EXPAND|wx.ALL, 4)
        szsub = wx.BoxSizer(wx.HORIZONTAL)
        szsub.Add(self.control, 1, wx.EXPAND|wx.ALL, 4)
        szsub.Add(sendbutton, 0, wx.EXPAND|wx.ALL, 4)
        szsub.Add(recvbutton, 0, wx.EXPAND|wx.ALL, 4)
        szmain.Add(szsub, 0, wx.EXPAND|wx.ALL, 0)

        self.panel.SetSizer(szmain)
        self.Centre()
        self.Show()

        # define styles for stc, 0 red fore color, 1 blue fore color
        self.stc.StyleSetSpec(0, "fore:#FF0000")
        self.stc.StyleSetSpec(1, "fore:#0000FF")
        self.stc.SetWrapMode(True) #autowrap

    def add_text(self, text, style):
        curpos = self.stc.GetCurrentPos()
        self.stc.AddText(text)
        self.stc.AddText('rn')
        newpos = self.stc.GetCurrentPos()
        delta = newpos-curpos

        # consult the Scintilla doc (stc is based on it) to understand how styling works
        # http://www.scintilla.org/ScintillaDoc.html#Styling
        self.stc.StartStyling(curpos, 31) # mask 31 allows setting of styles
        self.stc.SetStyling(delta, style)
        self.stc.ScrollToEnd() # keep last line visible

        # Sets the function of the send button
    def SendPress(self, event):
        self.add_text(self._get_text(), 0)

        # Sets the function of the recv button
    def RecvPress(self, event):
        self.add_text(self._get_text(), 1)

    def _get_text(self):
        text = self.control.GetValue()
        self.control.Clear()
        return text


if __name__=="__main__":
    app = wx.App(False)
    frame = WindowFrame(None, 'ChatClient')
    app.MainLoop()
 

Посмотрите в демо-версии wxPython, как использовать StyledTextControl (на основе Scintilla). И последнее, но не менее важное, это заставило меня учиться STC .

Комментарии:

1. StyledTextControl выглядит как хороший маршрут, сегодня я потратил некоторое время на изучение Scintilla, спасибо, что обратили на это мое внимание!