2010-09-22 9 views
1

J'essaie d'écrire un client non basé sur le web pour un service de chat sur un site web, il se connecte bien via le socket et peut communiquer avec lui et tout. Je vous écris une interface graphique pour (j'essayé d'écrire dans tkinter mais je frappé des murs, je vraiment wantd de se passer, donc je suis passé à wxPython)Besoin d'aide avec wxPython, NotebookCtrl en particulier

Ce que je vais avoir un problème avec:

Cette L'application utilise un widget Notebook étendu appelé NotebookCtrl. Cependant, le même problème apparaît avec un ordinateur portable ordinaire. D'abord, il crée une page dans laquelle les choses sont enregistrées, ce qui est réussi, puis il se connecte, et il est censé ajouter des pages avec chaque salle de chat qu'il rejoint sur le service. Cependant, quand il ajoute un onglet après le démarrage de mainloop (je communique avec l'interface graphique et les sockets via les files d'attente et le thread), l'onglet apparaît complètement vide. J'ai été bloqué sur cela pendant des heures et des heures et suis absolument nulle part

L'exemple fourni avec le téléchargement NotebookCtrl ajoute et supprime parfaitement les pages par lui-même. Je suis sur le point d'abandonner complètement ce projet. Voici ce que le code ressemble (noter que ceci est une très petite partie de l'application, mais cela couvre les choses wxPython)

class Chatroom(Panel): 
''' Frame for the notebook widget to tabulate a chatroom''' 
def __init__(self, ns, parent):   
    Panel.__init__(self, parent, -1) 
    self.msgs, self.typed, self.pcbuff = [], [], {} 
    self.members, self._topic, self._title, self.pc = None, None, None, None 
    self.name, self.tabsign, = ns, 0 

    self.hSizer1 = wx.BoxSizer(wx.HORIZONTAL) 
    self.vSizer = wx.BoxSizer(wx.VERTICAL) 

    self.Grid = wx.GridBagSizer(5, 2) 


    self.Title = TextCtrl(self, size=(-1, 50), style=wx.TE_MULTILINE | wx.TE_READONLY) 
    self.Topic = TextCtrl(self, size=(-1, 50), style=wx.TE_MULTILINE | wx.TE_READONLY) 
    self.Privclasses = TreeCtrl(self, size=(150, -1)) 
    self.Buffer = wx.html.HtmlWindow(self) 
    self.Buffer.SetStandardFonts(8) 
    self.templbl = StaticText(self, -1, 'This is where the formatting buttons will go!') 
    # note to remember: self.templbl.SetLabel('string') sets the label 
    self.Typer = TextCtrl(self, size=(-1, 50), style=wx.TE_MULTILINE) 

    self.Grid.Add(self.Title, (0,0), (1,2), wx.EXPAND, 2) 
    self.Grid.Add(self.Topic, (1,0), (1,1), wx.EXPAND, 2) 
    self.Grid.Add(self.Privclasses, (1,1), (2,1), wx.EXPAND, 2) 
    self.Grid.Add(self.Buffer, (2,0), (1,1), wx.EXPAND, 2) 
    self.Grid.Add(self.templbl, (3,0), (1,1), wx.EXPAND | wx.ALIGN_LEFT, 2) 
    self.Grid.Add(self.Typer, (4,0), (1,1), wx.EXPAND, 2) 


    self.Grid.AddGrowableCol(0) 
    self.Grid.AddGrowableRow(2) 


    self.SetSizerAndFit(self.Grid) 
    self.Show(True) 

    self.Typer.Bind(EVT_CHAR, self.Typer_OnKeyDown) 



def Typer_OnKeyDown(self, event): 
    keycode = event.GetKeyCode() 
    if event.ShiftDown(): 
     if keycode == WXK_RETURN: 
      pass 
     elif keycode == WXK_BACK: 
      pass 
     elif keycode == WXK_UP: 
      pass 
     elif keycode == WXK_DOWN: 
      pass 
    else: 
     if keycode == WXK_RETURN: 
      pass 
    event.Skip() 

def Write(self, msg, K): 
    self.msgs.append(msg) 
    if len(self.msgs) > 300: 
     self.msgs = self.msgs[50:] 
    self.Buffer.SetPage('<br>'.join(self.msgs)) 

class Application(App): 
def __init__(self, K): 
    self.Queue = Queue.Queue() 
    self.current = '' 
    self.chatorder = [] 

    self.Window = App(0) 
    self.frame = MainFrame(None, 0, "Komodo Dragon") 
    self.Pages = NC.NotebookCtrl(self.frame, 9000) 
    self.Channels = {} 
    self.AddChatroom('~Komodo', K) 


    self.frame.Show(True) 
    self.Window.SetTopWindow(self.frame) 

    self.Timer = _Timer(0.050, self.OnTimer) 
    self.Timer.start() 

    self.Pages.Bind(NC.EVT_NOTEBOOKCTRL_PAGE_CHANGED, self.onPageChanged) 
    self.Pages.Bind(NC.EVT_NOTEBOOKCTRL_PAGE_CHANGING, self.onPageChanging) 
    self.Pages.Bind(EVT_PAINT, self.onPaint) 
    self.Pages.Bind(EVT_SIZE, self.onSize) 

def onPaint(self, event): 
    event.Skip() 
def onSize(self, event): 
    event.Skip() 

def Run(self): 
    self.Window.MainLoop() 


def onPageChanged(self, event): 
    event.Skip() 

def onPageChanging(self, event): 
    event.Skip() 




# Timer and Queue functions 
def OnTimer(self): 
    self.CheckQueue() 
    self.Timer = _Timer(0.050, self.OnTimer) 
    self.Timer.start() 
def CheckQueue(self): # the Application needs to use a queue to do things in order to prevent 
    try:    # overlaps from happening, such as runtime errors and widgets changing 
     while not self.Queue.empty(): # suddenly. The most common error seems to be size 
      func = self.Queue.get_nowait() # changes during iterations. Everything from 
      func() # packet processing to adding widgets needs to wait in line this way 
    except Queue.Empty: 
     pass 
def AddQueue(self, func): 
    self.Queue.put(func) 

# Channel controls 
def AddChatroom(self, ns, K): 
    if ns in self.Channels: return 

    #self.typedindex = 0 
    c = K.format_ns(ns) 
    self.chatorder.append(ns) 
    self.current = ns 

    self.Channels[ns] = Chatroom(ns, self.Pages) 
    self.Pages.AddPage(self.Channels[ns], ns, True) 

def DeleteChatroom(self, ns, bot): # Delete a channel, it's properties, and buttons 
    ind = self.chatorder.index(ns) 
    del self.chatorder[ind] 
    for each in self.chatorder[ind:]: 
     x = self.channels[each].tab.grid_info() 
     if x['column'] == '0': r, c = int(x['row'])-1, 9 
     else:     r, c = int(x['row']), int(x['column'])-1 
     self.channels[each].tab.grid_configure(row=r, column=c) 
     x = self.channels[each].tab.grid_info() 
    self.channels[ns].Tab.destroy() 
    self.channels[ns].tab.destroy() 
    self.channels[self.chatorder[-1]].tab.select() 
    self.switchtab(bot, self.chatorder[-1]) 
    x = self.tabids_reverse[ns] 
    del self.tabids_reverse[ns], self.tabids[x], self.channels[ns] 

La classe Chatroom couvre ce que chaque onglet doit avoir en elle. le premier onglet ajouté dans la classe init de l'application fonctionne parfaitement et imprime même les messages qu'il reçoit du service de discussion lorsqu'il tente de se connecter. Une fois que le service m'envoie un paquet de jointure, il appelle la même fonction que celle utilisée pour ajouter cet onglet, AddChatroom(), et devrait créer exactement la même chose, en n'imprimant que des messages spécifiques pour ce chat. Il crée l'onglet, mais la page Chatroom est complètement vide et grise. Je suis très triste: C

Merci d'avance si vous pouvez m'aider.

EDIT

J'ai écrit un exemple concret de ce script, vous pouvez vous lancer (si vous avez wxPython). Il utilise Notebook au lieu de NotebookCtrl, donc vous n'avez pas besoin de télécharger ce widget, mais le bug survient pour les deux. L'exemple configure la fenêtre et configure l'onglet principal de débogage, puis un onglet de salon de discussion avant d'entrer dans la boucle principale. Après mainloop, tout chatrooms ajouté postfaces sont complètement vide

from wx import * 
import Queue, time 
from threading import Timer as _Timer 
from threading import Thread 
# import System._NotebookCtrl.NotebookCtrl as NC ## Using regular notebook for this example 

class MainFrame(Frame): 
    def __init__(self, parent, ID, title): 
     ID_FILE_LOGIN = 100 
     ID_FILE_RESTART = 101 
     ID_FILE_EXIT = 102 
     ID_HELP_ABOUT = 200 

     Frame.__init__(self, parent, ID, title, 
         DefaultPosition, Size(1000, 600)) 
     self.CreateStatusBar() 

     self.SetStatusText("This is the statusbar") 
     menu_File = Menu() 
     menu_Help = Menu() 
     menu_Edit = Menu() 
     menu_Config = Menu() 

     menu_File.Append(ID_FILE_LOGIN, "&Login Info", 
        "Enter your deviantArt Login information") 
     menu_File.AppendSeparator() 
     menu_File.Append(ID_FILE_RESTART, "&Restart", 
        "Restart the program") 
     menu_File.Append(ID_FILE_EXIT, "E&xit", 
        "Terminate the program") 

     menu_Help.Append(ID_HELP_ABOUT, "&About", 
        "More information about this program") 

     menuBar = MenuBar() 
     menuBar.Append(menu_File, "&File"); 
     menuBar.Append(menu_Edit, "&Edit"); 
     menuBar.Append(menu_Config, "&Config"); 
     menuBar.Append(menu_Help, "&Help"); 
     self.SetMenuBar(menuBar) 

     EVT_MENU(self, ID_FILE_LOGIN, self.OnLogin) 
     EVT_MENU(self, ID_FILE_RESTART, self.OnRestart) 
     EVT_MENU(self, ID_FILE_EXIT, self.OnQuit) 

     EVT_MENU(self, ID_HELP_ABOUT, self.OnAbout) 

    def OnAbout(self, event): 
     dlg = MessageDialog(self, "Hi! I am Komodo Dragon! I am an application\n" 
           "that communicates with deviantArt's Messaging Network (dAmn)", 
           "Komodo", OK | ICON_INFORMATION) 
     dlg.ShowModal() 
     dlg.Destroy() 

    def OnQuit(self, event): 
     self.Close(True) 

    def OnRestart(self, event): 
     pass 

    def OnLogin(self, event): 
     dlg = MessageDialog(self, "Enter your Login information here:\n" 
           "Work in progress, LOL", 
           "Login", OK | ICON_INFORMATION) 
     dlg.ShowModal() 
     dlg.Destroy() 

class Chatroom(Panel): 
    ''' Frame for the notebook widget to tabulate a chatroom''' 
    def __init__(self, parent, ns):   
     Panel.__init__(self, parent, -1) 
     self.msgs, self.typed, self.pcbuff = [], [], {} 
     self.members, self._topic, self._title, self.pc = None, None, None, None 
     self.name, self.tabsign, = ns, 0 

     self.hSizer1 = wx.BoxSizer(wx.HORIZONTAL) 
     self.vSizer = wx.BoxSizer(wx.VERTICAL) 

     self.Grid = wx.GridBagSizer(5, 2) 

     self.Title = TextCtrl(self, size=(-1, 50), style=wx.TE_MULTILINE | wx.TE_READONLY) 
     self.Topic = TextCtrl(self, size=(-1, 50), style=wx.TE_MULTILINE | wx.TE_READONLY) 
     self.Privclasses = TreeCtrl(self, size=(150, -1)) 
     self.Buffer = wx.html.HtmlWindow(self) 
     self.Buffer.SetStandardFonts(8) 
     self.templbl = StaticText(self, -1, 'This is where the formatting buttons will go!') 
     self.Typer = TextCtrl(self, size=(-1, 50), style=wx.TE_MULTILINE) 

     self.Grid.Add(self.Title, (0,0), (1,2), wx.EXPAND, 2) 
     self.Grid.Add(self.Topic, (1,0), (1,1), wx.EXPAND, 2) 
     self.Grid.Add(self.Privclasses, (1,1), (2,1), wx.EXPAND, 2) 
     self.Grid.Add(self.Buffer, (2,0), (1,1), wx.EXPAND, 2) 
     self.Grid.Add(self.templbl, (3,0), (1,1), wx.EXPAND | wx.ALIGN_LEFT, 2) 
     self.Grid.Add(self.Typer, (4,0), (1,1), wx.EXPAND, 2) 

     self.Grid.AddGrowableCol(0) 
     self.Grid.AddGrowableRow(2) 

     self.SetSizerAndFit(self.Grid) 
     self.Show(True) 

     self.Typer.Bind(EVT_CHAR, self.Typer_OnKeyDown) 

    def Typer_OnKeyDown(self, event): 
     keycode = event.GetKeyCode() 
     if event.ShiftDown(): 
      if keycode == WXK_RETURN: 
       pass 
      elif keycode == WXK_BACK: 
       pass 
      elif keycode == WXK_UP: 
       pass 
      elif keycode == WXK_DOWN: 
       pass 
     else: 
      if keycode == WXK_RETURN: 
       pass 
     event.Skip() 

    def Write(self, msg): 
     self.msgs.append(msg) 
     if len(self.msgs) > 300: 
      self.msgs = self.msgs[50:] 
     self.Buffer.SetPage('<br>'.join(self.msgs)) 



class Application(App): 
    def __init__(self, K): 
     self.Queue = Queue.Queue() 
     self.current = '' 
     self.chatorder = [] 

     self.Window = App(0) 
     self.frame = MainFrame(None, 0, "Komodo Dragon") 
     self.Pages = Notebook(self.frame, 9000) 
     self.Channels = {} 
     self.AddChatroom('~Komodo', K) 

     self.frame.Show(True) 
     self.Window.SetTopWindow(self.frame) 

     self.Timer = _Timer(0.050, self.OnTimer) 

     self.Pages.Bind(EVT_NOTEBOOK_PAGE_CHANGED, self.onPageChanged) 
     self.Pages.Bind(EVT_NOTEBOOK_PAGE_CHANGING, self.onPageChanging) 
     self.Pages.Bind(EVT_PAINT, self.onPaint) 
     self.Pages.Bind(EVT_SIZE, self.onSize) 

    def onPaint(self, event): 
     event.Skip() 
    def onSize(self, event): 
     event.Skip() 
    def onPageChanged(self, event): 
     event.Skip() 
    def onPageChanging(self, event): 
     event.Skip()   

    def Run(self): 
     self.Window.MainLoop() 

    # Timer and Queue functions 
    def OnTimer(self): 
     self.CheckQueue() 
     self.Timer = _Timer(0.050, self.OnTimer) 
     self.Timer.start() 
    def CheckQueue(self): # the Application needs to use a queue to do things in order to prevent 
     try:    # overlaps from happening, such as runtime errors and widgets changing 
      while not self.Queue.empty(): # suddenly. The most common error seems to be size 
       func = self.Queue.get_nowait() # changes during iterations. Everything from 
       if func is not None: 
        func() # packet processing to adding widgets needs to wait in line this way 
     except Queue.Empty: 
      pass 
    def AddQueue(self, func): 
     self.Queue.put(func) 

    # Channel controls 
    def AddChatroom(self, ns, K): 
     if ns in self.Channels: return 

     self.chatorder.append(ns) 
     self.current = ns 

     self.Channels[ns] = Chatroom(self.Pages, ns) 
     self.Pages.AddPage(self.Channels[ns], ns, True) 

class _Thread(Thread): 
    def __init__(self, K): 
     Thread.__init__(self) 
     self.K = K 

    def run(self): 
     self.K.Mainloop()   

class K: 
    def __init__(self): 
     self.App = Application(self) 
     self.App.AddQueue(self.App.Channels['~Komodo'].Write('>> Welcome!')) 
     self.App.AddQueue(self.App.Channels['~Komodo'].Write('>> Entering mainloop...')) 
     self.App.AddChatroom('#TestChatroom1', self) 

     self.roomcount = 1 
     self.timer = time.time() + 3 

     self.thread = _Thread(self) 
     self.thread.start() 
     self.App.Timer.start() 
     self.App.Run() 



    def Mainloop(self): 
     while True: 
      if time.time() > self.timer: 
       self.App.AddQueue(
        lambda: self.App.Channels['~Komodo'].Write('>> Testing')) 
       if self.roomcount < 5: 
        self.roomcount += 1 
        self.App.AddQueue(
         lambda: self.App.AddChatroom('#TestChatroom{0}'.format(self.roomcount), self)) 
       self.timer = time.time() + 3 


if __name__ == '__main__': 
    test = K() 
+0

Balise 'notebook' supprimée dans le cadre du [nettoyage de 2012] (http: // méta.stackexchange.com/questions/128315/the-great-stack-overflow-tag-question-cleanup-of-2012). –

Répondre

1

Voici votre problème:

lambda: self.App.AddChatroom('#TestChatroom{0}'.format(self.roomcount), self)) 

fixe à l'aide wx.CallAfter (testé sur win xp sp3):

lambda: wx.CallAfter(self.App.AddChatroom, '#TestChatroom{0}'.format(self.roomcount), self) 

Vous étiez probablement attachant l'interface graphique en appelant des objets wx à partir du code fil. Voir this wxPython wiki article.

+0

merci beaucoup! Qui savait qu'un si gros problème avait une solution aussi simple? Tu n'as aucune idée à quel point je suis reconnaissant. Je voterais si j'avais assez de réputation lol – Blazer

0

Il ne ressemble pas à vous créez votre Chatroom avec son parent comme l'ordinateur portable. Qu'est-ce que "K" dans Application.__init__? (Vous ne publiez pas un échantillon entièrement runnable)

+0

l'échantillon entièrement exécutable est trop grand pour coller ici. K se réfère à la classe principale du programme (le programme s'appelle Komodo) car l'interface de l'application doit communiquer avec le service de chat en utilisant des méthodes de la classe K (pas complètement implémentées, mais je vais obliger certaines choses à faire certaines choses L'entrée envoie un message, des boutons pour donner un coup de pied aux gens, etc.) – Blazer

+0

'self.Pages' est le bloc-notes, et le parent de l'onglet est indiqué dans le deuxième argument de la classe Chatroom dans cette ligne: 'self.Channels [ns] = Chatroom (ns, self.Pages) ' – Blazer

+0

et pour référence, ns (namespace) fait référence au nom du chatroom – Blazer

0

Lorsque vous ajoutez ou supprimez des onglets, vous devez probablement appeler Layout() juste après avoir terminé. Un moyen facile de trouver est de saisir un bord du cadre et le redimensionner légèrement. Si vous voyez des widgets apparaître par magie, c'est parce que Layout() a été appelé et votre page a été redessinée.